src/qvdta/qvcontour.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007. 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 <qvdta/qvcontour.h>
00026 
00027 // Direction-number             Y
00028 //      NE-7    N-0     NW-1    |
00029 //      E-6     *       W-2     v
00030 //      SE-5    S-4     SW-3
00031 // X -->
00032 //                                              N       NO      O       SO      S       SE      E       NE
00034 const char      coorX8Connect[8] =      {       0,      1,      1,      1,      0,      -1,     -1,     -1      };
00035 const char      coorY8Connect[8] =      {       -1,     -1,     0,      1,      1,      1,      0,      -1      };
00036 const char      coorX4Connect[4] =      {       0,              1,              0,              -1,             };
00037 const char      coorY4Connect[4] =      {       -1,             0,              1,              0,              };
00038 const char      coorX4Diag[8] =         {               1,              1,              -1,             -1      };
00039 const char      coorY4Diag[8] =         {               -1,             1,              1,              -1      };
00041 
00042 // 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.
00044 QVPolyline getConnectedSetBorderContourThresholdFromBorderPoint(const QVImage<uChar> &image, const int startPointX, const int startPointY, const uChar threshold)
00045         {
00046         QVPolyline lista;
00047 
00048         lista.closed = true;
00049         lista.append(QPoint(startPointX, startPointY));
00050 
00051         QVIMAGE_INIT_READ(uChar,image);
00052         QRect roi = image.getROI();
00053 
00054         Q_ASSERT_X(roi.contains(startPointX, startPointY), "getContourThresholdFromBorderPoint", "start point out of image ROI");
00055         Q_ASSERT_X(QVIMAGE_PIXEL(image, startPointX, startPointY, 0) >= threshold, "getContourThresholdFromBorderPoint", "start point is not contained in a connected set");
00056 
00057         // We check this is not an interior pixel, neither a solitary one.
00058         // Also we look for a neighbour pixel not belonging to any connected set.
00059         uChar searchDir = 128, numOuterPixels = 0;
00060         for (int i = 0; i<8; i++)
00061                 {
00062                 int x =  startPointX +coorX8Connect[i], y =  startPointY +coorY8Connect[i];
00063                 if (!roi.contains(x, y))
00064                         {
00065                         numOuterPixels++;
00066                         searchDir = i;
00067                         }
00068                 else if (QVIMAGE_PIXEL(image, x, y,0) < threshold)
00069                         {
00070                         numOuterPixels++;
00071                         searchDir = i;
00072                         }
00073                 }
00074 
00075         // Case we receive an interior pixel, raise assert.
00076         Q_ASSERT_X(searchDir < 8, "getContourThresholdFromBorderPoint", "start point is inside the set, not in the border");
00077 
00078         // Case we have a solitary pixel, we return that pixel.
00079         if (numOuterPixels == 8)
00080                 return lista;
00081 
00082         // We insert each point of the border contour, inserting it to the point list.
00083         int sumSearchDir = 0, actualPointX = startPointX, actualPointY = startPointY;
00084         while (true)
00085                 {
00086                 // We search for the next point belonging to the contour.
00087                 uChar d;
00088                 int     nextPointX, nextPointY;
00089                 for (d = 0; d < 8; d++)
00090                         {
00091                         searchDir = (searchDir+1)%8;
00092                         nextPointX = actualPointX + coorX8Connect[searchDir];
00093                         nextPointY = actualPointY + coorY8Connect[searchDir];
00094                         if (roi.contains(nextPointX, nextPointY))
00095                                 if ( (QVIMAGE_PIXEL(image, nextPointX, nextPointY,0) >= threshold) )
00096                                         break;
00097                         }
00098 
00099                 sumSearchDir += d - 3;
00100 
00101                 actualPointX = nextPointX;
00102                 actualPointY = nextPointY;
00103 
00104                 if ( QVIMAGE_PIXEL(image, actualPointX, actualPointY,0) < threshold )
00105                         break;
00106 
00107                 if ( startPointX == actualPointX && startPointY == actualPointY)
00108                         break;
00109 
00110                 lista.append(QPoint(actualPointX, actualPointY));
00111                 searchDir = searchDir + 4;
00112                 }
00113 
00114         lista.direction = (sumSearchDir >= 0);
00115         return lista;
00116         }
00118 
00119 QVPolyline getConnectedSetBorderContourThreshold(const QVImage<uChar> &image, const QPoint startPoint, const uChar threshold)
00120         {
00121         QVIMAGE_INIT_READ(uChar,image);
00122         const QRect roi = image.getROI();
00123 
00124         int col = startPoint.x(), row = startPoint.y();
00125 
00126         if (QVIMAGE_PIXEL(image, col, row,0) < threshold)
00127                 return QVPolyline();
00128 
00129         while (roi.contains(col+1, row))
00130                 {
00131                 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
00132                         break;
00133                 col++;
00134                 }
00135 
00136         return getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00137         }
00138 
00139 QList<QVPolyline> getConnectedSetBorderContoursThreshold(const QVImage <uChar> &image, const uChar threshold)
00140         {
00141         qDebug() << "getPolylinesThreshold()";
00142         QVImage<uChar> mask(image.getCols()+1, image.getRows()+1);
00143         Set(mask,0);
00144 
00145         QVIMAGE_INIT_READ(uChar,image);
00146         QVIMAGE_INIT_WRITE(uChar,mask);
00147 
00148         QRect roi = image.getROI();
00149 
00150         QList<QVPolyline> polylineList;
00151 
00152         // We look for pixels contained in a connected set (gray-level value >= threshold) in the image
00153         for (int row = roi.y(); row < roi.y() + roi.height(); row++)
00154                 for (int col = roi.x(); col < roi.y() + roi.width(); col++)
00155                         {
00156                         // If we find any pixel like that, we can be sure (because the search we did) it belongs to it's border.
00157                         if (QVIMAGE_PIXEL(image, col, row,0) >= threshold)
00158                                 {
00159                                 // if pixel is not marked, we get it's contour
00160                                 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
00161                                         {
00162                                         QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00163                                         draw(mask, lista, true, true);
00164                                         polylineList.append(lista);
00165                                         }
00166 
00167                                 // We ensure next pixel we process will not belong to a connected set.
00168                                 while (roi.contains(col+1, row))
00169                                         {
00170                                         if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
00171                                                 break;
00172                                         col++;
00173                                         }
00174 
00175                                 // This is for the case in which we find an internal contour, that has not been processed and marked.
00176                                 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
00177                                         {
00178                                         QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
00179                                         draw(mask, lista, true, true);
00180                                         polylineList.append(lista);
00181                                         }
00182                                 }
00183 
00184                         }
00185         qDebug() << "getPolylinesThreshold():"<< polylineList.size() << "contours obtained";
00186         qDebug() << "getPolylinesThreshold() <~ return";
00187         return polylineList;
00188         }
00189 
00191 
00192 QVPolyline getLineContourThreshold4Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
00193         {
00194         const uInt cols = image.getCols(), rows = image.getRows();
00195         QVIMAGE_INIT_WRITE(uChar, image);
00196 
00197         uInt lastDir = 666, coorX = point.x(), coorY = point.y();
00198 
00199         qDebug() << "\tContour: new contour";
00200 
00201         while(true)
00202                 {
00203                 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
00204                 if (reverse)
00205                         polyline.prepend(QPoint(coorX, coorY));
00206                 else
00207                         polyline.append(QPoint(coorX, coorY));
00208 
00209                 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
00210 
00211                 uInt dir;
00212                 int newCoorX, newCoorY;
00213                 for (dir = 0; dir < 4; dir++)
00214                         {
00215                         newCoorX = coorX + coorX4Connect[dir];
00216                         newCoorY = coorY + coorY4Connect[dir];
00217 
00218                         // Check if we are inside the limits in that direction
00219                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00220                                 continue;
00221 
00222                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00223                         if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00224                                 break;
00225                         }
00226 
00227                 if (dir == 4) break;
00228 
00229                 coorX = newCoorX;
00230                 coorY = newCoorY;
00231                 lastDir = (dir+2)%4;
00232                 }
00233 
00234         return polyline;
00235         }
00236 
00237 QList<QVPolyline> getLineContoursThreshold4Connectivity(const QVImage<uChar> &image, const uChar threshold)
00238         {
00239         const uInt cols = image.getCols(), rows = image.getRows();
00240         QVImage<uChar> clone = image;
00241 
00242         QList<QVPolyline> polylineList;
00243 
00244         // Transverse the image
00245         for(uInt col = 0; col < cols; col++)
00246                 for(uInt row = 0; row < rows; row++)
00247                         {
00248                         QVIMAGE_INIT_READ(uChar, clone);
00249                         // If we don't have an active pixel, continue
00250                         if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
00251                                 continue;
00252 
00253                         // Else, we compose the contour following two active neighbour pixels:
00254                         QVPolyline polyline;
00255 
00256                         // We follow first active neighbour pixel, composing the list of pixels in direct order
00257                         getLineContourThreshold4Connectivity(clone, QPoint(col, row), polyline, threshold, false);
00258 
00259                         // Find another neighbour close to the pixel.
00260                         uInt dir;                       
00261                         int     newCoorX, newCoorY;
00262                         for (dir = 0; dir < 4; dir++)
00263                                 {
00264                                 newCoorX = col + coorX4Connect[dir];
00265                                 newCoorY = row + coorY4Connect[dir];
00266         
00267                                 // Check if we are inside the limits in that direction
00268                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00269                                         continue;
00270         
00271                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00272                                 if ( (clone(newCoorX, newCoorY) >= threshold) )
00273                                         break;
00274                                 }
00275 
00276                         // If we found it, add the contour in reverse order.
00277                         if (dir != 4)
00278                                 getLineContourThreshold4Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00279 
00280                         // Finally add the polyline to the list.
00281                         polylineList.append(polyline);
00282                         }
00283 
00284         return polylineList;
00285         }
00286 
00288 // Replicated functions from 4-connected version.
00289 QVPolyline getLineContourThreshold8Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
00290         {
00291         const uInt cols = image.getCols(), rows = image.getRows();
00292         QVIMAGE_INIT_WRITE(uChar, image);
00293 
00294         uInt lastDir = 666, coorX = point.x(), coorY = point.y();
00295 
00296         qDebug() << "\tContour: new contour";
00297 
00298         bool continueCond = true;
00299         while(continueCond)
00300                 {
00301                 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
00302                 if (reverse)
00303                         polyline.prepend(QPoint(coorX, coorY));
00304                 else
00305                         polyline.append(QPoint(coorX, coorY));
00306 
00307                 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
00308 
00309                 // Buscamos un píxel en los vecinos 4 conectados.
00310                 uInt dir;
00311                 int newCoorX, newCoorY;
00312                 for (dir = 0; dir < 4; dir++)
00313                         {
00314                         newCoorX = coorX + coorX4Connect[dir];
00315                         newCoorY = coorY + coorY4Connect[dir];
00316 
00317                         // Check if we are inside the limits in that direction
00318                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00319                                 continue;
00320 
00321                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00322                         if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00323                                 break;
00324                         }
00325 
00326                 if (dir == 4) 
00327                         {
00328                         // Buscamos un píxel en los vecinos 4 conectados diagonalmente.
00329                         uInt dir;
00330                         int newCoorX, newCoorY;
00331                         for (dir = 0; dir < 4; dir++)
00332                                 {
00333                                 newCoorX = coorX + coorX4Diag[dir];
00334                                 newCoorY = coorY + coorY4Diag[dir];
00335         
00336                                 // Check if we are inside the limits in that direction
00337                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00338                                         continue;
00339         
00340                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00341                                 if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold)  && (lastDir != dir) )
00342                                         break;
00343                                 }
00344                         if (dir == 4) break;
00345 
00346                         coorX = newCoorX;
00347                         coorY = newCoorY;
00348                         lastDir = (dir+2)%4;
00349                         }
00350                 else    {
00351                         coorX = newCoorX;
00352                         coorY = newCoorY;
00353                         lastDir = (dir+2)%4;
00354                         }
00355                 }
00356 
00357         return polyline;
00358         }
00359 
00360 QList<QVPolyline> getLineContoursThreshold8Connectivity(const QVImage<uChar> &image, const uChar threshold)
00361         {
00362         const uInt cols = image.getCols(), rows = image.getRows();
00363         QVImage<uChar> clone = image;
00364 
00365         QList<QVPolyline> polylineList;
00366 
00367         // Transverse the image
00368         for(uInt col = 0; col < cols; col++)
00369                 for(uInt row = 0; row < rows; row++)
00370                         {
00371                         QVIMAGE_INIT_READ(uChar, clone);
00372                         // If we don't have an active pixel, continue
00373                         if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
00374                                 continue;
00375 
00376                         // Else, we compose the contour following two active neighbour pixels:
00377                         QVPolyline polyline;
00378 
00379                         // We follow first active neighbour pixel, composing the list of pixels in direct order
00380                         getLineContourThreshold8Connectivity(clone, QPoint(col, row), polyline, threshold, false);
00381 
00382                         // Find another neighbour close to the pixel, in 4 connected neighbours
00383                         uInt dir;                       
00384                         int     newCoorX, newCoorY;
00385                         for (dir = 0; dir < 4; dir++)
00386                                 {
00387                                 newCoorX = col + coorX4Connect[dir];
00388                                 newCoorY = row + coorY4Connect[dir];
00389         
00390                                 // Check if we are inside the limits in that direction
00391                                 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00392                                         continue;
00393         
00394                                 // Check if it is a valid direction and if the pixel in that direction is part of a contour
00395                                 if ( (clone(newCoorX, newCoorY) >= threshold) )
00396                                         break;
00397                                 }
00398 
00399                         // If we found it, add the contour in reverse order.
00400                         if (dir != 4)
00401                                 getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00402                         else    {
00403                                 // Find another neighbour close to the pixel, in diagonal connected neighbours
00404                                 uInt dir;                       
00405                                 int     newCoorX, newCoorY;
00406                                 for (dir = 0; dir < 4; dir++)
00407                                         {
00408                                         newCoorX = col + coorX4Diag[dir];
00409                                         newCoorY = row + coorY4Diag[dir];
00410                 
00411                                         // Check if we are inside the limits in that direction
00412                                         if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= cols) || (newCoorY >= rows) )
00413                                                 continue;
00414                 
00415                                         // Check if it is a valid direction and if the pixel in that direction is part of a contour
00416                                         if ( (clone(newCoorX, newCoorY) >= threshold) )
00417                                                 break;
00418                                         }
00419         
00420                                 // If we found it, add the contour in reverse order.
00421                                 if (dir != 4)
00422                                         getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
00423                                 }
00424 
00425                         // Finally add the polyline to the list.
00426                         polylineList.append(polyline);
00427                         }
00428 
00429         return polylineList;
00430         }
00431 

Generated on Thu Mar 13 19:18:16 2008 for QVision by  doxygen 1.5.3