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

TTBinaryImage.C

Go to the documentation of this file.
00001  /*---------------------------------------------------------------------*
00002   | Library QgarLib, graphics analysis and recognition                  |
00003   | Copyright (C) 2004  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 license. |
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  TTBinaryImage.C
00030  * @brief Implementation of class qgar::TTBinaryImage.
00031  *
00032  * See file TTBinaryImage.H for the interface.
00033  * 
00034  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Gérald Masini">Gérald Masini</a>
00035  * @date   July 18, 2005  15:15
00036  * @since  Qgar 2.2
00037  */
00038 
00039 
00040 
00041 // QGAR
00042 #include <qgarlib/array.H>
00043 #include <qgarlib/BoundingBox.H>
00044 #include <qgarlib/CannyGradientImage.H>
00045 #include <qgarlib/Component.H>
00046 #include <qgarlib/ConnectedComponents.H>
00047 #include <qgarlib/GenConvolImage.H>
00048 #include <qgarlib/GenImage.H>
00049 #include <qgarlib/GenMask2d.H>
00050 #include <qgarlib/GradientModuleImage.H>
00051 #include <qgarlib/LaplacianOfGaussianImage.H>
00052 #include <qgarlib/primitives.H>
00053 #include <qgarlib/TTBinaryImage.H>
00054 
00055 
00056 
00057 using namespace std;
00058 
00059 
00060 
00061 namespace qgar
00062 {
00063 
00064 
00065 // LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
00066 //
00067 // L O C A L   A U X I L I A R I E S
00068 //
00069 // LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
00070 
00071 
00072 namespace
00073 {
00074 
00075 // =======================
00076 // LABELS '-', '0' and '+'
00077 // =======================
00078 
00079 // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00080 // WARNING: See section (4)
00081 // Do not change the following values, because they are used
00082 // to increment the count of labels '-' and '+' in the border
00083 // of a WHITE component (table tabPlusMinus)
00084 // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00085 
00086 static const BinaryImage::value_type LABEL_MINUS = 0;
00087 static const BinaryImage::value_type LABEL_ZERO  = 1;
00088 static const BinaryImage::value_type LABEL_PLUS  = 2;
00089 
00090 } // unnamed namespace
00091 
00092 
00093 // LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
00094 //
00095 // E N D   O F   L O C A L   A U X I L I A R I E S
00096 //
00097 // LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
00098 
00099 
00100 
00101 
00102 // ---------------------------------------------------------------------
00103 // C O N S T R U C T O R S
00104 // ---------------------------------------------------------------------
00105 
00106 
00107 TTBinaryImage::TTBinaryImage(const GreyLevelImage& aGreyLevelImg, 
00108                              const double aSigma,
00109                              const double anActThrs,
00110                              const double aPostThrs)
00111 
00112   : BinaryImage(aGreyLevelImg.width(), aGreyLevelImg.height())
00113 
00114 {
00115   // DIMENSIONS OF THE INITIAL IMAGE
00116   const int imgWidth        = aGreyLevelImg.width();
00117   const int imgHeight       = aGreyLevelImg.height();
00118   const int imgWidthMinus2  = imgWidth  - 2;
00119   const int imgWidthMinus1  = imgWidth  - 1;
00120   const int imgWidthPlus1   = imgWidth  + 1;
00121   const int imgWidthPlus4   = imgWidth  + 4;
00122   const int imgWidthX2      = imgWidth  * 2;
00123   const int imgHeightMinus2 = imgHeight - 2;
00124   const int imgHeightPlus4  = imgHeight + 4;
00125   const int imgSize         = imgWidth * imgHeight;
00126 
00127   // POINTERS TO PIXEL MAPS
00128   // pActImg     : activity
00129   // pBinImg     : first temporary binary image built from activity
00130   // pCompImg    : components
00131   // pGradModImg : Gradient module
00132   // pLapImg     : Laplacian
00133   // pLblImg     : labels {-,0,+}
00134   // pNegCompImg : negative components
00135   // pNegImg     : negative binary image
00136   // pResImg     : final binary image
00137   LaplacianOfGaussianImage::pointer pActImg;
00138   BinaryImage::pointer              pBinImg;
00139   GradientModuleImage::pointer      pGradModImg;
00140   LaplacianOfGaussianImage::pointer pLapImg;
00141   BinaryImage::pointer              pLblImg;
00142   Component::label_type*            pCompImg;
00143   Component::label_type*            pNegCompImg;
00144   BinaryImage::pointer              pNegImg;
00145   BinaryImage::pointer              pResImg;
00146 
00147   // Total number of (connected) components
00148   int compCnt;
00149 
00150 
00151   // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00152   // In the image of (labels of) components, the first and last rows,
00153   // and the first and last columns are considered as parts of the
00154   // background, with label 0 (see class qgar::ConnectedComponents).
00155   // => They are generally not taken into account when dealing with
00156   // images in the following:
00157   //
00158   //   0  0    0    0    0    0    0    0    0    0  0
00159   //   0  F -> * -> * -> * -> * -> * -> * -> * -> *  0
00160   //   0  * -> ...                         ... -> *  0
00161   //   0  * -> * -> * -> * -> * -> * -> * -> * -> L  0
00162   //   0  0    0    0    0    0    0    0    0    0  0
00163   // 
00164   // F: first pixel to be processed
00165   // L:  last pixel to be processed
00166   // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00167 
00168 
00169   // ===================================================================
00170   // (1) ACTIVITY
00171   // ===================================================================
00172 
00173   // (1.1) Compute the Gradient using Canny's operator,
00174   // ===== instead of Sobel's one (used in Trier & Taxt's method)
00175 
00176   CannyGradientImage*  gradImg    = new CannyGradientImage(aGreyLevelImg, aSigma);
00177   GradientModuleImage* gradModImg = new GradientModuleImage(*gradImg);
00178 
00179   // (1.2) Delete useless Gradient image
00180   // =====
00181   delete gradImg;
00182 
00183   /*
00184    * (1.3) The activity of a given pixel is the sum of the Gradient
00185    * ===== modules of the pixels around:
00186    *             
00187    *  +--------------> X              i = x+1  j = y+1  
00188    *  | g g g g g g g                  __       __
00189    *  | g g G G G g g                  \        \  
00190    *  | g g G A G g g        A(x,y) =  /        /   gradModImg(i,j)
00191    *  | g g G G G g g                  --       --
00192    *  | g g g g g g g                 i = x-1  j = y-1
00193    *  v
00194    *  Y
00195    *
00196    * => Convolve the Gradient module image
00197    *    using a 3x3 mask with all coefficients set to 1
00198    *
00199    * Class ConvolImage should be used to perform the convolution:
00200    *
00201    * FConvolImage* actImg = new FConvolImage(*gradModImg, FMask2d(3,3,1.0));
00202    *
00203    * but multiplications by the mask coefficients would consume
00204    * a substantial amount of time.
00205    */
00206 
00207   GenImage<LaplacianOfGaussianImage::value_type>*
00208     actImg = new GenImage<LaplacianOfGaussianImage::value_type>(imgWidth, imgHeight);
00209 
00210   // pActImg     : first pixel to be processed in the activity image
00211   // pGradModImg : corresponding pixel in the Gradient image
00212   pActImg     = actImg->pPixMap()     + imgWidthPlus1;
00213   pGradModImg = gradModImg->pPixMap() + imgWidthPlus1;
00214 
00215   for (int rowIdx = 0 ; rowIdx < imgHeightMinus2 ; ++rowIdx)
00216     {
00217       for (int colIdx = 0 ; colIdx < imgWidthMinus2 ; ++colIdx)
00218         {
00219           *pActImg =   *(pGradModImg - imgWidthMinus1) // nw  |  |  |  |
00220                      + *(pGradModImg - imgWidth)       // n  -+--+--+--+-
00221                      + *(pGradModImg - imgWidthPlus1)  // ne  |nw| n|ne|
00222                      + *(pGradModImg - 1)              // w  -+--+--+--+-
00223                      + *pGradModImg                    // A   | w| A| e|
00224                      + *(pGradModImg + 1)              // e  -+--+--+--+-
00225                      + *(pGradModImg + imgWidthMinus1) // sw  |sw| s|se|
00226                      + *(pGradModImg + imgWidth)       // s  -+--+--+--+-
00227                      + *(pGradModImg + imgWidthPlus1); // se  |  |  |  |
00228           ++pActImg;
00229           ++pGradModImg;
00230         }
00231       pActImg     += 2;
00232       pGradModImg += 2;
00233     }
00234 
00235 
00236   // ===================================================================
00237   // (2) IMAGE OF LABELS AND CORRESPONDING BINARY IMAGE
00238   // ===================================================================
00239 
00240   // (2.1) Each pixel is associated with a label among {-,0,+}, stored
00241   // ===== at same coordinates in the current image (this), such as:
00242   //
00243   //          | 0  if  A(x,y) <  anActThrs
00244   // L(x,y) = | +  if  A(x,y) >= anActThrs  and  Laplacian(x,y) >= 0
00245   //          | -  if  A(X,Y) >= anActThrs  and  Laplacian(x,y) <  0
00246   //  
00247   // Simultaneously, binary image binImg is constructed, such as
00248   // a WHITE (resp. BLACK) pixel corresponds to a pixel labelled '0'
00249   // (resp. '+' or '-') at same coordinates in the current image (this)
00250 
00251   // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00252   // WARNING: the current image (this) is used to store L(x,y)
00253   // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00254 
00255   BinaryImage* binImg = new BinaryImage(imgWidth, imgHeight);
00256   LaplacianOfGaussianImage*
00257     lapImg = new LaplacianOfGaussianImage(aGreyLevelImg, aSigma);
00258   
00259   pBinImg = binImg->pPixMap() + imgWidthPlus1;
00260   pActImg = actImg->pPixMap() + imgWidthPlus1;
00261   pLapImg = lapImg->pPixMap() + imgWidthPlus1;
00262   pLblImg =   this->pPixMap() + imgWidthPlus1;
00263 
00264   for (int rowIdx = 0 ; rowIdx < imgHeightMinus2 ; ++rowIdx)
00265     {
00266       for (int colIdx = 0 ; colIdx < imgWidthMinus2 ; ++colIdx)
00267         {
00268           if (*pActImg < anActThrs)
00269             {
00270               *pBinImg = QGE_BW_WHITE;  // label '0' => WHITE
00271               *pLblImg = LABEL_ZERO;
00272             }
00273           else
00274             {
00275               *pBinImg = QGE_BW_BLACK;  // label '+' or '-' => BLACK
00276 
00277               if (*pLapImg < 0.)
00278                 {
00279                   *pLblImg = LABEL_MINUS;
00280                 }
00281               else
00282                 {
00283                   *pLblImg = LABEL_PLUS;
00284                 }
00285             } // END if
00286 
00287           // Next pixel in row
00288           ++pBinImg;
00289           ++pActImg;
00290           ++pLapImg;
00291           ++pLblImg;
00292         } // END for colIdx
00293 
00294       // Next row
00295       pBinImg += 2;
00296       pActImg += 2;
00297       pLapImg += 2;
00298       pLblImg += 2;
00299     } // END for rowIdx
00300 
00301 
00302   // (2.2) Delete useless images: Activity and Laplacian
00303   // =====
00304   delete actImg;
00305   delete lapImg;
00306 
00307 
00308   // ===================================================================
00309   // (3) CONNECTED COMPONENTS OF THE LABEL IMAGE
00310   // ===================================================================
00311 
00312   // (3.1) Connected components are computed from binary image binImg:
00313   // ===== * WHITE pixels correspond to pixels labelled '0'
00314   //       * BLACK pixels correspond to pixels labelled '+' and '-'
00315   //
00316   // WARNING:
00317   //
00318   //      b b b                    . w .
00319   //      b B b                    w W w
00320   //      b b b                    . w .
00321   //
00322   //   8-connexity              4-connexity
00323   //    for BLACK                for WHITE
00324 
00325   ConnectedComponents* components = new ConnectedComponents(*binImg);
00326 
00327   // The component image
00328   const ConnectedComponents::image_type&
00329     compImg = components->accessComponentImage();
00330 
00331   // (3.2) Delete useless temporary binary image
00332   // =====
00333   delete binImg;
00334 
00335 
00336   // ===================================================================
00337   // (4) BORDERS OF WHITE COMPONENTS
00338   // ===================================================================
00339 
00340   // colCnt : number of pixels in a row
00341   // colIdx : index of the current pixel in the current row
00342   int colCnt = imgWidthMinus2;
00343   int colIdx = 1;
00344 
00345   // rowCnt : total number of rows to be processed
00346   // rowIdx : index of the row being processed
00347   int rowCnt = imgHeightMinus2;
00348   int rowIdx = 1;
00349 
00350   // pCompImg : pointer to the pixel map of the image of components
00351   // pLblImg  : pointer to the pixel map of the image of labels {-,0,+}
00352   // They point to the same (x,y) location in both pixel maps
00353   pCompImg  = compImg.pPixMap() + imgWidthPlus1;
00354   pLblImg   = this->pPixMap()   + imgWidthPlus1;
00355 
00356   //                   0  1  2        compCnt - 1
00357   //                 +--+--+--+- - - -+--+
00358   // tabPlusMinus    |  |  |  |       |  |   initialized to 0
00359   //                 +--+--+--+- - - -+--+
00360   // 
00361   // * compCnt is the total number of components
00362   // * tabPlusMinus(i) is incremented (resp. decremented) by 1 for
00363   //   each pixel labelled '+' (resp. '-') in the border of component i
00364   compCnt = components->componentCnt();
00365   int* tabPlusMinus = new int[compCnt];
00366   qgMemSet(tabPlusMinus, compCnt);
00367 
00368   // Loop condition
00369   bool notLastPixel = true;
00370 
00371   while(notLastPixel)
00372     {
00373     // -----------------------------------------------------------------
00374     // WHILE each pixel of the component image is not processed
00375     // -----------------------------------------------------------------
00376 
00377       // Component (label) of the current pixel
00378       Component::label_type compC = *pCompImg;
00379 
00380       if ((compC != 0) && (*pLblImg == LABEL_ZERO))
00381         {
00382           // ############################################################
00383           // Process only 0-labelled components, if not background
00384           // ############################################################
00385           //
00386           //    0      i-2 i-1  i  i+1 i+2
00387           //     +----|---|---|---|---|---|---->
00388           //     |
00389           //     |
00390           //     -   -|---|---|---|---|---|-
00391           // j-1 |    |   | nw| n | ne|nee|
00392           //     -   -|---|---#=============
00393           // j   |    | ww| w # C | e |   |
00394           //     =   =========#---|---|---|-
00395           // j+1 |    |   | sw| s | se|   |
00396           //     -   -|---|---|---|---|---|-
00397           //     |
00398           //     v
00399           //
00400           // * Current pixel:
00401           //   - is pointed by pCompImg
00402           //   - belongs to component C (which is not a white component)
00403           // * Pixels belonging to components nw, n, ne, nee, ww, and w
00404           //   are already processed
00405           //
00406           // ############################################################
00407           // IF component e (resp. sw, s, se) is the same component as C,
00408           // THEN the corresponding pixel does not belong to the border
00409           //      of (current) component C: nothing to do
00410           // ############################################################
00411 
00412 
00413           // PIXEL BELONGING TO COMPONENT e
00414           // ##############################
00415           if (*(pCompImg + 1) != compC)  // e != c
00416             {
00417               //  -|---|---|---|---|---|-
00418               //   |   | nw| n | ne|nee|
00419               //  -|---|---#=============
00420               //   | ww| w # c | e |   |
00421               //  =========#---|---|---|-
00422               //   |   | sw| s | se|   |
00423               //  -|---|---|---|---|---|-
00424               //
00425               // * Values of pointers to access pixels of components:
00426               //     - n  :  PIX_MAP_POINTER - imgWidth
00427               //     - ne :  PIX_MAP_POINTER - imgWidth + 1
00428               //     - nee:  PIX_MAP_POINTER - imgWidth + 2
00429               //     - c  :  PIX_MAP_POINTER
00430               //     - e  :  PIX_MAP_POINTER + 1
00431               //   By construction (see beginning of the present section 4),
00432               //   all these values determine valid pointers
00433               //
00434               // * IF component n, ne, and nee are different from c
00435               //   THEN the pixel of component e has not been taken
00436               //        into account for component c
00437               
00438               Component::label_type* pCompImgTmp = pCompImg - imgWidth;
00439 
00440               if (   (*pCompImgTmp       != compC)   // n   != c
00441                   && (*(pCompImgTmp + 1) != compC)   // ne  != c
00442                   && (*(pCompImgTmp + 2) != compC))  // nee != c
00443                 {
00444                   // Update count of +/- pixels of component c: labels
00445                   // are coded so that the label has just to be added to
00446                   // the current count
00447                   tabPlusMinus[compC] += *(pLblImg + 1) - 1;
00448                   // NOTE: component e is necessarily BLACK
00449                   // (i.e. labelled '+' or '-' in the current image)
00450                   // as WHITE components are 4-connected
00451                 }
00452             } // END pixel e
00453 
00454 
00455           // PIXEL BELONGING TO COMPONENT sw
00456           // ###############################
00457           if (*(pCompImg + imgWidthMinus1) != compC)  // sw != c
00458             {
00459               //  -|---|---|---|---|---|-
00460               //   |   | nw| n | ne|nee|
00461               //  -|---|---#=============
00462               //   | ww| w # c | e |   |
00463               //  =========#---|---|---|-
00464               //   |   | sw| s | se|   |
00465               //  -|---|---|---|---|---|-
00466               //
00467               // * Values of pointers to access pixels of components:
00468               //     - ww :  PIX_MAP_POINTER - 2
00469               //     - w  :  PIX_MAP_POINTER - 1
00470               //     - c  :  PIX_MAP_POINTER
00471               //     - sw :  PIX_MAP_POINTER + imgWidth - 1
00472               //   By construction (see beginning of the present section 4),
00473               //   all these values determine valid pointers
00474               //
00475               // * IF components ww and w are different from c
00476               //   THEN the pixel of component sw has not been taken
00477               //        into account for component c
00478 
00479               if (   (*(pCompImg - 2) != compC)   // ww != c
00480                   && (*(pCompImg - 1) != compC))  // w  != c
00481                 {
00482                   // Update count of +/- pixels of component c: labels
00483                   // are coded so that the label has just to be added to
00484                   // the current count
00485                   tabPlusMinus[compC] += *(pLblImg + imgWidthMinus1) - 1;
00486                   // NOTE: component sw is not necessarily BLACK
00487                   // (i.e. labelled '+' or '-' in the current image)
00488                   // as WHITE components are 4-connected
00489                 }
00490             } // END pixel sw
00491 
00492 
00493           // PIXEL BELONGING TO COMPONENT s
00494           // ##############################
00495           if ((*pCompImg + imgWidth) != compC)  // s != c
00496             {
00497               //  -|---|---|---|---|---|-
00498               //   |   | nw| n | ne|   |
00499               //  -|---|---#=============
00500               //   | ww| w # c | e |   |
00501               //  =========#---|---|---|-
00502               //   |   | sw| s | se|   |
00503               //  -|---|---|---|---|---|-
00504               //
00505               // * Values of pointers to access pixels of components:
00506               //     - w  :  PIX_MAP_POINTER - 1
00507               //     - c  :  PIX_MAP_POINTER
00508               //     - s  :  PIX_MAP_POINTER + imgWidth
00509               //   By construction (see beginning of the present section 4),
00510               //   all these values determine valid pointers
00511               //
00512               // * IF component w is different from c
00513               //   THEN the pixel of component s has not been taken
00514               //        into account for component c
00515 
00516               if (*(pCompImg - 1) != compC)   // w != c
00517                 {
00518                   // Update count of +/- pixels of component c: labels
00519                   // are coded so that the label has just to be added to
00520                   // the current count
00521                   tabPlusMinus[compC] += *(pLblImg + imgWidth) - 1;
00522                   // NOTE: component s is necessarily BLACK
00523                   // (i.e. labelled '+' or '-' in the current image)
00524                   // as WHITE components are 4-connected
00525                 }
00526             } // END pixel s
00527 
00528 
00529           // PIXEL BELONGING TO COMPONENT se
00530           // ###############################
00531           if (*(pCompImg + imgWidthPlus1) != compC)  // se != c
00532             {
00533               //  -|---|---|---|---|---|-
00534               //   |   | nw| n | ne|   |
00535               //  -|---|---#=============
00536               //   | ww| w # c | e |   |
00537               //  =========#---|---|---|-
00538               //   |   | sw| s | se|   |
00539               //  -|---|---|---|---|---|-
00540               //
00541               // * Values of pointers to access pixels of components:
00542               //     - c  :  PIX_MAP_POINTER
00543               //     - se :  PIX_MAP_POINTER + imgWidth + 1
00544               //   By construction (see beginning of the present section 4),
00545               //   all these values determine valid pointers
00546               //
00547               // * From now on, this pixel has never been taken into account
00548 
00549               // Update count of +/- pixels of component c: labels
00550               // are coded so that the label has just to be added to
00551               // the current count
00552               tabPlusMinus[compC] += *(pLblImg + imgWidthPlus1) - 1;
00553               // NOTE: component se is not necessarily BLACK
00554               // (i.e. labelled '+' or '-' in the current image)
00555               // as WHITE components are 4-connected
00556             } // END pixel se
00557 
00558         } // END if component is white
00559           // #########################
00560 
00561 
00562       // -------------
00563       // LOOP features
00564       // -------------
00565       if (colIdx == colCnt)
00566         {
00567           // THE CURRENT ROW IS PROCESSED...
00568           if (rowIdx == rowCnt)
00569             {
00570               // ...AND ALL THE ROWS ARE PROCESSED
00571               notLastPixel = false;
00572             }
00573           else
00574             {
00575               // ...AND ALL THE ROWS ARE NOT PROCESSED
00576               // Next row
00577               ++rowIdx;
00578               colIdx = 1;
00579               // Update pointers in pixel maps
00580               pCompImg += 3;
00581               pLblImg  += 3;          
00582             }
00583         }
00584       else
00585         {
00586           // THE CURRENT ROW IS NOT PROCESSED
00587           // Next pixel
00588           ++colIdx;
00589           // Update pointers in pixel maps
00590           ++pCompImg;
00591           ++pLblImg;
00592         } // END loop features
00593 
00594     } // -----------------------------------------------------------------
00595       // END while notLastPixel
00596       // -----------------------------------------------------------------
00597 
00598 
00599   // ===================================================================
00600   // (5) CONSTRUCTION OF THE RESULTING BINARY IMAGE
00601   // ===================================================================
00602 
00603   // (5.1) Transform the current image into a true binary image
00604   // =====
00605   // Pointer to the pixel map:
00606   // pLblImg  : image of labels {-,0,+} (current image)
00607   // pCompImg : component image
00608   pLblImg  = this->pPixMap();
00609   pCompImg = compImg.pPixMap();
00610 
00611   for (int pixIdx = 0 ; pixIdx < imgSize ; ++pixIdx, ++pLblImg, ++pCompImg)
00612     {
00613 
00614        if (*pCompImg == 0)
00615         {
00616           // Current pixel belongs to the background => WHITE
00617           *pLblImg = QGE_BW_WHITE;
00618         }
00619        else
00620         {
00621           if (*pLblImg == LABEL_PLUS)
00622             {
00623               // Current pixel is labelled '+'  => BLACK
00624               *pLblImg = QGE_BW_BLACK;
00625             }
00626           else if (*pLblImg == LABEL_MINUS)
00627             {
00628               // Current pixel is labelled '-'  => WHITE
00629               *pLblImg = QGE_BW_WHITE;        
00630             }
00631           else if (tabPlusMinus[*pCompImg] > 0)
00632             {
00633               // Current pixel is labelled '0' and N+ > N-  => BLACK
00634               *pLblImg = QGE_BW_BLACK;
00635             }
00636           else
00637             {
00638               // Current pixel is labelled '0' and N+ <= N-  => WHITE
00639               *pLblImg = QGE_BW_WHITE;
00640             }
00641         } // END if
00642 
00643     } // END for
00644 
00645 
00646   // (5.2) Delete useless objects
00647   // =====
00648   delete components;
00649   delete [] tabPlusMinus;
00650 
00651 
00652   // ===================================================================
00653   // (6) POST-PROCESSING
00654   // ===================================================================
00655 
00656   // (6.1) Create a binary image with 2 extra rows/columns around it
00657   // =====
00658   //
00659   // WARNING: In the image of (labels of) components, the first and
00660   // last rows, as well as the first and last columns are always set
00661   // to the background (see class qgar::ConnectedComponents). 
00662   // If 2 extra rows and columns of BLACK pixels (B below) are added
00663   // around the image. In that way, WHITE regions touching the image
00664   // borders (w) do not belong to the background (W) in the resulting
00665   // component image:
00666   //
00667   //
00668   //  B B B B B B B ...                               W W W W W W W ...
00669   //  B B B B B B B ...                               W B B B B B B ...
00670   //  B B b w w b b ...  -- connected components -->  W B b w w b b ...
00671   //  B B w w w w b ...                               W B w w w w b ...
00672   //  B B b b b b b ...                               W B b b b b b ...
00673   //  . . . . . . . ...                               . . . . . . . ...
00674 
00675   // negImg  : the (negative) binary image
00676   // pNegImg : pointer to its pixel map
00677   BinaryImage negImg(imgWidthPlus4, imgHeightPlus4);
00678   pNegImg = negImg.pPixMap();
00679 
00680   for (int colIdx = 0 ; colIdx < (imgWidthX2 + 8) ; ++colIdx)
00681     {
00682       *pNegImg = QGE_BW_BLACK;
00683       ++pNegImg;
00684     }
00685   for (int rowIdx = 0 ; rowIdx < imgHeight ; ++rowIdx)
00686     {
00687       *pNegImg = QGE_BW_BLACK;
00688       ++pNegImg;
00689       *pNegImg = QGE_BW_BLACK;
00690       pNegImg += imgWidthPlus1;
00691       *pNegImg = QGE_BW_BLACK;
00692       ++pNegImg;
00693       *pNegImg = QGE_BW_BLACK;
00694       ++pNegImg;
00695     }
00696   for (int colIdx = 0 ; colIdx < (imgWidthX2 + 8) ; ++colIdx)
00697     {
00698       *pNegImg = QGE_BW_BLACK;
00699       ++pNegImg;
00700     }
00701 
00702 
00703   // (6.2) Construct the negative of the current image
00704   // =====
00705   // pNegImg : pointer to the pixel map of the negative image
00706   // pResImg : pointer to the pixel map of the final image
00707   pNegImg = negImg.pPixMap() + imgWidthX2 + 10;
00708   pResImg = this->pPixMap();
00709 
00710   for (int rowIdx = 0 ; rowIdx < imgHeight ; ++rowIdx)
00711     {
00712       for (int colIdx = 0 ; colIdx < imgWidth ; ++colIdx)
00713         {
00714           *pNegImg = (*pResImg == QGE_BW_WHITE) ? QGE_BW_BLACK : QGE_BW_WHITE;
00715 
00716           ++pResImg; // Next pixel in the source (positive) image
00717           ++pNegImg; // Next pixel in the destination (negative) image
00718         }
00719       pNegImg += 4 ; // Next row in the destination (negative) image
00720     } // END for rowIdx
00721 
00722 
00723   // (6.3) Connected components of the negative image
00724   // ===== WHITE components are 4-connected
00725   //       => BLACK components of the initial image are 4-connected
00726 
00727   ConnectedComponents negComponents(negImg);
00728 
00729   // The image of components and the total number of components
00730   const ConnectedComponents::image_type&
00731     negCompImg = negComponents.accessComponentImage();
00732   compCnt = negComponents.componentCnt();
00733 
00734 
00735   // (6.4) Process WHITE components
00736   // =====
00737   // NOTE: there's no need to process component 0, i.e. background
00738 
00739   for (int compIdx = 1 ; compIdx < compCnt ; ++compIdx)
00740     // -------------------------------------------------------------------
00741     // FOR each component
00742     // -------------------------------------------------------------------
00743     {
00744       // The current component
00745       Component& comp = negComponents[compIdx];
00746 
00747 
00748       if (comp.color() == QGE_BW_WHITE)
00749         {
00750           // THE COMPONENT IS WHITE (i.e. INITIALLY BLACK) 
00751           // #############################################
00752 
00753           // The bounding box of the component
00754           const BoundingBox& bb = comp.accessBoundingBox();
00755           int bbWidth  = bb.width();
00756           int bbHeight = bb.height();
00757 
00758           // Offsets of the top left pixel of the component:
00759           // offTopL    : in the Gradient image
00760           // offTopLNeg : in the (negative) component image
00761           int offTopL    = ((comp.yTopLeftPix() - 2) * imgWidth) + comp.xTopLeftPix() - 2;
00762           int offTopLNeg = (comp.yTopLeftPix() * imgWidthPlus4) + comp.xTopLeftPix();
00763 
00764           // Pointers to the pixel map:
00765           // pGradModImg : Gradient image
00766           // pNegCompImg : (negative) component image
00767           pGradModImg = gradModImg->pPixMap() + offTopL;
00768           pNegCompImg = negCompImg.pPixMap()  + offTopLNeg;
00769 
00770           // Offsets to go to next row:
00771           // offNextRow    : in the Gradient image
00772           // offNextRowNeg : in the (negative) component image
00773           int offNextRow    = imgWidth - bbWidth;
00774           int offNextRowNeg = offNextRow + 4;
00775 
00776           // (6.4.1) Compute the average sum of the Gradient modules
00777           // ======= of the contour pixels of the current component
00778 
00779           // contourCnt : number of pixels of the contour
00780           // gradSum    : sum of the Gradient modules
00781           GradientModuleImage::value_type contourCnt = 0;
00782           GradientModuleImage::value_type gradSum    = 0;
00783 
00784           // Offset of the top left pixel of the component in the bounding box
00785           int colIdx = comp.xTopLeftPix() - bb.xTopLeft();
00786           
00787           for (int rowIdx = 0 ; rowIdx < bbHeight ; ++rowIdx)
00788             {
00789               while (colIdx < bbWidth)
00790                 {
00791                   // 4-connexity:
00792                   //   . n .
00793                   //   w C e
00794                   //   . s .
00795                   //
00796                   // C is the current pixel (in component compIdx).
00797                   // Pixel map pointer offsets to point to:
00798                   //   * n: -(imgWidth + 4)  4 extra columns in the negative image
00799                   //   * w: -1
00800                   //   * e: +1
00801                   //   * s: +(imgWidth + 4)  4 extra columns in the negative image
00802                   // These pointers are always valid,
00803                   // by construction of the component image
00804 
00805                   if ((*pNegCompImg == compIdx) // C in current component
00806                       &&
00807                       (   (*(pNegCompImg - imgWidthPlus4) != compIdx)   // n != C
00808                        || (*(pNegCompImg - 1)             != compIdx)   // w != C
00809                        || (*(pNegCompImg + 1)             != compIdx)   // e != C
00810                        || (*(pNegCompImg + imgWidthPlus4) != compIdx))) // s != C
00811                     {
00812                       // The current pixel is in the border
00813                       ++contourCnt;
00814                       gradSum += *pGradModImg;
00815                     }
00816                   ++colIdx;
00817                   ++pNegCompImg;
00818                   ++pGradModImg;
00819                 }
00820 
00821               // Next row of the bounding box
00822               colIdx = 0;
00823               pNegCompImg += offNextRowNeg;
00824               pGradModImg += offNextRow;
00825             } // END for each row of the bounding box
00826               // (6.4.1) ============================
00827 
00828 
00829           // (6.4.2) If the average sum is less than argument aPostThrs,
00830           // ======= the component becomes WHITE in the final image
00831 
00832           if ((gradSum / contourCnt) < aPostThrs)
00833             {
00834               // pNegCompImg : pointer to the pixel map of the component image
00835               // offTopLNeg  : offset of the top left pixel of the component
00836               //               in the component image
00837               pNegCompImg = negCompImg.pPixMap() + offTopLNeg;
00838 
00839               // pResImg : pointer to the pixel map of the final image
00840               // offTopL : offset of the top left pixel of the component
00841               //           in the final image
00842               pResImg = this->pPixMap() + offTopL;
00843 
00844               // Offset of the top left pixel of the component
00845               // in the bounding box
00846               colIdx = comp.xTopLeftPix() - bb.xTopLeft();
00847           
00848               for (int rowIdx = 0 ; rowIdx < bbHeight ; ++rowIdx)
00849                 {
00850                   while (colIdx < bbWidth)
00851                     {
00852                       if ((*pNegCompImg) == compIdx)
00853                         {
00854                           // The current pixel belongs to the current component
00855                           // => it becomes WHITE in the final image
00856                           *pResImg = QGE_BW_WHITE;
00857                         }
00858                       ++colIdx;
00859                       ++pNegCompImg;
00860                       ++pResImg;
00861                     }
00862 
00863                   // Next row of the bounding box
00864                   colIdx = 0;
00865                   pNegCompImg += offNextRowNeg;
00866                   pResImg     += offNextRow;
00867                 } // END for each row of the bounding box
00868           
00869             } // END if average sum
00870               // (6.4.2) ==========
00871 
00872         } // END if current component is WHITE
00873           // #################################
00874       
00875     } // --------------------------------
00876       // END for each component (compIdx)
00877       // --------------------------------
00878 
00879 
00880   // (6.5) Delete useless Gradient module image
00881   // =====
00882   delete gradModImg;
00883 
00884 
00885   // ===================================================================
00886   // (7) FIRST AND LAST ROWS / FIRST AND LAST COLUMNS
00887   // ===================================================================
00888   // These rows and columns are set to WHITE when connected components
00889   // are constructed (see class qgar::ConnectedComponents)
00890   // => fill each of these rows (resp. columns) with its adjacent row
00891   //    (resp. column) in the final image
00892 
00893   // Pointer to the pixel map of the final image
00894   pResImg = this->pPixMap() + imgWidthPlus1;
00895 
00896   // TOP LEFT CORNER:    +------           +------
00897   //                     | . . .           | B . .
00898   //                     | . B      ==>    | B B<--- starting pixel
00899   //                     | .               | .
00900 
00901   if (*pResImg == QGE_BW_BLACK)
00902     {
00903       *(pResImg - 1)             = QGE_BW_BLACK;
00904       *(pResImg - imgWidthPlus1) = QGE_BW_BLACK;
00905     }
00906 
00907   // FIRST ROW
00908   for (int colIdx = 1 ; colIdx < imgWidthMinus1 ; ++colIdx)
00909     {
00910       *(pResImg - imgWidth) = *pResImg;
00911       ++pResImg;
00912     }
00913 
00914   // TOP RIGHT CORNER: same principle as top left corner
00915   if (*(pResImg - 1) == QGE_BW_BLACK)
00916     {
00917       *pResImg              = QGE_BW_BLACK;
00918       *(pResImg - imgWidth) = QGE_BW_BLACK;
00919     }
00920 
00921   // FIRST AND LAST COLUMNS
00922   for (int rowIdx = 1 ; rowIdx < (imgHeight - 3) ; ++rowIdx)
00923     {
00924       ++pResImg;
00925       *pResImg = *(pResImg + 1);
00926       pResImg += imgWidthMinus1;
00927       *pResImg = *(pResImg - 1);
00928     }
00929 
00930   // BOTTOM RIGHT CORNER: same principle as top left corner
00931   pResImg += 2;
00932   if (*pResImg == QGE_BW_BLACK)
00933     {
00934       *(pResImg - 1)              = QGE_BW_BLACK;
00935       *(pResImg + imgWidthMinus1) = QGE_BW_BLACK;
00936     }
00937 
00938   // LAST ROW
00939   for (int colIdx = 1 ; colIdx < imgWidthMinus1 ; ++colIdx)
00940     {
00941       *(pResImg + imgWidth) = *pResImg;
00942       ++pResImg;
00943     }
00944 
00945   // BOTTOM RIGHT CORNER: same principle as top left corner
00946   if (*(pResImg - 1) == QGE_BW_BLACK)
00947     {
00948       *pResImg              = QGE_BW_BLACK;
00949       *(pResImg + imgWidth) = QGE_BW_BLACK;
00950     }
00951 
00952 
00953   // ===================================================================
00954   // END of the constructor
00955   // ===================================================================
00956 }
00957 
00958 
00959 // ---------------------------------------------------------------------
00960 
00961 
00962 } // namespace qgar