PARP Research Group University of Murcia, Spain


src/qvip/qvpolylinef.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008. 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 <QVPolyline>
00026 #include <QVPolylineF>
00027 
00028 #include <qvdefines.h>
00029 #include <qvmatrixalgebra.h>
00030 
00031 #include <iostream>
00032 #include <float.h>
00033 
00034 // Iterative point elimination:
00035 #ifndef DOXYGEN_IGNORE_THIS
00036 class ClassAuxIPE_F
00037         {
00038         public:
00039                 double cost;
00040                 int index;
00041                 ClassAuxIPE_F *prev,*next;
00042         };
00043 
00044 bool costLessThan(ClassAuxIPE_F* &s1,ClassAuxIPE_F* &s2)
00045         {
00046         return s1->cost < s2->cost;
00047         }
00048 
00049 bool indexLessThan(ClassAuxIPE_F* &s1,ClassAuxIPE_F* &s2)
00050         {
00051         return s1->index < s2->index;
00052         }
00053 
00054 inline double costElimination(const QVPolylineF &polyline,int ia, int ib, int ic)
00055         {
00056         double   xA,yA,xB,yB,xC,yC;
00057         xA = polyline[ia].x(); yA=polyline[ia].y();
00058         xB = polyline[ib].x(); yB=polyline[ib].y();
00059         xC = polyline[ic].x(); yC=polyline[ic].y();
00060         if((xA != xC) or (yA != yC))
00061                 return ABS(xA*(yC-yB) + xB*(yA-yC) + xC*(yB-yA)) / sqrt((xA-xC)*(xA-xC)+(yA-yC)*(yA-yC));
00062         else
00063                 return sqrt((xB-xC)*(xB-xC)+(yB-yC)*(yB-yC));
00064         }
00065 
00066 class auxLine_F {
00067         public:
00068         auxLine_F(double l1,double l2,double l3,bool ok) : l1(l1),l2(l2),l3(l3),ok(ok) {};
00069         double l1,l2,l3;
00070         bool ok;
00071 };
00072 #endif
00073 
00074 double IterativePointElimination(const QVPolylineF &polyline, QVPolylineF &result,
00075         const double param, bool maxNumberOfPointsMethod,bool intersectLines,
00076         double *max_removed_cost)
00077         {
00078         const uInt tot_siz = polyline.size();
00079         QList<ClassAuxIPE_F*> list;
00080 
00081         // We start with an empty list:
00082         result.clear();
00083 
00084         // Maximum removed cost initialized to zero:
00085         if(max_removed_cost != NULL) *max_removed_cost = 0.0;
00086 
00087         // Only for polylines with 3 points or more; otherwise, the same
00088         // input polyline is returned:
00089         if(polyline.size()<3)
00090                 {
00091                 result = polyline;
00092                 return FLT_MAX;
00093                 }
00094 
00095         // Initialization of main data structure:
00096         for(uInt i=0;i<tot_siz;i++)
00097                 list.push_back(new ClassAuxIPE_F);
00098 
00099         for(uInt i=0;i<tot_siz;i++)
00100                 {
00101                 int ia = (i==0)?tot_siz-1:i-1, ib = i, ic = (i==tot_siz-1)?0:i+1;
00102                 list[i]->cost = costElimination(polyline,ia,ib,ic);
00103                 list[i]->index = ib;
00104                 list[i]->prev = list[ia];
00105                 list[i]->next = list[ic];
00106                 }
00107         if(not polyline.closed) // If not closed, never eliminate end points:
00108                 {
00109                 list[0]->cost = FLT_MAX;
00110                 list[tot_siz-1]->cost = FLT_MAX;
00111                 }
00112         qSort(list.begin(),list.end(),costLessThan);
00113 
00114         // Main loop:
00115         while(TRUE)
00116                 {
00117                 // Stop condition:
00118                 if( (list.size() == 3) or // Minimal size of a polyline.
00119                     ((not maxNumberOfPointsMethod) and (list[0]->cost > param)) or
00120                     ((maxNumberOfPointsMethod) and
00121                      (list.size() <= static_cast<int>(param))) )
00122                         break;
00123 
00124                 // Removal of best point (first in the list): 
00125                 ClassAuxIPE_F *elem = list.takeAt(0),
00126                             *elemPrev = list.takeAt(list.indexOf(elem->prev)),
00127                             *elemNext = list.takeAt(list.indexOf(elem->next));
00128                 elemPrev->next = elem->next;
00129                 elemNext->prev = elem->prev;
00130                 if(elemPrev->cost != FLT_MAX)
00131                         elemPrev->cost = costElimination(polyline,elemPrev->prev->index,
00132                                                                 elemPrev->index,
00133                                                                 elemPrev->next->index);
00134                 if(elemNext->cost != FLT_MAX)
00135                 elemNext->cost = costElimination(polyline,elemNext->prev->index,
00136                                                         elemNext->index,
00137                                                         elemNext->next->index);
00138 
00139                 // Binary (fast) insertion of neighbours in data structure:
00140                 int here;
00141                 for(int i=0;i<2;i++)
00142                         {
00143                         ClassAuxIPE_F* newelem = ((i==0)?elemNext:elemPrev);
00144                         int first=0,last=list.size()-1;
00145                         while (first <= last) {
00146                                 int mid = (first + last) / 2;
00147                                 if (newelem->cost > list[mid]->cost)
00148                                         first = mid + 1;
00149                                 else if (newelem->cost < list[mid]->cost)
00150                                         last = mid - 1;
00151                                 else
00152                                         {
00153                                         here = mid;
00154                                         break;
00155                                         }
00156                         }
00157                         if(first>last)
00158                                 here=first;
00159                         list.insert(here,newelem);
00160 
00161                         }
00162 
00163                 if(max_removed_cost != NULL)
00164                         if(elem->cost > *max_removed_cost)
00165                                 *max_removed_cost = elem->cost;
00166                 delete elem;
00167                 }
00168 
00169         // We will return the cost of the first non deleted point:
00170         double return_value = list.first()->cost;
00171 
00172         // Once IPE finished, sort the list by position in original polyline:
00173         qSort(list.begin(),list.end(),indexLessThan);
00174 
00175         // Now, postprocess, fitting lines:
00176         QList<ClassAuxIPE_F*>::iterator it = list.begin();
00177         if(intersectLines)
00178                 {
00179                 // Line intersection computation (could be subpixel, in fact...):
00180                 double ratio_eig=1.0;
00181                 QList<auxLine_F> lines;
00182                 for(int i=0;i<list.size();i++)
00183                         {
00184                         // If not closed, do not need to compute last line:
00185                         if((not polyline.closed) and (i==list.size()-1))
00186                                 break;
00187                         int i1 = list[i]->index;
00188                         int i2 = list[(i+1)%list.size()]->index;
00189                         if(i2<i1) i2 += tot_siz;
00190                         int dist = i2-i1+1;
00191                         #define MIN_PIXELS_IPE_LINE 15
00192                         if(dist >= MIN_PIXELS_IPE_LINE)
00193                                 {
00194                                 i1 = (i1+dist/5)%tot_siz;
00195                                 i2 = (i2-dist/5)%tot_siz;
00196                                 dist = dist-2*(dist/5);
00197                                 }
00198                         else 
00199                                 {
00200                                 dist = i2-i1+1;
00201                                 i1 = i1%tot_siz;
00202                                 i2 = i2%tot_siz;
00203                                 }
00204         
00205                         double x=0,y=0,xx=0,xy=0,yy=0;
00206                         uInt j=i1;
00207                         do
00208                                 {
00209                                 x += polyline[j].x();
00210                                 y += polyline[j].y();
00211                                 xx += polyline[j].x()*polyline[j].x();
00212                                 xy += polyline[j].x()*polyline[j].y();
00213                                 yy += polyline[j].y()*polyline[j].y();
00214                                 j = (j+1)%tot_siz;
00215                                 } while(j!=(i2+1)%tot_siz);
00216                         double l1,l2,l3;
00217                         x /= dist; y /= dist; xx /= dist; xy /= dist; yy /= dist;
00218                         // If line does not fit well, just put old point instead of intersection:
00219                         ratio_eig = homogLineFromMoments(x,y,xx,xy,yy,l1,l2,l3);
00220                         lines.push_back(auxLine_F(l1,l2,l3,ratio_eig < 0.1));
00221                         }
00222 
00223                 for(int i=0;i<list.size();i++)
00224                         {
00225                         QPointF oldPoint = polyline[list[i]->index];
00226                         if( (not polyline.closed) and ((i==0) or (i==list.size()-1)))
00227                                 {
00228                                 // If not closed, just include end points:
00229                                 result.append(oldPoint);
00230                                 continue;
00231                                 }
00232                         int ant = (i-1+list.size())%list.size();
00233                         int post = (i+1)%list.size();
00234                         double  newx = (lines[i].l2)*(lines[ant].l3) - (lines[i].l3)*(lines[ant].l2);
00235                         double  newy = -(lines[i].l1)*(lines[ant].l3) + (lines[i].l3)*(lines[ant].l1);
00236                         double  newz = (lines[i].l1)*(lines[ant].l2) - (lines[i].l2)*(lines[ant].l1);
00237                         if ( (not lines[i].ok) or (not lines[ant].ok) or // Bad segment
00238                                 (fabs(newz) < EPSILON) ) // Lines too parallel
00239                                 result.append(oldPoint);
00240                         else
00241                                 {
00242                                 int nx = qRound(newx/newz);
00243                                 int ny = qRound(newy/newz);
00244                                 // Only consider intersection if it is inside
00245                                 // a maximum radius circle around the old point
00246                                 // (relative to its nearer previous/next point
00247                                 // in polyline):
00248                                 double dist =
00249                                         sqrt((nx-oldPoint.x())*(nx-oldPoint.x()) +
00250                                              (ny-oldPoint.y())*(ny-oldPoint.y()));
00251                                 QPointF prevPoint = polyline[list[ant]->index],
00252                                         nextPoint = polyline[list[post]->index];
00253                                 double minDist =
00254                                         qMin(
00255                                         sqrt((prevPoint.x()-oldPoint.x())*(prevPoint.x()-oldPoint.x()) +
00256                                         (prevPoint.y()-oldPoint.y())*(prevPoint.y()-oldPoint.y())),
00257                                         sqrt((nextPoint.x()-oldPoint.x())*(nextPoint.x()-oldPoint.x()) +
00258                                         (nextPoint.y()-oldPoint.y())*(nextPoint.y()-oldPoint.y()))
00259                                         );
00260                                 if(dist < 0.2*minDist)
00261                                         result.append(QPoint(nx,ny));
00262                                 else
00263                                         result.append(oldPoint);
00264                                 }
00265                         }
00266                 }
00267         else 
00268                 {
00269                 // No postprocess, simply store the resulting points in result polyline.
00270                 it = list.begin();
00271                 while(it != list.end())
00272                         {
00273                                 result.append(polyline.at((*it)->index));
00274                                 it++;
00275                         }
00276                 }
00277 
00278         // Free memory of remaining structure:
00279         while (!list.isEmpty())
00280                 delete list.takeFirst();
00281 
00282         // Polyline type and direction are the same of the original polyline:
00283         result.closed = polyline.closed;
00284         result.direction = polyline.direction;
00285 
00286         // We return the cost of the first non deleted point:
00287         return return_value;
00288         }
00289 
00290 // Draw
00291 QVPolylineF::QVPolylineF(): QList<QPointF>(),
00292         closed(false), direction(false)//, dir(0)
00293         {
00294         qDebug() << "QVPolylineF()";
00295         qDebug() << "QVPolylineF() <~ return";
00296         };
00297 
00298 QVPolylineF::QVPolylineF(const QVPolyline &polyline): QList<QPointF>(),
00299         closed(polyline.closed), direction(polyline.direction)//, dir(polyline.dir)
00300         {
00301         foreach(QPoint point, polyline)
00302                 {
00303                 append(QPointF(point));
00304                 }
00305         qDebug() << "QVPolylineF(const QVPolylineF &)";
00306         qDebug() << "QVPolylineF(const QVPolylineF &) <~ return";
00307         };
00308 
00309 QVPolylineF::QVPolylineF(const QVPolylineF &polyline): QList<QPointF>(polyline),
00310         closed(polyline.closed), direction(polyline.direction)//, dir(polyline.dir)
00311         {
00312         qDebug() << "QVPolylineF(const QVPolylineF &)";
00313         qDebug() << "QVPolylineF(const QVPolylineF &) <~ return";
00314         };
00315 
00316 
00317 // QPointF      linesIntersection(QPointF a, QPointF b, QPointF c, QPointF d)
00318 //      // gets the intersection point for lines ab and cd
00319 //      {
00320 //      double x1 = a.rx(), x2 = b.rx(), x3 = c.rx(), x4 = d.rx();
00321 //      double y1 = a.ry(), y2 = b.ry(), y3 = c.ry(), y4 = d.ry();
00322 // 
00323 //      double denominador = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
00324 //      if (denominador == 0)
00325 //              return QPointF( (int)(b.rx() + c.rx())/2, (int)(b.ry() + c.ry())/2 );
00326 // 
00327 //      double  ua = (x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3),
00328 //              ub = (x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3);
00329 // 
00330 //      QPointF p = QPointF(
00331 //                      (int) (x1 + ua*(x2 - x1) / denominador),
00332 //                      (int) (y1 + ub*(y2 - y1) / denominador)
00333 //                      );
00334 //      return p;
00335 //      }
00336 
00337 
00338 
00340 
00341 QVPolylineF QVPolylineF::ellipse(uInt n, float x, float y, float maxRadio, float minRadio, float ang)
00342         {
00343         QVPolylineF ellipse;
00344         float w = 2*PI/(n-1);
00345         float maxArg = (maxRadio+minRadio)/2;
00346         float minArg = (maxRadio-minRadio)/2;
00347 
00348         for (uInt t = 0; t < n; t++)
00349                 ellipse.append(QPointF (        (uInt) (x + maxArg*cos(ang+w*t) + minArg*cos(ang-w*t)),
00350                                                 (uInt) (y + maxArg*sin(ang+w*t) + minArg*sin(ang-w*t))
00351                                                 ));
00352         return ellipse;
00353         }
00354 
00355 QVPolylineF QVPolylineF::rectangle(float x1, float y1, float x2, float y2)
00356         {
00357         QVPolylineF rectangle;
00358         rectangle.append(QPointF( x1, y1 ));
00359         rectangle.append(QPointF( x1, y2 ));
00360         rectangle.append(QPointF( x2, y2 ));
00361         rectangle.append(QPointF( x2, y1 ));
00362         rectangle.append(QPointF( x1, y1 ));
00363         return rectangle;
00364         }
00365 
00366 QVPolylineF::operator QVPolyline() const
00367         {
00368         QVPolyline polyline;
00369         foreach(QPointF pointf, *this)
00370                 {
00371                 polyline.append( QPoint(pointf.toPoint()) );
00372                 }
00373         return polyline;
00374         }
00375 



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