/*****************************************************************
 * flklnr.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
 *
 * Copyright (C) 1989,1990 by Michael Mauldin.  Permission is granted
 * to use this file in whole or in part for any purpose, educational,
 * recreational or commercial, provided that this copyright notice
 * is retained unchanged.  This software is available to all free of
 * charge by anonymous FTP and in the UUNET archives.
 *
 * flklnr.c: Flip "stray" bits in a 1bit bitmap, cleaning the image.
 *
 * USAGE
 *	clean_fbm (input, output, beta, gamma, nbr)
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 01:00:06 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/flklnr.c
 *
 * HISTORY
 * 25-Jun-90  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Package for Release 1.0
 *
 * 07-Mar-89  Michael Mauldin (mlm) at Carnegie Mellon University
 *	Beta release (version 0.9) mlm@cs.cmu.edu
 *
 * 21-Aug-88  Michael Mauldin (mlm) at Carnegie-Mellon University
 *	Created.
 *****************************************************************/

# include <stdio.h>
# include <math.h>
# include <ctype.h>
# include "fbm.h"

/****************************************************************
 * clean_fbm: determine whether image is in color, and call the
 *	        appropriate cleaning routine.
 ****************************************************************/

#ifndef lint
static char *fbmid =
"$FBM flklnr.c <1.0> 25-Jun-90  (C) 1989,1990 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

clean_fbm (input, output, beta, gamma, nbr)
FBM *input, *output;
int beta, gamma, nbr;
{
  if (input->hdr.planes == 1)
  { return (clean_bw (input, output, beta, gamma, nbr)); }
  else
  { return (clean_bw (input, output, beta, gamma, nbr)); }
}

/****************************************************************
 * clean_bw: use a digital Laplacian filter to clean a BW image
 ****************************************************************/

clean_bw (input, output, beta, gamma, nbr)
FBM *input, *output;
int beta, gamma, nbr;
{ register unsigned char *obm, *bmp;
  register int dx, dy, left, right, top, bot, i, j;
  int rowlen, w, h, off, cnt;
  int new, sum, sumw, sumb, Whites;
  int bf, wf, ubf, uwf; /* white and black pixel counters */

  double pc;

  if (input->hdr.planes != 1)
  { fprintf (stderr, "clean_bw: can't process color images\n");
    return (0);
  }

  fprintf (stderr, "Clean BW, beta %d, gamma %d, nbr %d\n",
	   beta, gamma, nbr);

  /* Allocate output */
  output->hdr = input->hdr;
  alloc_fbm (output);

  w = input->hdr.cols;
  h = input->hdr.rows;
  rowlen = input->hdr.rowlen;
  Whites = 252;

  /* If not edge detect do black white trip point */
  if (gamma > 0)
  {
    fprintf (stderr, "Thresholding image, gamma %d...\n", gamma);
    bf = wf = 0;
    for (j=0; j < h; j++)
    { bmp = &(input->bm[j*rowlen]);

      for (i=0; i < w; i++)
      {
        if (bmp[i] >= gamma)	{ bmp[i] = WHITE; wf++; }
        else			{ bmp[i] = BLACK; bf++; }
      }
    }

    pc = (((double)bf) * 100.00) / ((double)(bf + wf));
    fprintf (stderr, "Converted to %1.2f %% Black, %1.2f %% White image.\n",
	     pc, (100.00 - pc));
  }

  /* Set pixel counters for image statistics */
  bf = wf = ubf = uwf = 0;
  off = nbr/2;

  /* Compute outer border of 2 pixels */
    /* Compute Top Line U1 of Pixels */
      /* Compute U1L1Pixel */

  /* Compute Main Image Body */
  for (j=0; j<h; j++)
  { obm = &(output->bm[j*rowlen]);

    /* Set limits of neighborhood */
    top   =  j-off;		if (top < 0)	top = 0;
    bot   =  top+nbr;		if (bot > h)	bot = h;

    for (i=0; i<w; i++)
    { sum = 0;
      cnt = 0;
      
      /* Set limits of neighborhood */
      left  =  i-off;		if (left < 0)	left = 0;
      right =  left+nbr;	if (right > w)	right = w;
      
      /* Sample neighborhood */
      bmp = &(input->bm[top*rowlen]);
	
      for (dy = top;   dy < bot;   dy++, bmp += rowlen)
      { for (dx = left;   dx < right;   dx++)
        { sum += bmp[dx]; cnt ++; }
      }
      
      if (cnt == 0)
      { fprintf (stderr, "Panic, no pixels in neighborhood!\n");
        abort ();
      }
      
      sumw = sum * 100 / (WHITE * cnt);
      sumb = 100 - sumw;
      
      if (input->bm[i + j*rowlen] > Whites)
      {
        if (sumw < beta) { new = BLACK; bf++; }
        else { new = WHITE; uwf++; }
      }
      else
      {
        if (sumb < beta) { new = WHITE; wf++; }
        else { new = BLACK; ubf++; }
      }

      obm[i] = new;
    }
  }


  fprintf (stderr, "Cleaning pass complete for %2d neighbors of %d pixels.\n",
	   beta, w*h);
  fprintf (stderr, "Removed %d white pixels and %d black pixels.\n", bf, wf);
  fprintf (stderr, "Left Unchanged %d white and %d black pixels.\n", uwf, ubf);

  return (1);
}

# ifdef UNDEFINED
/****************************************************************
 * clean_clr: use a digital Laplacian filter to edge detect a CLR image
 ****************************************************************/

clean_clr (input, output, beta)
FBM *input, *output;
double beta;
{ register unsigned char *b, *obm, *avg;
  register int i, j, k, rowlen, plnlen, w, h, p, sum;
  int new, delta, beta100 = beta * 100;
  unsigned char gray[500000];

  fprintf (stderr, "Sharpen color, beta %lg\n", beta);

  /* Allocate output */
  output->hdr = input->hdr;
  alloc_fbm (output);

  w = input->hdr.cols;
  h = input->hdr.rows;
  p = input->hdr.planes;
  rowlen = input->hdr.rowlen;
  plnlen = input->hdr.plnlen;
  
  /* Calculate the intensity plane */
/*  gray = (unsigned char *) malloc (plnlen); */
  
  fprintf (stderr, "Allocating %d bytes for gray[]\n", plnlen);

  for (j=0; j<h; j++)  
  { b = &(input->bm[j*rowlen]);
    avg = &(gray[j*rowlen]);    

    for (i=0; i<w; i++)
    { sum = 0;
      for (k=0; k<p; k++)
      { sum += b[i+k*plnlen]; }
      avg[i] = sum/p;
    }
  }
  
  /* Copy edges directly */
  for (k=0; k<p; k++)
  {  for (j=0; j<h; j++)
    { output->bm[k*plnlen + j*rowlen] =
	input->bm[k*plnlen + j*rowlen];
      output->bm[k*plnlen + j*rowlen + w-1] =
	input->bm[k*plnlen + j*rowlen + w-1];
    }
  
    for (i=0; i<w; i++)
    { output->bm[k*plnlen + i] =
	input->bm[k*plnlen + i];
      output->bm[k*plnlen + (h-1)*rowlen + i] =
	input->bm[k*plnlen + (h-1)*rowlen + i];
    }
  }

  for (j=1; j < h-1; j++)
  { avg = &(gray[j*rowlen]);
    
    for (i=1; i < w-1; i++)
    { sum = avg[i-rowlen-1] +     avg[i-rowlen] + avg[i-rowlen+1] +
	    avg[i-1]        - 8 * avg[i]        + avg[i+1]        +
	    avg[i+rowlen-1] +     avg[i+rowlen] + avg[i+rowlen+1];

      for (k=0; k<p; k++)
      { b =  &(input->bm[k*plnlen + j*rowlen + i]);
        obm = &(output->bm[k*plnlen + j*rowlen + i]);
        
	if (sum < 0)
	{ delta = - (beta100 * *b * -sum / (8*100)); }
	else
	{ delta = beta100 * *b * sum / (8*100); }
  
	new = *b - delta;
  
	if (new < BLACK) new = BLACK;
	else if (new > WHITE) new = WHITE;
	
	*obm = new;
      }
    }
  }

  return (1);
}
# endif
