PARP Research Group University of Murcia, Spain


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



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