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

Component.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   Component.C
00030  * @brief  Implementation of class qgar::Component.
00031  *
00032  * See file Component.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   March 17, 2004  17:17
00036  * @since  Qgar 2.1
00037  */
00038 
00039 
00040 //////////////////////////////////////////////////////////////////////////////
00041 #include <iostream>
00042 //////////////////////////////////////////////////////////////////////////////
00043 
00044 // STD
00045 #include <cmath>
00046 // STL
00047 #include <list>
00048 // QGAR
00049 #include <qgarlib/BoundingBox.H>
00050 #include <qgarlib/Component.H>
00051 #include <qgarlib/GenImage.H>
00052 #include <qgarlib/Maer.H>
00053 #include <qgarlib/primitives.H>
00054 
00055 
00056 
00057 namespace qgar
00058 {
00059 
00060 // -------------------------------------------------------------------
00061 // -------------------------------------------------------------------
00062 // S P E C I A L   L A B E L S   (STATIC)
00063 // -------------------------------------------------------------------
00064 // -------------------------------------------------------------------
00065 
00066 
00067 // MAXIMUM LABEL
00068 
00069 const Component::label_type
00070 Component::_MAX_LABEL = std::numeric_limits<Component::label_type>::max();
00071 
00072 
00073 // NO LABEL
00074 
00075 const Component::label_type
00076 Component::_NO_LABEL = -1;
00077 
00078 
00079 // -------------------------------------------------------------------
00080 // -------------------------------------------------------------------
00081 // AUXILIARY DATA FOR CONTOUR FOLLOWING (STATIC)
00082 // -------------------------------------------------------------------
00083 // -------------------------------------------------------------------
00084 
00085 
00086 // TABLE FOR X COORDINATES INCREMENTS
00087 
00088 const int Component::_s_incr_x[14] =
00089 {
00090   -1, // W  (N  entry)
00091   -1, // NW (NE entry)
00092    0, // N  (E  entry)
00093    1, // NE (SE entry)
00094    1, // E  (S  entry)
00095    1, // SE (SW entry)
00096    0, // S  (W  entry)
00097   -1, // SW (NW entry)
00098   -1, // W
00099   -1, // NW
00100    0, // N
00101    1, // NE
00102    1, // E
00103    1  // SE
00104 };
00105 
00106 
00107 // TABLE FOR Y COORDINATES INCREMENTS
00108 
00109 const int Component::_s_incr_y[14] =
00110 {
00111    0, // W  (N  entry)
00112   -1, // NW (NE entry)
00113   -1, // N  (E  entry)
00114   -1, // NE (SE entry)
00115    0, // E  (S  entry)
00116    1, // SE (SW entry)
00117    1, // S  (W  entry)
00118    1, // SW (NW entry)
00119    0, // W
00120   -1, // NW
00121   -1, // N
00122   -1, // NE
00123    0, // E
00124    1  // SE
00125 };
00126 
00127 
00128 // TABLE FOR NEW CONTOUR DIRECTIONS
00129 
00130 const int Component::_s_new_dir[14] =
00131 {
00132   6, // W
00133   7, // NW
00134   0, // N
00135   1, // NE
00136   2, // E
00137   3, // SE
00138   4, // S
00139   5, // SW
00140   6, // W
00141   7, // NW
00142   0, // N
00143   1, // NE
00144   2, // E
00145   3  // SE
00146 };
00147 
00148 
00149 // -------------------------------------------------------------------
00150 // -------------------------------------------------------------------
00151 // C O N S T R U C T O R S
00152 // -------------------------------------------------------------------
00153 // -------------------------------------------------------------------
00154 
00155 
00156 // DEFAULT CONSTRUCTOR.
00157 
00158 Component::Component()
00159 {
00160   // VOID
00161 }
00162 
00163 // CONSTRUCT FROM FULL DATA
00164 
00165 Component::Component(GenImage<Component::label_type>* aPCompImg,
00166                      int   aLabel,
00167                      int   anInLabel,
00168                      QGEbw aBW,
00169                      int   aXTopLeftPix,
00170                      int   aYTopLeftPix,
00171                      int   aXTopLeft,
00172                      int   aYTopLeft,
00173                      int   aXBottomRight,
00174                      int   aYBottomRight,
00175                      int   anAreaPixels)
00176 
00177   : _pCompImg    (aPCompImg),
00178     _label       (aLabel),
00179     _inLabel     (anInLabel),
00180     _color       (aBW),
00181     _xTopLeftPix (aXTopLeftPix),
00182     _yTopLeftPix (aYTopLeftPix),
00183     _boundingBox (aXTopLeft, aYTopLeft, aXBottomRight, aYBottomRight),
00184     _areaPixels  (anAreaPixels),
00185     _maer(0)
00186 
00187 {
00188   // VOID
00189 }
00190 
00191 // COPY CONSTRUCTOR.
00192 
00193 Component::Component(const Component& aCComp)
00194 
00195   : _pCompImg    (aCComp._pCompImg),
00196     _label       (aCComp._label),
00197     _inLabel     (aCComp._inLabel),
00198     _color       (aCComp._color),
00199     _xTopLeftPix (aCComp._xTopLeftPix),
00200     _yTopLeftPix (aCComp._yTopLeftPix),
00201     _boundingBox (aCComp._boundingBox),
00202     _areaPixels  (aCComp._areaPixels)
00203 
00204 {
00205   // Copy MAER if and only if it is computed
00206   if (_maer != 0)
00207     {
00208       _maer = new Maer(*(aCComp._maer));
00209     }
00210 }
00211 
00212 // -------------------------------------------------------------------
00213 // -------------------------------------------------------------------
00214 // D E S T R U C T O R
00215 // -------------------------------------------------------------------
00216 // -------------------------------------------------------------------
00217 
00218 
00219 Component::~Component()
00220 {
00221 //////////////////////////////////////////////////////////////////////////////
00222 //  std::cout << "Component> Destruction de la composante " << _label << std::endl;
00223 //////////////////////////////////////////////////////////////////////////////
00224 
00225   delete _maer;
00226 }
00227 
00228 
00229 // -------------------------------------------------------------------
00230 // -------------------------------------------------------------------
00231 // C O O R D I N A T E S
00232 // -------------------------------------------------------------------
00233 // -------------------------------------------------------------------
00234 
00235 
00236 // GET BOUNDING BOX
00237 
00238 const BoundingBox&
00239 Component::accessBoundingBox() const
00240 {
00241   return _boundingBox;
00242 }
00243 
00244 // GET A COPY OF THE BOUNDING BOX
00245 
00246 BoundingBox
00247 Component::boundingBox() const
00248 {
00249   return _boundingBox;
00250 }
00251 
00252 // GET DENSITY WITH REGARD TO THE BOUNDING BOX
00253 
00254 double
00255 Component::densityBox() const
00256 {
00257   double a = _boundingBox.area();
00258 
00259   if (a < 1.)
00260     {
00261       // When the area is less than 1, it should mean that the component
00262       // is a degenerated one, with a null length and/or width
00263       return 1.;
00264     }
00265   else
00266     {
00267       return ((double) _areaPixels) / a;
00268     }
00269 }
00270 
00271 
00272 // -------------------------------------------------------------------
00273 // -------------------------------------------------------------------
00274 // A C C E S S   T O   M A E R   (MINIMUM-AREA ENCASING RECTANGLE)
00275 // -------------------------------------------------------------------
00276 // -------------------------------------------------------------------
00277 
00278 
00279 // GET THE MAER
00280 
00281 const Maer&
00282 Component::accessMaer()
00283 {
00284   if (_maer == 0)
00285     {
00286       PRIVATEcomputeMaer();
00287     }
00288 
00289   return *_maer;
00290 }
00291 
00292 // GET A COPY OF THE MAER
00293 
00294 Maer
00295 Component::maer()
00296 {
00297   if (_maer == 0)
00298     {
00299       PRIVATEcomputeMaer();
00300     }
00301 
00302   return *_maer;
00303 }
00304 
00305 // GET MAER AREA
00306 
00307 int
00308 Component::maerAreaPixels()
00309 {
00310   if (_maer == 0)
00311     {
00312       PRIVATEcomputeMaer();
00313     }
00314 
00315   return _maerAreaPixels;
00316 }
00317 
00318 // GET DENSITY WITH REGARD TO THE MAER
00319 
00320 double
00321 Component::densityMaer()
00322 {
00323   if (_maer == 0)
00324     {
00325       PRIVATEcomputeMaer();
00326     }
00327 
00328   return ((double) _areaPixels) / ((double) _maerAreaPixels);
00329 }
00330 
00331 
00332 // -------------------------------------------------------------------
00333 // -------------------------------------------------------------------
00334 // A C C E S S   T O   T H E   C O N T O U R
00335 // -------------------------------------------------------------------
00336 // -------------------------------------------------------------------
00337 
00338 
00339 // GET A POINTER TO THE COMPONENT CONTOUR
00340 
00341 const std::list<DPoint>& 
00342 Component::accessContour()
00343 {
00344   if (_contour.empty())
00345     {
00346       PRIVATEcomputeContour();
00347     }
00348 
00349   return _contour;
00350 }
00351 
00352 // GET A COPY OF THE COMPONENT CONTOUR
00353 
00354 std::list<DPoint>
00355 Component::contour()
00356 {
00357   if (_contour.empty())
00358     {
00359       PRIVATEcomputeContour();
00360     }
00361 
00362   return _contour;
00363 }
00364 
00365 
00366 // -------------------------------------------------------------------
00367 // -------------------------------------------------------------------
00368 // O P E R A T O R S
00369 // -------------------------------------------------------------------
00370 // -------------------------------------------------------------------
00371 
00372 
00373 Component&
00374 Component::operator=(const Component& aCComp)
00375 {
00376   // Are left hand side and right hand side different objects?
00377   if (this != &aCComp)
00378     {
00379       _pCompImg    = aCComp._pCompImg;
00380       _label       = aCComp._label;
00381       _inLabel     = aCComp._inLabel;
00382       _color       = aCComp._color;
00383       _xTopLeftPix = aCComp._xTopLeftPix;
00384       _yTopLeftPix = aCComp._yTopLeftPix;
00385       _boundingBox = aCComp._boundingBox;
00386       _areaPixels  = aCComp._areaPixels;
00387 
00388       // Copy MAER if it is computed
00389       if (_maer != 0)
00390         {
00391           _maer = new Maer(*(aCComp._maer));
00392         }
00393     }
00394 
00395   return *this;
00396 }
00397 
00398 
00399 // -------------------------------------------------------------------
00400 // -------------------------------------------------------------------
00401 // C O N S T R U C T   T H E   C O N T O U R   (PRIVATE FUNCTION)
00402 // -------------------------------------------------------------------
00403 // -------------------------------------------------------------------
00404 
00405 
00406 // Compute the component contour
00407 
00408 void
00409 Component::PRIVATEcomputeContour()
00410 {
00411   if (_label == 0)
00412 
00413   // _________________________________________________________________
00414   //
00415   // WARNING: COMPONENT 0 (BACKGROUND) IS A PARTICULAR CASE
00416     {
00417       _contour.push_back(DPoint((double) _boundingBox.xTopLeft(),
00418                                 (double) _boundingBox.yTopLeft()));
00419       _contour.push_back(DPoint((double) _boundingBox.xBottomRight(),
00420                                 (double) _boundingBox.yTopLeft()));
00421       _contour.push_back(DPoint((double) _boundingBox.xBottomRight(),
00422                                 (double) _boundingBox.yBottomRight()));
00423       _contour.push_back(DPoint((double) _boundingBox.xTopLeft(),
00424                                 (double) _boundingBox.yBottomRight()));
00425     }
00426   // _________________________________________________________________
00427 
00428 
00429   else if (_areaPixels != 1)
00430 
00431   // _________________________________________________________________
00432   //
00433   // The component is not a single point
00434   // WARNING: Contour following does not work if component is a single
00435   // point, as starting point (the point constituting the component)
00436   // cannot be reached again when searching for a new contour point
00437   // around the starting point!
00438   // _________________________________________________________________
00439   //
00440 
00441     {
00442       // Component image width
00443       int width = _pCompImg->width();
00444 
00445       // Compute offset table to get pointers to 8-connexity neighbors
00446       // See NOTE (1) in comments of BLOCK-WHILE
00447       int mapPtOffset[14];
00448       for (int idx = 0 ; idx < 14 ; ++idx)
00449         {
00450           mapPtOffset[idx] = _s_incr_y[idx] * width;
00451         }
00452 
00453       // Pointer to the first contour point
00454       Component::label_type* pMap =
00455         _pCompImg->pPixMap() + (_yTopLeftPix * width) + _xTopLeftPix;
00456 
00457       // Mark this first point
00458       (*pMap) = _NO_LABEL;
00459 
00460       // Because of the way the component is constructed, current
00461       // direction can always be EAST (i.e. 2) in the beginning
00462       int currDir = 2;
00463 
00464       // Coordinates of the current contour point
00465       int currX = _xTopLeftPix;
00466       int currY = _yTopLeftPix;
00467 
00468       bool notBackToStartingPoint = true;
00469 
00470       while (notBackToStartingPoint)
00471         // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00472         // BLOCK-WHILE
00473         // while the new contour point is not the starting point
00474         // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00475         {
00476 
00477           for (int iCnt = 0 ; iCnt < 8 ; ++iCnt)
00478             //           ^^^        ^^^
00479             // for each direction around the current point,
00480             // from NORTH (0) to NORTH-WEST (7)
00481             {
00482             // COORDINATES AND DIRECTION COMPUTATION
00483 
00484             //  +---+---+---+
00485             //  |   | 1 | 2 |
00486             //  +---+---+---+
00487             //  | 7 | *-->3 |
00488             //  +---+---+---+
00489             //  | 6 | 5 | 4 |
00490             //  +---+---+---+
00491             //
00492             //  The outside of the component is always on the left 
00493             //  when moving along the contour direction. The figure
00494             // above gives an example: When following a contour in
00495             // the EAST direction, the first new possible contour
00496             // point (1) is left-perpendicular to the direction,
00497             // i.e. at NORTH of the current point (*), the second
00498             // point (2) is then at NORTH-EAST, and so on.
00499 
00500             //               0  1  2  3  4  5  6  7  8  9 10 11 12 13
00501             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00502             // _s_incr_x   |-1|-1| 0| 1| 1| 1| 0|-1|-1|-1| 0| 1| 1| 1|
00503             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00504             //
00505             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00506             // _s_incr_y   | 0|-1|-1|-1| 0| 1| 1| 1| 0|-1|-1|-1| 0| 1|
00507             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00508             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00509             // mapPtOffset | 0|-w|-w|-w| 0| w| w| w| 0|-w|-w|-w| 0| w|
00510             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00511             // initial       N NE  E SE  S SW  W NW
00512             // incrIdx
00513             //
00514             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00515             // _s_new_dir  | 6| 7| 0| 1| 2| 3| 4| 5| 6| 7| 0| 1| 2| 3|
00516             //             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00517             // newDir        W NW  N NE  E SE  S SW  W NW  N NE  E SE
00518             //               new contour directions
00519             //
00520             // The _s_incr_x (resp. y) table contains the X (resp. Y)
00521             // increment to get the X (resp. Y) coordinates of the 7
00522             // possible next contour points (points 1 to 7 on 1st
00523             // figure) from the X (resp. Y) coordinate of the current
00524             // point (*). The current contour direction (EAST on 1st
00525             // figure) determines the first index to enter the tables.
00526             //
00527             // Once a new contour point is found, table _s_new_dir
00528             // gives the corresponding new contour direction, using
00529             // the index corresponding to the new contour point in
00530             // table _s_incr_x or _s_incr_y. For example, if the new
00531             // contour point is at position 5 (on 1st figure), the
00532             // corresponding index in table _s_new_dir is 2 (for EAST,
00533             // current contour direction) + 4 (index of point at
00534             // position 5 among the neighbors of central point) = 6,
00535             // which gives SOUTH as new contour direction.
00536 
00537             // -------------------------------------------------------
00538             // NOTE (1) To avoid multiplications, table _s_incr_y
00539             // is substituted by table mapPtOffset, in which each
00540             // element is multiplied by the width (w) of the image.
00541             // -------------------------------------------------------
00542 
00543               // Index to look for possible contour points around the
00544               // current point (points 1 to 7 on the example above),
00545               // using tables _s_incr_x and _s_incr_y
00546               int incrIdx = currDir + iCnt;
00547 
00548               // Pointer to the point corresponding to this direction
00549               // in the pixel map of the component image
00550               Component::label_type* newPMap
00551                 = pMap + mapPtOffset[incrIdx] + _s_incr_x[incrIdx];
00552               //         ********************
00553               // See NOTE (1) in comments above
00554 
00555               if (*newPMap == _NO_LABEL)
00556                 {
00557                   //__________________________________________________
00558                   //
00559                   // THE NEW POINT IS MARKED
00560                   // => starting point is reached
00561 
00562                   // New contour direction corresponding to the point
00563                   // under consideration (see figure above)
00564                   int newDir = _s_new_dir[incrIdx];
00565 
00566                   if (currDir != newDir)
00567                     {
00568                       // Contour direction is changed => store the current
00569                       // point in the list representing the contour
00570                       _contour.push_back(DPoint((double) currX, (double) currY));
00571                     }
00572 
00573                   // Check that the starting point is not recorded
00574                   // in the contour before storing it
00575                   if (   ((int) (_contour.front()).x() != _xTopLeftPix)
00576                       || ((int) (_contour.front()).y() != _yTopLeftPix) )
00577                     {
00578                       _contour.push_front
00579                         (DPoint((double) _xTopLeftPix, (double) _yTopLeftPix));
00580                     }
00581 
00582                   // Unmark the starting point
00583                   (*newPMap) = _label;
00584 
00585                   // Leave loops
00586                   notBackToStartingPoint = false;
00587                   break;
00588                   //__________________________________________________
00589                 }
00590               else if (*newPMap == _label)
00591                 {
00592                   //__________________________________________________
00593                   //
00594                   // THE NEW POINT IS LABELLED LIKE THE COMPONENT
00595                   // => new contour point
00596                   pMap = newPMap;
00597 
00598                   // New contour direction corresponding to the point
00599                   // under consideration (see figure above)
00600                   int newDir = _s_new_dir[incrIdx];
00601 
00602                   if (currDir != newDir)
00603                     {
00604                       // Contour direction is changed => store the current
00605                       // point in the list representing the contour
00606                       _contour.push_back(DPoint((double) currX, (double) currY));
00607 
00608                       // Set new contour direction
00609                       currDir = newDir;
00610                     }
00611 
00612                   // Update coordinates of the new contour point
00613                   currX += _s_incr_x[incrIdx];
00614                   currY += _s_incr_y[incrIdx];
00615 
00616                   // Leave loop
00617                   break;
00618                   //__________________________________________________
00619                 }
00620 
00621             } // END for
00622 
00623         }
00624         // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00625         // END BLOCK-WHILE
00626         // back to the starting point
00627         // WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
00628     }
00629 
00630   else
00631 
00632   // _________________________________________________________________
00633   //
00634   // The component is a single point
00635   // _________________________________________________________________
00636   //
00637 
00638     {
00639       _contour.push_back(DPoint((double) _boundingBox.xTopLeft(),
00640                                 (double) _boundingBox.yTopLeft()));
00641     }
00642 
00643   // _________________________________________________________________
00644   //
00645   // END if
00646   // _________________________________________________________________
00647   //
00648 
00649 } // END Component::PRIVATEcomputeContour()
00650 
00651 
00652 // -------------------------------------------------------------------
00653 // -------------------------------------------------------------------
00654 // M A E R (PRIVATE FUNCTION)
00655 // -------------------------------------------------------------------
00656 // -------------------------------------------------------------------
00657 
00658 
00659 // Compute the MAER (Minimum-Area Encasing Rectangle)
00660 
00661 void
00662 Component::PRIVATEcomputeMaer()
00663 {
00664   // Construct the maer
00665   _maer = new Maer(accessContour());
00666 
00667   // WARNING: The computation of the MAER is performed in the continuous
00668   // 2D space, while the computation of connected components features is
00669   // performed in the 2D discrete space. In particular, a component area
00670   // is provided in terms of a number of pixels, but its MAER area is not
00671   // => calculate an APPROXIMATION of the MAER area in pixels,
00672   //    by adding 1 to the width and length of the MAER
00673 
00674   const std::list<DPoint>& lv = _maer->accessVertices();
00675   std::list<DPoint>::const_iterator itV1 = lv.begin();
00676   std::list<DPoint>::const_iterator itV2 = itV1;
00677   ++itV2;
00678 
00679   double dx12 = itV1->x() - itV2->x();
00680   double dy12 = itV1->y() - itV2->y();
00681 
00682   ++itV1;
00683   ++itV2;
00684   double dx23 = itV1->x() - itV2->x();
00685   double dy23 = itV1->y() - itV2->y();
00686 
00687   _maerAreaPixels = (int) std::ceil(  (hypot(dx12, dy12) + 1.)
00688                                     * (hypot(dx23, dy23) + 1.) );
00689 }
00690 
00691 // -------------------------------------------------------------------
00692 // -------------------------------------------------------------------
00693 
00694 } // namespace qgar