Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

NiblackBinaryImage.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------+
00002  | Library QgarLib, graphics analysis and recognition                  |
00003  | Copyright (C) 2002  Qgar Project, LORIA                             |
00004  |                                                                     |
00005  | This library is free software; you can redistribute it and/or       |
00006  | modify it under the terms of the GNU Lesser General Public          |
00007  | License version 2.1, as published by the Free Software Foundation.  |
00008  |                                                                     |
00009  | This library is distributed in the hope that it will be useful,     |
00010  | but WITHOUT ANY WARRANTY; without even the implied warranty of      |
00011  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                |
00012  | See the GNU Lesser General Public License for more details.         |
00013  |                                                                     |
00014  | The GNU Lesser General Public License is included in the file       |
00015  | LICENSE.LGPL, in the root directory of the Qgar packaging. See      |
00016  | http://www.gnu.org/licenses/lgpl.html for the terms of the licence. |
00017  | To receive a paper copy, write to the Free Software Foundation,     |
00018  | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.       |
00019  |                                                                     |
00020  | Contact Project Qgar for any information:                           |
00021  |   LORIA - équipe Qgar                                               |
00022  |   B.P. 239, 54506 Vandoeuvre-lès-Nancy Cedex, France                |
00023  |   email: qgar-contact@loria.fr                                      |
00024  |   http://www.qgar.org/                                              |
00025  *---------------------------------------------------------------------*/
00026 
00027 
00028 /**
00029  * @file   NiblackBinaryImage.C
00030  * @brief  Implementation of class qgar::NiblackBinaryImage.
00031  *
00032  * See file NiblackBinaryImage.H for the interface.
00033  *
00034  * @author   <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Karl Tombre">Karl Tombre</a>
00035  * @date     July 3, 200  15:01
00036  * @since    Qgar 2.0
00037  */
00038 
00039 
00040 
00041 // QGAR
00042 #include <qgarlib/CannyGradientImage.H>
00043 #include <qgarlib/Component.H>
00044 #include <qgarlib/ConnectedComponents.H>
00045 #include <qgarlib/ErodedBinaryImage.H>
00046 #include <qgarlib/GenImage.H>
00047 #include <qgarlib/GradientModuleImage.H>
00048 #include <qgarlib/NiblackBinaryImage.H>
00049 #include <qgarlib/StandardDeviationImage.H>
00050 
00051 
00052 
00053 namespace qgar
00054 {
00055 
00056 // -------------------------------------------------------------------
00057 // C O N S T R U C T O R
00058 // -------------------------------------------------------------------
00059 
00060 NiblackBinaryImage::NiblackBinaryImage(const GreyLevelImage& anImg,
00061                                        const int aLowThres,
00062                                        const int aHighThres,
00063                                        const int aMaskSize,
00064                                        const float aK,
00065                                        const float aPostThres)
00066 
00067   : BinaryImage(anImg.width(), anImg.height())
00068 
00069 {
00070   // Pointer to mean image
00071   FloatImage* pMeanImg = 0;
00072 
00073   // Compute standard deviation and mean
00074   StandardDeviationImage stdImg(FloatImage(anImg), pMeanImg, aMaskSize);
00075 
00076   // Rows to exchange data
00077   GreyLevelImage::value_type* bRow = new GreyLevelImage::value_type [_width];
00078   GreyLevelImage::value_type* gRow = new GreyLevelImage::value_type [_width];
00079 
00080   float* mRow = new float [_width];
00081   float* sRow = new float [_width];
00082   
00083   // Binarize
00084   for (int i = 0 ; i < _height ; ++i)
00085     {
00086       // Read means
00087       pMeanImg->row(i, mRow);
00088       // Read deviations
00089       stdImg.row(i, sRow);
00090       // Read original
00091       anImg.row(i, gRow);
00092 
00093       // Pointers
00094       float* m = mRow;
00095       float* s = sRow;
00096       GreyLevelImage::value_type* g = gRow;
00097       GreyLevelImage::value_type* b = bRow;
00098 
00099       // Dynamic thresholding
00100       for (int j = 0 ; j < _width ; ++j, ++b, ++g)
00101         {
00102           if (*g < aLowThres)
00103             {
00104               *b = 1;
00105             }
00106           else if (*g > aHighThres)
00107             {
00108               *b = 0;
00109             }
00110           else if (*g < (GreyLevelImage::value_type) (*m++ + aK * *s++))
00111             {
00112               *b = 1;
00113             }
00114           else
00115             {
00116               *b = 0;
00117             }
00118         }
00119 
00120       // Save line
00121       setRow(i, bRow);
00122     }
00123 
00124   // Post-processing
00125   if (aPostThres > 0)
00126     {
00127       // Copy reference image
00128       BinaryImage* contours = new BinaryImage(*this);
00129 
00130       // Extract connected components
00131       ConnectedComponents* compConnexes = new ConnectedComponents(*contours);
00132 
00133       // Prepare tables
00134       int labCnt = compConnexes->componentCnt();
00135       // Tables for the sums of the means of the gradient
00136       float* gsum = new float [labCnt];
00137       qgFill(gsum, labCnt, 0.f);
00138       // Tables for the numbers of points -- for the mean
00139       int* psum = new int [labCnt];
00140       qgFill(psum, labCnt, 0);
00141 
00142       // Table of labels
00143       Component::label_type* labRow = new Component::label_type[_width];
00144       // By construction, first and last pixels are always WHITE
00145       labRow[1] = 0;
00146       labRow[_width - 1] = 0;
00147 
00148       // Compute the module of Canny gradient
00149       CannyGradientImage* gradImg = new CannyGradientImage(anImg);
00150       GradientModuleImage gradModImg(*gradImg);
00151       delete gradImg;
00152 
00153       // Construct the image of the contours of the black components
00154       // which thus includes the interesting pixels
00155       ErodedBinaryImage* eroImg = new ErodedBinaryImage(*contours);
00156       (*contours) -= (*eroImg);
00157       delete eroImg;
00158 
00159       // Create a line of floats
00160       float* fRow = new float [_width];
00161 
00162       // Pointer to the pixel map of the component image
00163       Component::label_type* pMapCCImg =
00164         (compConnexes->accessComponentImage()).pPixMap() + _width;
00165 
00166       for (int i = 1 ; i < (_height - 1) ; ++i)
00167         {
00168           // Get a line of labels from the component image
00169           // and set pixels from white components to 0
00170           pMapCCImg += 2;
00171           PRIVATEgetBlackLabels(pMapCCImg, labRow);
00172 
00173           // Read the corresponding line in the contours
00174           contours->row(i, bRow);
00175 
00176           // Read the corresponding line in the module of the gradient
00177           gradModImg.row(i, fRow);
00178           Component::label_type* p = labRow;
00179           GreyLevelImage::value_type* q = bRow;
00180           float* r = fRow;
00181           for (int j = 0 ; j < _width ; ++j, ++p, ++q, ++r)
00182             {
00183               if (*q != 0)  // We are on a contour
00184                 {
00185                   gsum[(int)(*p)] += *r;
00186                   psum[(int)(*p)] += 1;
00187                 }
00188             } // END for j
00189         } // END for i
00190 
00191       delete contours;
00192  
00193      // Compute the means
00194       for (int i = 0 ; i < labCnt ; ++i)
00195         {
00196           if (psum[i] != 0)
00197             {
00198               gsum[i] /= psum[i];
00199             }
00200         } // END for
00201   
00202       // Pointer to the pixel map of the component image
00203       pMapCCImg = (compConnexes->accessComponentImage()).pPixMap() + _width;
00204       // Delete fake black components
00205       for (int i = 1 ; i < _height - 1 ; ++i)
00206         {
00207           // Read the current line of components
00208           pMapCCImg += 2;
00209           PRIVATEgetBlackLabels(pMapCCImg, labRow);
00210 
00211           // Read the corresponding line in the binary image
00212           row(i, bRow);
00213 
00214           // Examine components and delete
00215           Component::label_type* p = labRow;
00216           GreyLevelImage::value_type* pb = bRow;
00217           for (int j = 0 ; j < _width ; ++j, ++p, ++pb)
00218             {
00219               if (((*pb) != 0) && (gsum[(int)(*p)] < aPostThres))
00220                 {
00221                   *pb = 0;
00222                 }
00223             }
00224 
00225           // Save this line
00226           setRow(i, bRow);
00227         } // END for
00228 
00229       // Clean up
00230       delete [] fRow;
00231       delete [] psum;
00232       delete [] gsum;
00233       delete compConnexes;
00234     }
00235 
00236   // And clean up
00237   delete [] bRow;
00238   delete [] gRow;
00239   delete [] mRow;
00240   delete [] sRow;
00241 }
00242 
00243 // -------------------------------------------------------------------
00244 // P R I V A T E    F U N C T I O N S
00245 // -------------------------------------------------------------------
00246 
00247 // Get a line of labels from the component image
00248 // and set pixels from WHITE components to 0
00249 
00250 void
00251 NiblackBinaryImage::PRIVATEgetBlackLabels(Component::label_type* aPMapCCImg,
00252                                           Component::label_type* aBuffer)
00253 {
00254   // By construction, first pixel of each line is WHITE
00255   // and the color of the current pixel changes when the label changes
00256 
00257   Component::label_type prevLabel = 0;
00258   bool currColorIsWhite = true;
00259 
00260   for (int idx = 1 ; idx < (_width - 2) ; ++idx, ++aPMapCCImg)
00261     {
00262       Component::label_type currLabel = *aPMapCCImg;
00263 
00264       if (currLabel != prevLabel)
00265         {
00266           currColorIsWhite = !currColorIsWhite;
00267           prevLabel = currLabel;
00268         }
00269 
00270       if (currColorIsWhite)
00271         {
00272           aBuffer[idx] = 0;
00273         }
00274       else
00275         {
00276           aBuffer[idx] = currLabel;
00277         }
00278     }
00279 }
00280 
00281 // -------------------------------------------------------------------
00282 
00283 } // namespace qgar