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

WDSegmentList.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  WDSegmentList.C
00030  * @brief Implementation of class qgar::WDSegmentList.
00031  *
00032  *        See file WDSegmentList.H for the interface.
00033  *
00034  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Karl Tombre">Karl Tombre</a>
00035  * @date   July 3, 2001  10:51
00036  * @since  Qgar 1.0
00037  */
00038 
00039 
00040 
00041 // STL
00042 #include <algorithm>
00043 #include <cmath>
00044 #include <cstdlib>
00045 // QGAR
00046 #include <qgarlib/AbstractGenPointChain.H>
00047 #include <qgarlib/image.H>
00048 #include <qgarlib/math.H>
00049 #include <qgarlib/primitives.H>
00050 #include <qgarlib/WDSegmentList.H>
00051 
00052 
00053 
00054 namespace qgar
00055 {
00056 
00057 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00058 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00059 // L O C A L    C O N S T A N T S
00060 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00061 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00062 
00063 
00064 // Maximum trivial direction change
00065 static const int WALLDANIELSSONSEGMENTLIST_MAX_DIR_CHANGE = 1;
00066 
00067 
00068 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00069 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00070 
00071 
00072 
00073 
00074 /*---------------------------------------------------------------------*
00075  |                                                                     |
00076  |             C  L  A  S  S      W  D  S  E  G  M  E  N  T            |
00077  |                                                                     |
00078  *---------------------------------------------------------------------*/
00079 
00080 // -------------------------------------------------------------------
00081 // C O N S T R U C T O R S 
00082 // -------------------------------------------------------------------
00083 
00084 
00085 // DEFAULT CONSTRUCTOR: CREATION OF A NULL SEGMENT
00086 
00087 WDSegment::WDSegment()
00088 
00089   : _sqrLength(0),
00090     _approxFactor(0)
00091 
00092 {
00093   // VOID
00094 }
00095 
00096 
00097 // CONSTRUCT FROM FULL DATA (USED DURING APPROXIMATION)
00098 
00099 WDSegment::WDSegment(GenPoint<int>& orig,
00100                      int deltaX,
00101                      int deltaY,
00102                      QGEdirection d)
00103 
00104   : Segment(orig, orig),
00105     _sqrLength(deltaX * deltaX + deltaY * deltaY),
00106     _approxFactor(0),
00107     _direction(d)
00108 
00109 {
00110   _target.translate(deltaX, deltaY);
00111 }
00112 
00113 
00114 // COPY CONSTRUCTOR
00115 
00116 WDSegment::WDSegment(const WDSegment& aSeg)
00117 {
00118   _source = aSeg._source;
00119   _target = aSeg._target;
00120   _sqrLength = aSeg._sqrLength;
00121   _approxFactor = aSeg._approxFactor;
00122   _direction = aSeg._direction;
00123 }
00124 
00125 
00126 // -------------------------------------------------------------------
00127 // T R A N S F O R M A T I O N 
00128 // -------------------------------------------------------------------
00129 
00130 
00131 // SET SEGMENT FROM AN OTHER SEGMENT (MEMBER-TO-MEMBER COPY)
00132 
00133 void
00134 WDSegment::set(const WDSegment& aSeg)
00135 {
00136   _source = aSeg._source;
00137   _target = aSeg._target;
00138   _sqrLength = aSeg._sqrLength;
00139   _approxFactor = aSeg._approxFactor;
00140   _direction = aSeg._direction;
00141 }
00142 
00143 
00144 // SET SEGMENT FROM GIVEN DATA.
00145 
00146 void
00147 WDSegment::set(GenPoint<int>& orig,
00148                int deltaX,
00149                int deltaY,
00150                QGEdirection d)
00151 {
00152   _source = orig;
00153   _target = orig;
00154   _target.translate(deltaX, deltaY);
00155   _sqrLength = deltaX * deltaX + deltaY * deltaY;
00156   _approxFactor = 0;
00157   _direction = d;
00158 }
00159 
00160 
00161 // TRANSLATE EXTREMUM POINT WITH GIVEN POINT
00162 
00163 void WDSegment::translateTarget(GenPoint<int>& translation)
00164 {
00165   _target += translation;
00166   
00167   // Work in relative coordinates
00168   Point p0 = _target - _source;
00169   _sqrLength = p0.x() * p0.x() + p0.y() * p0.y();
00170 
00171   int b1 = (abs(p0.x()) > (2 * abs(p0.y())));
00172   int b2 = (abs(p0.y()) > (2 * abs(p0.x())));
00173 
00174   _direction = (p0.x() > 0)
00175     ? (p0.y() > 0)
00176       ? (b1) ? QGE_DIRECTION_E : (b2) ? QGE_DIRECTION_S : QGE_DIRECTION_SE
00177       : (b1) ? QGE_DIRECTION_E : (b2) ? QGE_DIRECTION_N : QGE_DIRECTION_NE
00178     : (p0.y() > 0)
00179       ? (b1) ? QGE_DIRECTION_W : (b2) ? QGE_DIRECTION_S : QGE_DIRECTION_SE
00180       : (b1) ? QGE_DIRECTION_W : (b2) ? QGE_DIRECTION_N : QGE_DIRECTION_NW;
00181 }
00182 
00183 
00184 // -------------------------------------------------------------------
00185 // O P E R A T O R S 
00186 // -------------------------------------------------------------------
00187 
00188 
00189 // ASSIGNMENT
00190 
00191 WDSegment&
00192 WDSegment::operator=(const WDSegment& w)
00193 {
00194   _sqrLength = w._sqrLength;
00195   _approxFactor = w._approxFactor;
00196   _direction = w._direction;
00197   setSource(w.source());
00198   setTarget(w.target());
00199   return *this;
00200 }
00201 
00202 
00203 // -------------------------------------------------------------------
00204 
00205 
00206 
00207 
00208 
00209 
00210 /*---------------------------------------------------------------------*
00211  |                                                                     |
00212  |       C  L  A  S  S      W  D  S  E  G  M  E  N  T  L  I  S  T      |
00213  |                                                                     |
00214  *---------------------------------------------------------------------*/
00215 
00216 // -------------------------------------------------------------------
00217 // C O N S T R U C T O R S
00218 // -------------------------------------------------------------------
00219 
00220 
00221 // CONSTRUCT FROM GIVEN CHAIN, USING GIVEN THRESHOLD
00222 
00223 WDSegmentList::WDSegmentList(AbstractGenPointChain<int>& chain,
00224                              int wdThreshold)
00225 { 
00226   PRIVATEperform(chain, wdThreshold);
00227 }
00228 
00229 
00230 // -------------------------------------------------------------------
00231 // D E S T R U C T O R
00232 // -------------------------------------------------------------------
00233 
00234 
00235 // VIRTUAL DESTRUCTOR
00236 
00237 WDSegmentList::~WDSegmentList()
00238 {
00239   // VOID
00240 }
00241 
00242 
00243 // -------------------------------------------------------------------
00244 // COMPUTE THE WALL & DANIELSSON POLYGONAL APPROXIMATION OF A CHAIN
00245 // -------------------------------------------------------------------
00246 // Computer Vision, Graphics and Image Processing, 28:220-227, 1984
00247 //
00248 // Input parameters:
00249 //    the chain to approximate
00250 //    the W&D threshold
00251 // -------------------------------------------------------------------
00252 
00253 void
00254 WDSegmentList::PRIVATEperform(AbstractGenPointChain<int>& aChain,
00255                               int wdThreshold)
00256 {
00257   int ddist;
00258   int f;
00259   int l2;
00260   Point relativeCoords;
00261   WDSegment currseg;
00262 
00263   if (aChain.empty()) return; // nothing else to do
00264   
00265   // Initialize position in chain
00266   aChain.setToBegin();
00267   
00268   // Create first segment
00269   Point currpoint = aChain.current();
00270   WDSegment wdtmp(currpoint, 0, 0, QGE_DIRECTION_E);
00271 //   push_back(wdtmp);
00272 //   currseg = &back();
00273 
00274   currseg = wdtmp;
00275   _cutSeg.setSource(currpoint);  /*current point potential cut*/
00276   _cutSeg.setTarget(currpoint);
00277   _cutSeg.setApproxFactor(0);
00278   _cutSeg.setSqrLength(0);
00279   _cutSeg.setDirection(QGE_DIRECTION_E);
00280   
00281   while (aChain.hasNext())
00282     {
00283       Point prevpoint = currpoint;
00284       aChain.moveNext();
00285       currpoint = aChain.current();
00286 
00287       int dx = currpoint.x() - prevpoint.x();
00288       int dy = currpoint.y() - prevpoint.y();
00289 
00290       QGEdirection dir = qgDirection(dx, dy);
00291       Point translation = Point(dx, dy);
00292 
00293       /* call approximation algorithm */
00294       if(currseg.sqrLength() != 0)
00295         {
00296           relativeCoords = currpoint - currseg.source();
00297           f = currseg.approxFactor() + 
00298             (relativeCoords.x() * translation.y() - 
00299              relativeCoords.y() * translation.x());   /*new approximation*/
00300 
00301           l2 = relativeCoords.x() * relativeCoords.x() +
00302                relativeCoords.y() * relativeCoords.y();
00303           
00304           /*square of new length*/
00305 
00306           if (   (std::abs(f) <= Math::QG_SQRT_MAX_INT16b)
00307               && ((f * f) < (l2 * wdThreshold)))
00308             {
00309               /*if the new point can be added to current segment*/
00310         
00311               int oldf = currseg.approxFactor(); 
00312               int oldl2 = currseg.sqrLength();   /*save old values*/
00313               QGEdirection olddir = currseg.direction();
00314 
00315               currseg.translateTarget(translation);
00316               currseg.setApproxFactor(f);         /*new values*/
00317 
00318               ddist =  std::abs(((int) dir) - ((int) olddir));
00319               if (std::min(ddist,(8-ddist)) >
00320                     WALLDANIELSSONSEGMENTLIST_MAX_DIR_CHANGE)
00321                 {
00322                   /*if there is a non trivial direction change*/
00323 
00324                   if (_cutSeg.target() != prevpoint)
00325                     {
00326                       /*if cutpoint is not endpoint of segment*/
00327 
00328                       _newSeg.translateTarget(translation);
00329                       
00330                       Point newRelativeCoord = currpoint - _newSeg.source();
00331                       /*new approximation*/
00332                       _newSeg.setApproxFactor(_newSeg.approxFactor() + 
00333                         (newRelativeCoord.x() * translation.y() - 
00334                          newRelativeCoord.y() * translation.x()));
00335                     }
00336                   else
00337                     {
00338                       /*if there was no previous cutpoint*/
00339 
00340                       _newSeg.setSource(prevpoint); // new _newSeg
00341                       _newSeg.setTarget(prevpoint);
00342                       _newSeg.translateTarget(translation);
00343                       _newSeg.setApproxFactor(0);
00344 
00345                       // new _cutSeg is old segment
00346                       _cutSeg.setTarget(prevpoint);
00347                       _cutSeg.setSqrLength(oldl2);
00348                       _cutSeg.setApproxFactor(oldf);
00349                       _cutSeg.setDirection(olddir);
00350                     }
00351                 }
00352           
00353               else 
00354                 _cutSeg.setTarget(currpoint);
00355             }
00356           else
00357             {
00358               /*if it was not a good approximation, we must create new segment*/
00359               /*now, where do we have to cut the previous segment?*/
00360 
00361               if (_cutSeg.target() == prevpoint)
00362                 {
00363                   /*there was no special cutpoint : no problem*/
00364                   // store currseg
00365                   push_back(currseg);
00366                   // And modfiy it to new
00367                   currseg.set(prevpoint, translation.x(), translation.y(), dir);
00368                 }
00369               else
00370                 {
00371                   /*if there was a cutpoint*/
00372                   // store _cutSeg
00373                   push_back(_cutSeg);
00374 
00375                   // start again with _newSeg
00376                   currseg = _newSeg;
00377                   
00378                   relativeCoords = currpoint - currseg.source();
00379 
00380                   f = currseg.approxFactor() + 
00381                     (relativeCoords.x() * translation.y() - 
00382                      relativeCoords.y() * translation.x());
00383                   /*new approximation*/
00384 
00385                   l2 = relativeCoords.x() * relativeCoords.x() +
00386                    relativeCoords.y() * relativeCoords.y();
00387           
00388                   if ((l2 == 0) || 
00389                       ((std::abs(f) <= Math::QG_SQRT_MAX_INT16b)
00390                         && ((f * f) < (l2 * wdThreshold))))
00391                   
00392                     {
00393                       /*if this segment is good*/
00394 
00395                       currseg.translateTarget(translation);
00396                       currseg.setApproxFactor(f);           /*new values*/
00397 
00398                     }
00399 
00400                   else
00401                     {
00402                       // We must store the intermediate segment
00403                       // comprising former _newSeg and start a new one
00404                       push_back(_newSeg);
00405                       currseg.set(prevpoint, translation.x(),
00406                                   translation.y(), dir);
00407                       
00408                     }
00409                 }
00410 
00411               _cutSeg.setTarget(currpoint);
00412               _cutSeg.setSource(currseg.source()); /*no special cutpoint yet*/
00413             }
00414         }
00415       else
00416         {
00417           currseg.setSource(prevpoint);
00418           currseg.setTarget(currpoint);
00419           currseg.setSqrLength(translation.x() * translation.x() +
00420                          translation.y() * translation.y());
00421           currseg.setApproxFactor(0);
00422           currseg.setDirection(dir);
00423           _cutSeg = currseg;
00424         }
00425     }
00426 
00427   /*See if we can merge the first and the last into one segment*/
00428   /* This only applies to closed curves */
00429 
00430   if (aChain.front() == aChain.back())   // if chain is a loop
00431     {
00432       // currseg = &back(); // *** ATTENTION !!! ****
00433       WDSegment firstseg = front();
00434   
00435       ddist = std::abs((int) currseg.direction() - (int) firstseg.direction());
00436       if ((currseg != firstseg)
00437           && (std::min(ddist,(8-ddist)) <= WALLDANIELSSONSEGMENTLIST_MAX_DIR_CHANGE))
00438         {
00439           /*it may be possible to merge them*/
00440       
00441           /*according to my calculations, the new f should be equal*/
00442           /*to vectorial_product(currseg,firstseg) + f1 + f2*/
00443           /*where f1 and f2 are the WD approximations for*/
00444           /*the 2 segments*/
00445           f = (firstseg.source().x() - currseg.source().x()) *
00446             (firstseg.target().y() - firstseg.source().y()) -
00447             (firstseg.source().y() - currseg.source().y()) *
00448             (firstseg.target().x() - firstseg.source().x());
00449           f += firstseg.approxFactor() + currseg.approxFactor();
00450 
00451           relativeCoords = firstseg.target() - currseg.source();
00452 
00453           l2 = relativeCoords.x() * relativeCoords.x() +
00454             relativeCoords.y() * relativeCoords.y();
00455           
00456           /*square of new length*/
00457     
00458           if ((std::abs(f) <= Math::QG_SQRT_MAX_INT16b)
00459               && (f*f < (l2*wdThreshold)))
00460             {
00461               /*we can merge the 2 segments*/
00462               firstseg.setSource(currseg.source());  /*new origin of firstseg*/
00463               firstseg.setTarget(currseg.source());
00464               firstseg.translateTarget(relativeCoords);
00465               firstseg.setApproxFactor(f);
00466               // Update chaining
00467               // pop_back();
00468             }
00469           else
00470             // Store currseg
00471             push_back(currseg);
00472         }
00473       else
00474         push_back(currseg);
00475     }
00476   else
00477     push_back(currseg);
00478 }
00479 
00480 // -------------------------------------------------------------------
00481 
00482 } // namespace qgar