PARP Research Group University of Murcia, Spain


src/qvip/qvpolyline.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008, 2009. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include <iostream>
00026 #include <float.h>
00027 
00028 #include <qvmath.h>
00029 #include <qvdefines.h>
00030 #include <qvmatrixalgebra.h>
00031 
00032 #include <QVPolyline>
00033 #include <QVPolylineF>
00034 
00036 // QVPolylines
00037 
00038 // Iterative point elimination:
00039 #ifndef DOXYGEN_IGNORE_THIS
00040 class ClassAuxIPE
00041         {
00042         public:
00043                 double cost;
00044                 int index;
00045                 ClassAuxIPE *prev,*next;
00046         };
00047 
00048 bool costLessThan(ClassAuxIPE* &s1,ClassAuxIPE* &s2)
00049         {
00050         return s1->cost < s2->cost;
00051         }
00052 
00053 bool indexLessThan(ClassAuxIPE* &s1,ClassAuxIPE* &s2)
00054         {
00055         return s1->index < s2->index;
00056         }
00057 
00058 inline double costElimination(const QVPolyline &polyline,int ia, int ib, int ic)
00059         {
00060         double   xA,yA,xB,yB,xC,yC;
00061         xA = polyline[ia].x(); yA=polyline[ia].y();
00062         xB = polyline[ib].x(); yB=polyline[ib].y();
00063         xC = polyline[ic].x(); yC=polyline[ic].y();
00064         if((xA != xC) or (yA != yC))
00065                 return ABS(xA*(yC-yB) + xB*(yA-yC) + xC*(yB-yA)) / sqrt((xA-xC)*(xA-xC)+(yA-yC)*(yA-yC));
00066         else
00067                 return sqrt((xB-xC)*(xB-xC)+(yB-yC)*(yB-yC));
00068         }
00069 
00070 class auxLine {
00071         public:
00072         auxLine(double l1,double l2,double l3,bool ok) : l1(l1),l2(l2),l3(l3),ok(ok) {};
00073         double l1,l2,l3;
00074         bool ok;
00075 };
00076 #endif
00077 
00078 double IterativePointElimination(const QVPolyline &polyline, QVPolyline &result,
00079         const double param, bool maxNumberOfPointsMethod,bool intersectLines,
00080         double *max_removed_cost)
00081         {
00082         const uInt tot_siz = polyline.size();
00083         QList<ClassAuxIPE*> list;
00084 
00085         // We start with an empty list:
00086         result.clear();
00087 
00088         // Maximum removed cost initialized to zero:
00089         if(max_removed_cost != NULL) *max_removed_cost = 0.0;
00090 
00091         // Only for polylines with 3 points or more; otherwise, the same
00092         // input polyline is returned:
00093         if(polyline.size()<3)
00094                 {
00095                 result = polyline;
00096                 return FLT_MAX;
00097                 }
00098 
00099         // Initialization of main data structure:
00100         for(uInt i=0;i<tot_siz;i++)
00101                 list.push_back(new ClassAuxIPE);
00102 
00103         for(uInt i=0;i<tot_siz;i++)
00104                 {
00105                 int ia = (i==0)?tot_siz-1:i-1, ib = i, ic = (i==tot_siz-1)?0:i+1;
00106                 list[i]->cost = costElimination(polyline,ia,ib,ic);
00107                 list[i]->index = ib;
00108                 list[i]->prev = list[ia];
00109                 list[i]->next = list[ic];
00110                 }
00111         if(not polyline.closed) // If not closed, never eliminate end points:
00112                 {
00113                 list[0]->cost = FLT_MAX;
00114                 list[tot_siz-1]->cost = FLT_MAX;
00115                 }
00116         qSort(list.begin(),list.end(),costLessThan);
00117 
00118         // Main loop:
00119         while(TRUE)
00120                 {
00121                 // Stop condition:
00122                 if( (list.size() == 3) or // Minimal size of a polyline.
00123                     ((not maxNumberOfPointsMethod) and (list[0]->cost > param)) or
00124                     ((maxNumberOfPointsMethod) and
00125                      (list.size() <= static_cast<int>(param))) )
00126                         break;
00127 
00128                 // Removal of best point (first in the list): 
00129                 ClassAuxIPE *elem = list.takeAt(0),
00130                             *elemPrev = list.takeAt(list.indexOf(elem->prev)),
00131                             *elemNext = list.takeAt(list.indexOf(elem->next));
00132                 elemPrev->next = elem->next;
00133                 elemNext->prev = elem->prev;
00134                 if(elemPrev->cost != FLT_MAX)
00135                         elemPrev->cost = costElimination(polyline,elemPrev->prev->index,
00136                                                                 elemPrev->index,
00137                                                                 elemPrev->next->index);
00138                 if(elemNext->cost != FLT_MAX)
00139                 elemNext->cost = costElimination(polyline,elemNext->prev->index,
00140                                                         elemNext->index,
00141                                                         elemNext->next->index);
00142 
00143                 // Binary (fast) insertion of neighbours in data structure:
00144                 int here;
00145                 for(int i=0;i<2;i++)
00146                         {
00147                         ClassAuxIPE* newelem = ((i==0)?elemNext:elemPrev);
00148                         int first=0,last=list.size()-1;
00149                         while (first <= last) {
00150                                 int mid = (first + last) / 2;
00151                                 if (newelem->cost > list[mid]->cost)
00152                                         first = mid + 1;
00153                                 else if (newelem->cost < list[mid]->cost)
00154                                         last = mid - 1;
00155                                 else
00156                                         {
00157                                         here = mid;
00158                                         break;
00159                                         }
00160                         }
00161                         if(first>last)
00162                                 here=first;
00163                         list.insert(here,newelem);
00164 
00165                         }
00166 
00167                 if(max_removed_cost != NULL)
00168                         if(elem->cost > *max_removed_cost)
00169                                 *max_removed_cost = elem->cost;
00170                 delete elem;
00171                 }
00172 
00173         // We will return the cost of the first non deleted point:
00174         double return_value = list.first()->cost;
00175 
00176         // Once IPE finished, sort the list by position in original polyline:
00177         qSort(list.begin(),list.end(),indexLessThan);
00178 
00179         // Now, postprocess, fitting lines:
00180         QList<ClassAuxIPE*>::iterator it = list.begin();
00181         if(intersectLines)
00182                 {
00183                 // Line intersection computation (could be subpixel, in fact...):
00184                 double ratio_eig=1.0;
00185                 QList<auxLine> lines;
00186                 for(int i=0;i<list.size();i++)
00187                         {
00188                         // If not closed, do not need to compute last line:
00189                         if((not polyline.closed) and (i==list.size()-1))
00190                                 break;
00191                         int i1 = list[i]->index;
00192                         int i2 = list[(i+1)%list.size()]->index;
00193                         if(i2<i1) i2 += tot_siz;
00194                         int dist = i2-i1+1;
00195                         #define MIN_PIXELS_IPE_LINE 15
00196                         if(dist >= MIN_PIXELS_IPE_LINE)
00197                                 {
00198                                 i1 = (i1+dist/5)%tot_siz;
00199                                 i2 = (i2-dist/5)%tot_siz;
00200                                 dist = dist-2*(dist/5);
00201                                 }
00202                         else 
00203                                 {
00204                                 dist = i2-i1+1;
00205                                 i1 = i1%tot_siz;
00206                                 i2 = i2%tot_siz;
00207                                 }
00208         
00209                         double x=0,y=0,xx=0,xy=0,yy=0;
00210                         uInt j=i1;
00211                         do
00212                                 {
00213                                 x += polyline[j].x();
00214                                 y += polyline[j].y();
00215                                 xx += polyline[j].x()*polyline[j].x();
00216                                 xy += polyline[j].x()*polyline[j].y();
00217                                 yy += polyline[j].y()*polyline[j].y();
00218                                 j = (j+1)%tot_siz;
00219                                 } while(j!=(i2+1)%tot_siz);
00220                         double l1,l2,l3;
00221                         x /= dist; y /= dist; xx /= dist; xy /= dist; yy /= dist;
00222                         // If line does not fit well, just put old point instead of intersection:
00223                         ratio_eig = homogLineFromMoments(x,y,xx,xy,yy,l1,l2,l3);
00224                         lines.push_back(auxLine(l1,l2,l3,ratio_eig < 0.1));
00225                         }
00226 
00227                 for(int i=0;i<list.size();i++)
00228                         {
00229                         QPoint oldPoint = polyline[list[i]->index];
00230                         if( (not polyline.closed) and ((i==0) or (i==list.size()-1)))
00231                                 {
00232                                 // If not closed, just include end points:
00233                                 result.append(oldPoint);
00234                                 continue;
00235                                 }
00236                         int ant = (i-1+list.size())%list.size();
00237                         int post = (i+1)%list.size();
00238                         double  newx = (lines[i].l2)*(lines[ant].l3) - (lines[i].l3)*(lines[ant].l2);
00239                         double  newy = -(lines[i].l1)*(lines[ant].l3) + (lines[i].l3)*(lines[ant].l1);
00240                         double  newz = (lines[i].l1)*(lines[ant].l2) - (lines[i].l2)*(lines[ant].l1);
00241                         if ( (not lines[i].ok) or (not lines[ant].ok) or // Bad segment
00242                                 (fabs(newz) < EPSILON) ) // Lines too parallel
00243                                 result.append(oldPoint);
00244                         else
00245                                 {
00246                                 int nx = qRound(newx/newz);
00247                                 int ny = qRound(newy/newz);
00248                                 // Only consider intersection if it is inside
00249                                 // a maximum radius circle around the old point
00250                                 // (relative to its nearer previous/next point
00251                                 // in polyline):
00252                                 double dist =
00253                                         sqrt((nx-oldPoint.x())*(nx-oldPoint.x()) +
00254                                              (ny-oldPoint.y())*(ny-oldPoint.y()));
00255                                 QPoint prevPoint = polyline[list[ant]->index],
00256                                         nextPoint = polyline[list[post]->index];
00257                                 double minDist =
00258                                         qMin(
00259                                         sqrt((prevPoint.x()-oldPoint.x())*(prevPoint.x()-oldPoint.x()) +
00260                                         (prevPoint.y()-oldPoint.y())*(prevPoint.y()-oldPoint.y())),
00261                                         sqrt((nextPoint.x()-oldPoint.x())*(nextPoint.x()-oldPoint.x()) +
00262                                         (nextPoint.y()-oldPoint.y())*(nextPoint.y()-oldPoint.y()))
00263                                         );
00264                                 if(dist < 0.2*minDist)
00265                                         result.append(QPoint(nx,ny));
00266                                 else
00267                                         result.append(oldPoint);
00268                                 }
00269                         }
00270                 }
00271         else 
00272                 {
00273                 // No postprocess, simply store the resulting points in result polyline.
00274                 it = list.begin();
00275                 while(it != list.end())
00276                         {
00277                                 result.append(polyline.at((*it)->index));
00278                                 it++;
00279                         }
00280                 }
00281 
00282         // Free memory of remaining structure:
00283         while (!list.isEmpty())
00284                 delete list.takeFirst();
00285 
00286         // Polyline type and direction are the same of the original polyline:
00287         result.closed = polyline.closed;
00288         result.direction = polyline.direction;
00289 
00290         // We return the cost of the first non deleted point:
00291         return return_value;
00292         }
00293 
00294 
00295 // Draw
00296 QVPolyline::QVPolyline(): QList<QPoint>(),
00297         closed(false), direction(false)//, dir(0)
00298         {
00299         qDebug() << "QVPolyline()";
00300         qDebug() << "QVPolyline() <~ return";
00301         };
00302 
00303 QVPolyline::QVPolyline(const QVPolyline &polyline): QList<QPoint>(polyline),
00304         closed(polyline.closed), direction(polyline.direction)//, dir(polyline.dir)
00305         {
00306         qDebug() << "QVPolyline(const QVPolyline &)";
00307         qDebug() << "QVPolyline(const QVPolyline &) <~ return";
00308         };
00309 
00310 QVPolyline::QVPolyline(const QVPolylineF &polyline): QList<QPoint>(),
00311         closed(polyline.closed), direction(polyline.direction)//, dir(polyline.dir)
00312         {
00313         foreach(QPointF pointf, polyline)
00314                 {
00315                 append( QPoint(pointf.toPoint()) );
00316                 }
00317         qDebug() << "QVPolylineF(const QVPolylineF &)";
00318         qDebug() << "QVPolylineF(const QVPolylineF &) <~ return";
00319         };
00320 
00321 // QPoint       linesIntersection(QPoint a, QPoint b, QPoint c, QPoint d)
00322 //      // gets the intersection point for lines ab and cd
00323 //      {
00324 //      double x1 = a.rx(), x2 = b.rx(), x3 = c.rx(), x4 = d.rx();
00325 //      double y1 = a.ry(), y2 = b.ry(), y3 = c.ry(), y4 = d.ry();
00326 // 
00327 //      double denominador = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
00328 //      if (denominador == 0)
00329 //              return QPoint( (int)(b.rx() + c.rx())/2, (int)(b.ry() + c.ry())/2 );
00330 // 
00331 //      double  ua = (x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3),
00332 //              ub = (x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3);
00333 // 
00334 //      QPoint p = QPoint(
00335 //                      (int) (x1 + ua*(x2 - x1) / denominador),
00336 //                      (int) (y1 + ub*(y2 - y1) / denominador)
00337 //                      );
00338 //      return p;
00339 //      }
00340 
00341 
00342 
00344 
00345 QVPolyline QVPolyline::ellipse(uInt n, float x, float y, float maxRadio, float minRadio, float ang)
00346         {
00347         QVPolyline ellipse;
00348         float w = 2*PI/(n-1);
00349         float maxArg = (maxRadio+minRadio)/2;
00350         float minArg = (maxRadio-minRadio)/2;
00351 
00352         for (uInt t = 0; t < n; t++)
00353                 ellipse.append(QPoint ( (uInt) (x + maxArg*cos(ang+w*t) + minArg*cos(ang-w*t)),
00354                                                 (uInt) (y + maxArg*sin(ang+w*t) + minArg*sin(ang-w*t))
00355                                                 ));
00356         return ellipse;
00357         }
00358 
00359 QVPolyline QVPolyline::rectangle(int x1, int y1, int x2, int y2)
00360         {
00361         QVPolyline rectangle;
00362         rectangle.append(QPoint( x1, y1 ));
00363         rectangle.append(QPoint( x1, y2 ));
00364         rectangle.append(QPoint( x2, y2 ));
00365         rectangle.append(QPoint( x2, y1 ));
00366         rectangle.append(QPoint( x1, y1 ));
00367         return rectangle;
00368         }
00369 
00370 QVPolyline QVPolyline::line(int x1, int y1, int x2, int y2)
00371         {
00372         QVPolyline line;
00373 
00374         line.append(QPoint( x1, y1 ));
00375 
00376         if (x1 == x2 && y1 == y2)
00377                 return line;
00378 
00379         int i,dx,dy,sdx,sdy,dxabs,dyabs,x,y,px,py;
00380         
00381         dx=x2-x1;      // the horizontal distance of the line
00382         dy=y2-y1;      // the vertical distance of the line
00383         dxabs=abs(dx);
00384         dyabs=abs(dy);
00385         sdx=SIGN(dx);
00386         sdy=SIGN(dy);
00387         x=dyabs>>1;
00388         y=dxabs>>1;
00389         px=x1;
00390         py=y1;
00391         
00392         if (dxabs>=dyabs) // the line is more horizontal than vertical
00393                 for(i=0;i<dxabs;i++)
00394                         {
00395                         y+=dyabs;
00396                         if (y>=dxabs)
00397                                 {
00398                                 y-=dxabs;
00399                                 py+=sdy;
00400                                 }
00401                         px+=sdx;
00402                         line.append(QPoint( px, py ));
00403                         }
00404         else    // the line is more vertical than horizontal
00405                 for(i=0;i<dyabs;i++)
00406                         {
00407                         x+=dxabs;
00408                         if (x>=dyabs)
00409                                 {
00410                                 x-=dyabs;
00411                                 px+=sdx;
00412                                 }
00413                         py+=sdy;
00414                         line.append(QPoint( px, py ));
00415                         }
00416 
00417         return line;
00418         }
00419 
00420 QVPolyline::operator QVPolylineF() const
00421         {
00422         QVPolylineF polyline;
00423         foreach(QPoint point, *this)
00424                 {
00425                 polyline.append(QPointF(point));
00426                 }
00427         return polyline;
00428         }
00429 
00430 
00432 // Direction-number             Y
00433 //      NE-7    N-0     NW-1    |
00434 //      E-6     *       W-2     v
00435 //      SE-5    S-4     SW-3
00436 // X -->
00437 //                                              N       NO      O       SO      S       SE      E       NE
00438 #ifndef DOXYGEN_IGNORE_THIS
00439 const char      coorX8Connect[8] =      {       0,      1,      1,      1,      0,      -1,     -1,     -1      };
00440 const char      coorY8Connect[8] =      {       -1,     -1,     0,      1,      1,      1,      0,      -1      };
00441 const char      coorX4Connect[4] =      {       0,              1,              0,              -1,             };
00442 const char      coorY4Connect[4] =      {       -1,             0,              1,              0,              };
00443 const char      coorX4Diag[8] =         {               1,              1,              -1,             -1      };
00444 const char      coorY4Diag[8] =         {               -1,             1,              1,              -1      };
00445 #endif
00446 
00447 // Auxiliar function for border extraction. It gets a border point, and the direction where there is one of the outside of the connected-set pixels.
00448 #ifndef DOXYGEN_IGNORE_THIS
00449 QVPolyline getConnectedSetBorderContourThresholdFromBorderPoint(const QVImage<uChar> &image, const int startPointX, const int startPointY, const uChar threshold)
00450         {
00451         QVPolyline lista;
00452 
00453         lista.closed = true;
00454         lista.append(QPoint(startPointX, startPointY));
00455 
00456         QVIMAGE_INIT_READ(uChar,image);
00457         QRect roi = image.getROI();
00458 
00459         Q_ASSERT_X(roi.contains(startPointX, startPointY), "getContourThresholdFromBorderPoint", "start point out of image ROI");
00460         Q_ASSERT_X(QVIMAGE_PIXEL(image, startPointX, startPointY, 0) >= threshold, "getContourThresholdFromBorderPoint", "start point is not contained in a connected set");
00461 
00462         // We check this is not an interior pixel, neither a solitary one.
00463         // Also we look for a neighbour pixel not belonging to any connected set.
00464         uChar searchDir = 128, numOuterPixels = 0;
00465         for (int i = 0; i<8; i++)
00466                 {
00467                 int x =  startPointX +coorX8Connect[i], y =  startPointY +coorY8Connect[i];
00468                 if (!roi.contains(x, y))
00469                         {
00470                         numOuterPixels++;
00471                         searchDir = i;
00472                         }
00473                 else if (QVIMAGE_PIXEL(image, x, y,0) < threshold)
00474                         {
00475                         numOuterPixels++;
00476                         searchDir = i;
00477                         }
00478                 }
00479 
00480         // Case we receive an interior pixel, raise assert.
00481         Q_ASSERT_X(searchDir < 8, "getContourThresholdFromBorderPoint", "start point is inside the set, not in the border");
00482 
00483         // Case we have a solitary pixel, we return that pixel.
00484         if (numOuterPixels == 8)
00485                 return lista;
00486 
00487         // We insert each point of the border contour, inserting it to the point list.
00488         int sumSearchDir = 0, actualPointX = startPointX, actualPointY = startPointY;
00489         while (true)
00490                 {
00491                 // We search for the next point belonging to the contour.
00492                 uChar d;
00493                 int     nextPointX, nextPointY;
00494                 for (d = 0; d < 8; d++)
00495                         {
00496                         searchDir = (searchDir+1)%8;
00497                         nextPointX = actualPointX + coorX8Connect[searchDir];
00498                         nextPointY = actualPointY + coorY8Connect[searchDir];
00499                         if (roi.contains(nextPointX, nextPointY))
00500                                 if ( (QVIMAGE_PIXEL(image, nextPointX, nextPointY,0) >= threshold) )
00501                                         break;
00502                         }
00503 
00504                 sumSearchDir += d - 3;
00505 
00506                 actualPointX = nextPointX;
00507                 actualPointY = nextPointY;
00508 
00509                 if ( QVIMAGE_PIXEL(image, actualPointX, actualPointY,0) < threshold )
00510                         break;
00511 
00512                 if ( startPointX == actualPointX && startPointY == actualPointY)
00513                         break;
00514 
00515                 lista.append(QPoint(actualPointX, actualPointY));
00516                 searchDir = searchDir + 4;
00517                 }
00518 
00519         lista.direction = (sumSearchDir >= 0);
00520         return lista;
00521         }
00522 #endif
00523 
00524 QVPolyline getConnectedSetBorderContourThreshold(const QVImage<uChar> &image, const QPoint startPoint, const uChar threshold)
00525         {
00526         QVIMAGE_INIT_READ(uChar,image);
00527         const QRect roi = image.getROI();
00528 
00529         int col = startPoint.x(), row = startPoint.y();
00530 
00531         if (QVIMAGE_PIXEL(image, col, row,0) < threshold)
00532                 return QVPolyline();
00533 
00534         while (roi.contains(col+1, row))
00535                 {
00536                 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
00537                         break;
00538                 col++;
00539                 }
00540 
00541         return getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00542         }
00543 
00544 QList<QVPolyline> getConnectedSetBorderContoursThreshold(const QVImage <uChar> &image, const uChar threshold)
00545         {
00546         qDebug() << "getPolylinesThreshold()";
00547         QVImage<uChar> mask(image.getCols()+1, image.getRows()+1);
00548         Set(0, mask);
00549 
00550         QVIMAGE_INIT_READ(uChar,image);
00551         QVIMAGE_INIT_WRITE(uChar,mask);
00552 
00553         const QRect roi = image.getROI();
00554 
00555         QList<QVPolyline> polylineList;
00556 
00557         // We look for pixels contained in a connected set (gray-level value >= threshold) in the image
00558         for (int row = roi.y(); row < roi.y() + roi.height(); row++)
00559                 for (int col = roi.x(); col < roi.y() + roi.width(); col++)
00560                         {
00561                         // If we find any pixel like that, we can be sure (because the search we did) it belongs to it's border.
00562                         if (QVIMAGE_PIXEL(image, col, row,0) >= threshold)
00563                                 {
00564                                 // if pixel is not marked, we get it's contour
00565                                 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
00566                                         {
00567                                         QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00568                                         polylineList.append(lista);
00569 
00570                                         QListIterator<QPoint> iterator(lista);
00571                                         for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
00572                                                 {
00573                                                 actual = iterator.next();
00574                                                 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
00575                                                         QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
00576                                                 }
00577                                         }
00578 
00579                                 // We ensure next pixel we process will not belong to a connected set.
00580                                 while (roi.contains(col+1, row))
00581                                         {
00582                                         if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
00583                                                 break;
00584                                         col++;
00585                                         }
00586 
00587                                 // This is for the case in which we find an internal contour, that has not been processed and marked.
00588                                 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
00589                                         {
00590                                         QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00591                                         polylineList.append(lista);
00592 
00593                                         QListIterator<QPoint> iterator(lista);
00594                                         for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
00595                                                 {
00596                                                 actual = iterator.next();
00597                                                 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
00598                                                         QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
00599                                                 }
00600                                         }
00601                                 }
00602 
00603                         }
00604         qDebug() << "getPolylinesThreshold():"<< polylineList.size() << "contours obtained";
00605         qDebug() << "getPolylinesThreshold() <~ return";
00606         return polylineList;
00607         }
00608 
00610 
00611 QVPolyline getLineContourThreshold4Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
00612         {
00613         const uInt cols = image.getCols(), rows = image.getRows();
00614         QVIMAGE_INIT_WRITE(uChar, image);
00615 
00616         uInt lastDir = 666, coorX = point.x(), coorY = point.y();
00617 
00618         qDebug() << "\tContour: new contour";
00619 
00620         forever {
00621                 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
00622                 if (reverse)
00623                         polyline.prepend(QPoint(coorX, coorY));
00624                 else
00625                         polyline.append(QPoint(coorX, coorY));
00626 
00627                 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
00628 
00629                 uInt dir;
00630                 int newCoorX, newCoorY;
00631                 for (dir = 0; dir < 4; dir++)
00632                         {
00633                         newCoorX = coorX + coorX4Connect[dir];
00634                         newCoorY = coorY + coorY4Connect[dir];
00635 
00636                         // Check if we are inside the limits in that direction
00637                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00638                                 continue;
00639 
00640                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00641                         if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00642                                 break;
00643                         }
00644 
00645                 if (dir == 4) break;
00646 
00647                 coorX = newCoorX;
00648                 coorY = newCoorY;
00649                 lastDir = (dir+2)%4;
00650                 }
00651 
00652         return polyline;
00653         }
00654 
00655 QList<QVPolyline> getLineContoursThreshold4Connectivity(const QVImage<uChar> &image, const uChar threshold)
00656         {
00657         const uInt cols = image.getCols(), rows = image.getRows();
00658         QVImage<uChar> clone = image;
00659 
00660         QList<QVPolyline> polylineList;
00661 
00662         // Transverse the image
00663         for(uInt col = 0; col < cols; col++)
00664                 for(uInt row = 0; row < rows; row++)
00665                         {
00666                         QVIMAGE_INIT_READ(uChar, clone);
00667                         // If we don't have an active pixel, continue
00668                         if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
00669                                 continue;
00670 
00671                         // Else, we compose the contour following two active neighbour pixels:
00672                         QVPolyline polyline;
00673 
00674                         // We follow first active neighbour pixel, composing the list of pixels in direct order
00675                         getLineContourThreshold4Connectivity(clone, QPoint(col, row), polyline, threshold, false);
00676 
00677                         // Find another neighbour close to the pixel.
00678                         uInt dir;                       
00679                         int     newCoorX, newCoorY;
00680                         for (dir = 0; dir < 4; dir++)
00681                                 {
00682                                 newCoorX = col + coorX4Connect[dir];
00683                                 newCoorY = row + coorY4Connect[dir];
00684         
00685                                 // Check if we are inside the limits in that direction
00686                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00687                                         continue;
00688         
00689                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00690                                 if ( (clone(newCoorX, newCoorY) >= threshold) )
00691                                         break;
00692                                 }
00693 
00694                         // If we found it, add the contour in reverse order.
00695                         if (dir != 4)
00696                                 getLineContourThreshold4Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00697 
00698                         // Finally add the polyline to the list.
00699                         polylineList.append(polyline);
00700                         }
00701 
00702         return polylineList;
00703         }
00704 
00706 // Replicated functions from 4-connected version.
00707 QVPolyline getLineContourThreshold8Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
00708         {
00709         const uInt cols = image.getCols(), rows = image.getRows();
00710         QVIMAGE_INIT_WRITE(uChar, image);
00711 
00712         uInt lastDir = 666, coorX = point.x(), coorY = point.y();
00713 
00714         qDebug() << "\tContour: new contour";
00715 
00716         bool continueCond = true;
00717         while(continueCond)
00718                 {
00719                 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
00720                 if (reverse)
00721                         polyline.prepend(QPoint(coorX, coorY));
00722                 else
00723                         polyline.append(QPoint(coorX, coorY));
00724 
00725                 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
00726 
00727                 // Buscamos un píxel en los vecinos 4 conectados.
00728                 uInt dir;
00729                 int newCoorX, newCoorY;
00730                 for (dir = 0; dir < 4; dir++)
00731                         {
00732                         newCoorX = coorX + coorX4Connect[dir];
00733                         newCoorY = coorY + coorY4Connect[dir];
00734 
00735                         // Check if we are inside the limits in that direction
00736                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00737                                 continue;
00738 
00739                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00740                         if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00741                                 break;
00742                         }
00743 
00744                 if (dir == 4) 
00745                         {
00746                         // Buscamos un píxel en los vecinos 4 conectados diagonalmente.
00747                         uInt dir;
00748                         int newCoorX, newCoorY;
00749                         for (dir = 0; dir < 4; dir++)
00750                                 {
00751                                 newCoorX = coorX + coorX4Diag[dir];
00752                                 newCoorY = coorY + coorY4Diag[dir];
00753         
00754                                 // Check if we are inside the limits in that direction
00755                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00756                                         continue;
00757         
00758                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00759                                 if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00760                                         break;
00761                                 }
00762                         if (dir == 4) break;
00763 
00764                         coorX = newCoorX;
00765                         coorY = newCoorY;
00766                         lastDir = (dir+2)%4;
00767                         }
00768                 else    {
00769                         coorX = newCoorX;
00770                         coorY = newCoorY;
00771                         lastDir = (dir+2)%4;
00772                         }
00773                 }
00774 
00775         return polyline;
00776         }
00777 
00778 QList<QVPolyline> getLineContoursThreshold8Connectivity(const QVImage<uChar> &image, const uChar threshold)
00779         {
00780         const uInt cols = image.getCols(), rows = image.getRows();
00781         QVImage<uChar> clone = image;
00782 
00783         QList<QVPolyline> polylineList;
00784 
00785         // Transverse the image
00786         for(uInt col = 0; col < cols; col++)
00787                 for(uInt row = 0; row < rows; row++)
00788                         {
00789                         QVIMAGE_INIT_READ(uChar, clone);
00790                         // If we don't have an active pixel, continue
00791                         if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
00792                                 continue;
00793 
00794                         // Else, we compose the contour following two active neighbour pixels:
00795                         QVPolyline polyline;
00796 
00797                         // We follow first active neighbour pixel, composing the list of pixels in direct order
00798                         getLineContourThreshold8Connectivity(clone, QPoint(col, row), polyline, threshold, false);
00799 
00800                         // Find another neighbour close to the pixel, in 4 connected neighbours
00801                         uInt dir;                       
00802                         int     newCoorX, newCoorY;
00803                         for (dir = 0; dir < 4; dir++)
00804                                 {
00805                                 newCoorX = col + coorX4Connect[dir];
00806                                 newCoorY = row + coorY4Connect[dir];
00807         
00808                                 // Check if we are inside the limits in that direction
00809                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00810                                         continue;
00811         
00812                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00813                                 if ( (clone(newCoorX, newCoorY) >= threshold) )
00814                                         break;
00815                                 }
00816 
00817                         // If we found it, add the contour in reverse order.
00818                         if (dir != 4)
00819                                 getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00820                         else    {
00821                                 // Find another neighbour close to the pixel, in diagonal connected neighbours
00822                                 uInt dir;                       
00823                                 int     newCoorX, newCoorY;
00824                                 for (dir = 0; dir < 4; dir++)
00825                                         {
00826                                         newCoorX = col + coorX4Diag[dir];
00827                                         newCoorY = row + coorY4Diag[dir];
00828                 
00829                                         // Check if we are inside the limits in that direction
00830                                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
00831                                                 continue;
00832                 
00833                                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00834                                         if ( (clone(newCoorX, newCoorY) >= threshold) )
00835                                                 break;
00836                                         }
00837         
00838                                 // If we found it, add the contour in reverse order.
00839                                 if (dir != 4)
00840                                         getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00841                                 }
00842 
00843                         // Finally add the polyline to the list.
00844                         polylineList.append(polyline);
00845                         }
00846 
00847         return polylineList;
00848         }



QVision framework. PARP research group, copyright 2007, 2008.