NiblackBinaryImage::NiblackBinaryImage(const GreyLevelImage& anImg,
				       const int aLowThres,
				       const int aHighThres,
				       const int aMaskSize,
				       const float aK,
				       const float aPostThres)

  : BinaryImage(anImg.width(), anImg.height())

{
  // Pointer to mean image
  FloatImage* pMeanImg = 0;

  // Compute standard deviation and mean
  StandardDeviationImage stdImg(FloatImage(anImg), pMeanImg, aMaskSize);

  // Rows to exchange data
  GreyLevelImage::pointer bRow = new GreyLevelImage::value_type [_width];
  GreyLevelImage::pointer gRow = new GreyLevelImage::value_type [_width];

  float* mRow = new float [_width];
  float* sRow = new float [_width];
  
  // Binarize
  for (int i = 0 ; i < _height ; ++i)
    {
      // Read means
      pMeanImg->row(i, mRow);
      // Read deviations
      stdImg.row(i, sRow);
      // Read original
      anImg.row(i, gRow);

      // Pointers
      float* m = mRow;
      float* s = sRow;
      GreyLevelImage::pointer g = gRow;
      GreyLevelImage::pointer b = bRow;

      // Dynamic thresholding
      for (int j = 0 ; j < _width ; ++j, ++b, ++g)
	{
	  if (*g < aLowThres)
	    {
	      *b = 1;
	    }
	  else if (*g > aHighThres)
	    {
	      *b = 0;
	    }
	  else if (*g < (GreyLevelImage::value_type) (*m++ + aK * *s++))
	    {
	      *b = 1;
	    }
	  else
	    {
	      *b = 0;
	    }
	}

      // Save line
      setRow(i, bRow);
    }

  // Post-processing
  if (aPostThres > 0)
    {
      // Copy reference image
      BinaryImage* contours = new BinaryImage(*this);

      // Extract connected components
      ConnectedComponents* compConnexes = new ConnectedComponents(*contours);

      // Prepare tables
      int labCnt = compConnexes->componentCnt();
      // Tables for the sums of the means of the gradient
      float* gsum = new float [labCnt];
      qgFill(gsum, labCnt, 0.f);
      // Tables for the numbers of points -- for the mean
      int* psum = new int [labCnt];
      qgFill(psum, labCnt, 0);

      // Table of labels
      Component::label_type* labRow = new Component::label_type[_width];
      // By construction, first and last pixels are always WHITE
      labRow[1] = 0;
      labRow[_width - 1] = 0;

      // Compute the module of Canny gradient
      CannyGradientImage* gradImg = new CannyGradientImage(anImg);
      GradientModuleImage gradModImg(*gradImg);
      delete gradImg;

      // Construct the image of the contours of the black components
      // which thus includes the interesting pixels
      ErodedBinaryImage* eroImg = new ErodedBinaryImage(*contours);
      (*contours) -= (*eroImg);
      delete eroImg;

      // Create a line of floats
      float* fRow = new float [_width];

      // Pointer to the pixel map of the component image
      Component::label_type* pMapCCImg =
	(compConnexes->accessComponentImage()).pPixMap() + _width;

      for (int i = 1 ; i < (_height - 1) ; ++i)
	{
	  // Get a line of labels from the component image
	  // and set pixels from white components to 0
	  pMapCCImg += 2;
	  PRIVATEgetBlackLabels(pMapCCImg, labRow);

	  // Read the corresponding line in the contours
	  contours->row(i, bRow);

	  // Read the corresponding line in the module of the gradient
	  gradModImg.row(i, fRow);
	  Component::label_type* p = labRow;
	  GreyLevelImage::pointer q = bRow;
	  float* r = fRow;
	  for (int j = 0 ; j < _width ; ++j, ++p, ++q, ++r)
	    {
	      if (*q != 0)  // We are on a contour
		{
		  gsum[(int)(*p)] += *r;
		  psum[(int)(*p)] += 1;
		}
	    } // END for j
	} // END for i

      delete contours;
 
     // Compute the means
      for (int i = 0 ; i < labCnt ; ++i)
	{
	  if (psum[i] != 0)
	    {
	      gsum[i] /= psum[i];
	    }
	} // END for
  
      // Pointer to the pixel map of the component image
      pMapCCImg = (compConnexes->accessComponentImage()).pPixMap() + _width;
      // Delete fake black components
      for (int i = 1 ; i < _height - 1 ; ++i)
	{
	  // Read the current line of components
	  pMapCCImg += 2;
	  PRIVATEgetBlackLabels(pMapCCImg, labRow);

	  // Read the corresponding line in the binary image
	  row(i, bRow);

	  // Examine components and delete
	  Component::label_type* p = labRow;
	  GreyLevelImage::pointer pb = bRow;
	  for (int j = 0 ; j < _width ; ++j, ++p, ++pb)
	    {
	      if (((*pb) != 0) && (gsum[(int)(*p)] < aPostThres))
		{
		  *pb = 0;
		}
	    }

	  // Save this line
	  setRow(i, bRow);
	} // END for

      // Clean up
      delete [] fRow;
      delete [] psum;
      delete [] gsum;
      delete compConnexes;
    }

  // And clean up
  delete [] bRow;
  delete [] gRow;
  delete [] mRow;
  delete [] sRow;
}
// -------------------------------------------------------------------
// C O N S T R U C T O R
// -------------------------------------------------------------------
ContrastEnhancedImage::ContrastEnhancedImage(GreyLevelImage& anImg,
					     unsigned int aMaskSize)

  throw(QgarErrorDomain)

  : GreyLevelImage(anImg)

{
  int sqsize = (2 * (int)aMaskSize) + 1;  // Effective mask size

  if ((sqsize > _width) || (sqsize > _height))
    {
      std::ostringstream os;
      os << "Mask ["
	 << sqsize
	 << " X "
	 << sqsize
	 << "] too large for image ["
	 << _width
	 << " X "
	 << _height
	 << "]";
      throw QgarErrorDomain(__FILE__, __LINE__,
			    "qgar::ContrastEnhancedImage::ContrastEnhancedImage(qgar::GreyLevelImage&, unsigned int)",
			    os.str());
    }

  // Allocate a table for the maxima and minima per column
  GreyLevelImage::pointer ltabmax = new GreyLevelImage::value_type[_width];
  GreyLevelImage::pointer ltabmin = new GreyLevelImage::value_type[_width];

  // Allocate a table for current input row
  GreyLevelImage::pointer crow = new GreyLevelImage::value_type[_width];

  // Allocate a table for current output row
  GreyLevelImage::pointer orow = new GreyLevelImage::value_type[_width];
  
  GreyLevelImage::pointer p;
  GreyLevelImage::pointer q;
  
  // Now loop on all lines to process
  // As the image was created from anImg, by default the value of the pixels
  // is that of anImg, so we do not need to change the first and last rows

  int i = 0; // current line number in input image

  for (int l = (int)aMaskSize ; l < _height - (int)aMaskSize ; ++l, ++i)
    {
      // Update ltabmax and ltabmin
      GreyLevelImage::pointer smin = ltabmin;
      GreyLevelImage::pointer smax = ltabmax;

      // Set ltabmin to max value (255)
      qgFill(ltabmin, _width, static_cast<GreyLevelImage::value_type>(255));
      // Set ltabmax to min value (0)
      qgFill(ltabmax, _width, static_cast<GreyLevelImage::value_type>(0));
  
      // ii : current line number while computing ltab
      for (int ii = i ; ii < i + sqsize ; ++ii)
	{
	  anImg.row(ii, crow);  // get the current row of anImg
	  q = crow;
	  smin = ltabmin;
	  smax = ltabmax;

	  for (int j = 0 ; j < _width ; ++j, ++q, ++smin, ++smax)
	    {
	      if (*smax < *q)
		{
		  *smax = *q;
		}
	      if (*smin > *q)
		{
		  *smin = *q;
		}
	    } // END for j
	} // END for ii
  

      // And now process the current line

      row(l, orow); // initialize with old values
      
      p = orow + aMaskSize;
      q = crow + aMaskSize;
      smin = ltabmin, smax = ltabmax;

      for (int j = (int)aMaskSize ;
	   j < _width - (int)aMaskSize ;
	   ++j, ++smin, ++smax, ++p, ++q)
	{
	  // On all columns which can be processed
	  GreyLevelImage::value_type curmax = 0;    // current maximum
	  GreyLevelImage::value_type curmin = 255;  // current minimum
	  GreyLevelImage::pointer slmax = smax;
	  GreyLevelImage::pointer slmin = smin;
	  
	  for (int k = 0 ; k < sqsize ; ++k, ++slmin, ++slmax)
	    {
	      if (curmax < *slmax)
		{
		  curmax = *slmax;	// new maximum
		}
	      if (curmin > *slmin)
		{
		  curmin = *slmin;     // new minimum
		}
	    } // END for k

	  // Compute result
	  if ((curmax - *q) < (*q - curmin))
	    {
	      *p = curmax;
	    }
	  else
	    {
	      *p = curmin;
	    }
	} // END for j

      // Write result
      setRow(l, orow);

    } // END for l

  // Clean up
  delete [] ltabmin;
  delete [] ltabmax;
  delete [] crow;
  delete [] orow;
}