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

Generated on Wed Jan 16 18:41:28 2008 for QVision by  doxygen 1.5.3