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