00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00024
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <float.h>
00028 #include <iostream>
00029
00030 #include <qvip.h>
00031 #include <qvmath.h>
00032 #include <qvdefines.h>
00033 #include <qvmatrixalgebra.h>
00034 #include <QVPolyline>
00035 #include <QVPolylineF>
00036 #include <QList>
00037
00038 #include<qvip/fast-C-src-2.1/fast.h>
00039
00040 #ifdef QVIPP
00041 #include <qvipp.h>
00042 #endif
00043
00044 #ifndef QVIPP
00045 QVector<int> HistogramRange(const QVImage<uChar, 1> &src)
00046 {
00047 QVector< int > result(256);
00048
00049 QVIMAGE_INIT_READ(uChar,src);
00050 for(uInt row = 0; row < src.getRows(); row++)
00051 for(uInt col = 0; col < src.getCols(); col++)
00052 result[QVIMAGE_PIXEL(src, col, row,0)]++;
00053
00054 return result;
00055 }
00056 #endif
00057
00058 QVector< QVector< QPoint > > CountingSort(const QVImage<uChar, 1> &image)
00059 {
00060 QVector< QVector <QPoint> > result(256);
00061 const QVector<int> histogram = HistogramRange(image);
00062
00063 for (int k=0; k<256; k++)
00064 result[k].reserve(histogram[k]);
00065
00066 QVIMAGE_INIT_READ(uChar,image);
00067 for(uInt row = 0; row < image.getRows(); row++)
00068 for(uInt col = 0; col < image.getCols(); col++)
00069 result[QVIMAGE_PIXEL(image, col, row,0)].append(QPoint(col, row));
00070
00071 return result;
00072 }
00073
00074 QList<QPointF> getLocalExtremaPixels(const QVImage<uChar, 1> responseImg, const int threshold)
00075 {
00076 const uChar *imgData = responseImg.getReadData();
00077 const int imgStep = responseImg.getStep();
00078 const int minX = responseImg.getROI().x(),
00079 maxX = minX + responseImg.getROI().width(),
00080 minY = responseImg.getROI().y(),
00081 maxY = minY + responseImg.getROI().height();
00082
00083 QList<QPointF> pointsHi;
00084 const int imgStepX2 = imgStep*2;
00085 for(int i = minY + 2; i < maxY-2; i++)
00086 {
00087 const uChar *rowData = imgData + i*imgStep;
00088
00089 for(int j = minX + 2; j < maxX-2; j++)
00090 if (rowData[j] > threshold)
00091 {
00092 const uChar value = rowData[j];
00093
00094 if (value <= rowData[j-1])
00095 continue;
00096 if (value <= rowData[j+1])
00097 continue;
00098 if (value <= rowData[j+imgStep])
00099 continue;
00100 if (value <= rowData[j-imgStep])
00101 continue;
00102 if (value <= rowData[j-1+imgStep])
00103 continue;
00104 if (value <= rowData[j+1+imgStep])
00105 continue;
00106 if (value <= rowData[j-1-imgStep])
00107 continue;
00108 if (value <= rowData[j+1-imgStep])
00109 continue;
00110
00111
00112 if (value <= rowData[j-2])
00113 continue;
00114 if (value <= rowData[j+2])
00115 continue;
00116 if (value <= rowData[j+imgStepX2])
00117 continue;
00118 if (value <= rowData[j-imgStepX2])
00119 continue;
00120 if (value <= rowData[j-2+imgStepX2])
00121 continue;
00122 if (value <= rowData[j+2+imgStepX2])
00123 continue;
00124 if (value <= rowData[j-2-imgStepX2])
00125 continue;
00126 if (value <= rowData[j+2-imgStepX2])
00127 continue;
00128
00129
00130 if (value <= rowData[j-2+imgStep])
00131 continue;
00132 if (value <= rowData[j-2-imgStep])
00133 continue;
00134 if (value <= rowData[j+2+imgStep])
00135 continue;
00136 if (value <= rowData[j+2-imgStep])
00137 continue;
00138 if (value <= rowData[j+1+imgStepX2])
00139 continue;
00140 if (value <= rowData[j-1+imgStepX2])
00141 continue;
00142 if (value <= rowData[j+1+imgStepX2])
00143 continue;
00144 if (value <= rowData[j-1+imgStepX2])
00145 continue;
00146
00147 pointsHi << QPointF(j+2,i+2);
00148 }
00149 }
00150 return pointsHi;
00151 }
00152
00153 double ShiTomasiScore( const uChar *imagePtr, const int imageStep,
00154 const int x, const int y,
00155 const int nHalfBoxSize)
00156 {
00157 float dXX = 0.0, dYY = 0.0, dXY = 0.0;
00158
00159 for(int ir_y = y - nHalfBoxSize; ir_y <= y + nHalfBoxSize; ir_y++)
00160 for(int ir_x = x - nHalfBoxSize; ir_x <= x + nHalfBoxSize; ir_x++)
00161 {
00162 int imageIndex = ir_x + ir_y * imageStep;
00163 float dx = imagePtr[imageIndex +1] - imagePtr[imageIndex-1],
00164 dy = imagePtr[imageIndex + imageStep] - imagePtr[imageIndex - imageStep];
00165
00166 dXX += dx*dx;
00167 dYY += dy*dy;
00168 dXY += dx*dy;
00169 }
00170
00171 int nPixels = 2.0 * POW2(2*nHalfBoxSize+1);
00172 dXX = dXX / nPixels;
00173 dYY = dYY / nPixels;
00174 dXY = dXY / nPixels;
00175
00176
00177 return 0.5 * (dXX + dYY - sqrt( (dXX + dYY) * (dXX + dYY) - 4.0 * (dXX * dYY - dXY * dXY) ));
00178 };
00179
00180 QMap<double, QPointF> pointsByShiTomasiValue(const QVImage<uChar, 1> & image, const QList<QPointF> &points, const int shiTomasiRadius)
00181 {
00182 const int imgCols = image.getCols(),
00183 imgRows = image.getRows(),
00184 maxCol = imgCols - shiTomasiRadius -1,
00185 maxRows = imgRows - shiTomasiRadius - 1;
00186
00187 const int imageStep = image.getStep();
00188 const uChar *imagePtr = image.getReadData();
00189
00190
00191 QMap<double, QPointF> pointsMap;
00192 foreach(QPointF point, points)
00193 {
00194 const double x = point.x(), y = point.y();
00195 if ( x > shiTomasiRadius and x < maxCol and y > shiTomasiRadius and y < maxRows)
00196 pointsMap.insertMulti( -ShiTomasiScore(imagePtr, imageStep, x, y, shiTomasiRadius), point);
00197 }
00198 return pointsMap;
00199 }
00200
00201 #ifdef QVIPP
00202 QList<QPointF> hiPassPointDetector(const QVImage<uChar, 1> &image, const int threshold)
00203 {
00204 QVImage<uChar, 1> gauss3x3;
00205 FilterGauss(image, gauss3x3, ippMskSize3x3, image.getROI().topLeft() + QPoint(1,1));
00206
00207 QVImage<uChar, 1> hiPass;
00208 FilterHipass(gauss3x3, hiPass, ippMskSize3x3, gauss3x3.getROI().topLeft() + QPoint(1,1));
00209
00210 return getLocalExtremaPixels(hiPass, threshold);
00211 }
00212
00213 QList<QPointF> DoGPointDetector(const QVImage<uChar, 1> &image, const int threshold)
00214 {
00215 QVImage<uChar, 1> gauss3x3;
00216 FilterGauss(image, gauss3x3, ippMskSize3x3, image.getROI().topLeft() + QPoint(1,1));
00217
00218 QVImage<uChar, 1> gauss5x5;
00219 FilterGauss(gauss3x3, gauss5x5, ippMskSize3x3, gauss3x3.getROI().topLeft() + QPoint(1,1));
00220
00221 gauss3x3.setROI(gauss5x5.getROI());
00222
00223 QVImage<uChar, 1> absDiff;
00224 AbsDiff(gauss3x3, gauss5x5, absDiff, gauss5x5.getROI().topLeft());
00225
00226 return getLocalExtremaPixels(absDiff, threshold);
00227 }
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 void FilterHarrisCornerResponseImage(const QVImage<uChar> &image, QVImage<sFloat> &result, int aperture, int avgwindow, const QPoint &)
00256 {
00257 QVImage<uChar> buffer;
00258 MinEigenValGetBufferSize(image, buffer);
00259
00260 MinEigenVal(image, result, ippKernelSobel, aperture, avgwindow, buffer);
00261 }
00262
00263 void FilterDoG(const QVImage<uChar> &image, QVImage<uChar> &result)
00264 {
00265 const uInt rows = image.getRows(), cols = image.getCols();
00266 QVImage<uChar> gauss3x3(cols, rows), gauss5x5(cols, rows);
00267
00268
00269 FilterGauss(image, gauss3x3, ippMskSize3x3, QPoint(1,1));
00270 FilterGauss(image, gauss5x5, ippMskSize5x5, QPoint(2,2));
00271
00272 gauss3x3.setROI(gauss5x5.getROI());
00273
00274 AbsDiff(gauss3x3, gauss5x5, result);
00275 }
00276
00277 void SobelCornerResponseImage(const QVImage<sFloat> &image, QVImage<sFloat> &result)
00278 {
00279 std::cerr << "WARNING: SobelCornerResponseImage is deprecated. Use FilterHessianCornerResponseImage instead." << std::endl;
00280 FilterHessianCornerResponseImage(image, result);
00281 }
00282
00283 void FilterHessianCornerResponseImage(const QVImage<sFloat> &image, QVImage<sFloat> &result, const QPoint &destROIOffset)
00284 {
00285 QVImage<sFloat> Gx, Gy, Gxx, Gxy, Gyy, GxyGxy, GxxGyy;
00286
00287 FilterSobelHorizMask(image,Gx, ippMskSize3x3);
00288 FilterSobelVertMask(image,Gy, ippMskSize3x3);
00289
00290 FilterSobelHorizMask(Gx,Gxx, ippMskSize3x3, QPoint(2,2));
00291 FilterSobelVertMask(Gy,Gyy, ippMskSize3x3, QPoint(2,2));
00292 FilterSobelVertMask(Gx,Gxy, ippMskSize3x3, QPoint(2,2));
00293
00294 Gxx.setROI(Gxy.getROI());
00295 Gyy.setROI(Gxy.getROI());
00296
00297 Mul(Gxy, Gxy, GxyGxy);
00298 Mul(Gxx, Gyy, GxxGyy);
00299 AbsDiff(GxyGxy, GxxGyy, result, destROIOffset);
00300 }
00301
00302 #ifdef GSL_AVAILABLE
00303 void FilterSeparable(const QVImage<sFloat, 1> &image, QVImage<sFloat, 1> &dest,
00304 const QVVector &rowFilter, const QVVector &colFilter, const QPoint &destROIOffset)
00305 {
00306 const uInt cols = image.getCols(), rows = image.getRows();
00307 QVImage<sFloat> rowFiltered(cols, rows);
00308 FilterRow(image, rowFiltered, rowFilter);
00309 FilterColumn(rowFiltered, dest, colFilter, destROIOffset);
00310 }
00311 #endif // GSL_AVAILABLE
00312
00313 QMap<sFloat, QPointF> fastMaximalPoints(const QVImage<sFloat> &image, const double threshold, const int windowRadius)
00314 {
00315 QVImage<sFloat> maxImage;
00316 FilterMax(image, maxImage, QSize(2*windowRadius+1, 2*windowRadius+1), QPoint(0,0), image.getROI().topLeft() + QPoint(windowRadius, windowRadius));
00317
00318 const QRect ROI = maxImage.getROI();
00319 const int maxStep = maxImage.getStep() / sizeof(sFloat),
00320 imageStep = image.getStep() / sizeof(sFloat);
00321
00322 QMap<sFloat, QPointF> sortedPoints;
00323
00324 sFloat *actualPtr = (sFloat *) image.getReadData() + (imageStep + 1) * windowRadius;
00325 sFloat *maxPtr = (sFloat *) maxImage.getReadData() + (maxStep + 1) * windowRadius;
00326
00327 for(int j = 0; j < ROI.height(); j++, actualPtr += imageStep, maxPtr += maxStep)
00328 for(int i = 0; i < ROI.width(); i++)
00329 if ( (actualPtr[i] >= threshold) and (maxPtr[i] == actualPtr[i]) )
00330 sortedPoints.insertMulti(-actualPtr[i], QPointF(i+ROI.x(), j+ROI.y()));
00331
00332 return sortedPoints;
00333 }
00334
00335 QMap<uChar, QPointF> fastMaximalPoints(const QVImage<uChar> &image, const double threshold, const int windowRadius)
00336 {
00337 QVImage<uChar> maxImage;
00338 FilterMax(image, maxImage, QSize(2*windowRadius+1, 2*windowRadius+1), QPoint(0,0), image.getROI().topLeft() + QPoint(windowRadius, windowRadius));
00339
00340 const QRect ROI = maxImage.getROI();
00341 const int maxStep = maxImage.getStep() / sizeof(uChar),
00342 imageStep = image.getStep() / sizeof(uChar);
00343
00344 QMap<uChar, QPointF> sortedPoints;
00345
00346 uChar *actualPtr = (uChar *) image.getReadData() + (imageStep + 1) * windowRadius;
00347 uChar *maxPtr = (uChar *) maxImage.getReadData() + (maxStep + 1) * windowRadius;
00348
00349 for(int j = 0; j < ROI.height(); j++, actualPtr += imageStep, maxPtr += maxStep)
00350 for(int i = 0; i < ROI.width(); i++)
00351 if ( (actualPtr[i] >= threshold) and (maxPtr[i] == actualPtr[i]) )
00352 sortedPoints.insertMulti(-actualPtr[i], QPointF(i+ROI.x(), j+ROI.y()));
00353
00354 return sortedPoints;
00355 }
00356
00357 #define DEFINE_QVDTA_FUNCTION_NORMALIZE(TYPE, C) \
00358 void FilterNormalize(const QVImage<TYPE,C> &image, QVImage<TYPE,C> &equalized, const QPoint &destROIOffset) \
00359 { \
00360 TYPE maxVal, minVal; \
00361 \
00362 Max(image,maxVal); \
00363 Min(image,minVal); \
00364 \
00365 QVImage<TYPE,C> temp, result; \
00366 SubC(image, minVal, temp); \
00367 MulC(temp, 255/(maxVal-minVal), result, 1, destROIOffset); \
00368 equalized = result; \
00369 }
00370
00371 DEFINE_QVDTA_FUNCTION_NORMALIZE(uChar,1);
00372
00373 #define DEFINE_QVDTA_FUNCTION_NORMALIZE2(TYPE, C) \
00374 void FilterNormalize(const QVImage<TYPE,C> &image, QVImage<TYPE,C> &equalized, const QPoint &destROIOffset) \
00375 { \
00376 uInt rows = image.getRows(), cols = image.getCols(); \
00377 TYPE maxVal, minVal; \
00378 \
00379 Max(image,maxVal); \
00380 Min(image,minVal); \
00381 \
00382 QVImage<TYPE,C> temp(cols, rows), result(cols, rows); \
00383 SubC(image, minVal, temp); \
00384 MulC(temp, 255/(maxVal-minVal), result, destROIOffset); \
00385 equalized = result; \
00386 }
00387 DEFINE_QVDTA_FUNCTION_NORMALIZE2(sFloat,1);
00388 #endif // IPP_AVAILABLE
00389
00390 void FilterLocalMax(const QVImage<sFloat> &src, QVImage<uChar> &dest, uInt colMaskSize, uInt rowMaskSize, sFloat threshold)
00391 {
00392 const int cols = src.getCols(), rows = src.getRows();
00393 Set(0, dest);
00394 sFloat actual;
00395 QVIMAGE_INIT_READ(sFloat,src);
00396 QVIMAGE_INIT_WRITE(uChar,dest);
00397 for(int row = ((int)rowMaskSize); row < rows-((int)rowMaskSize); row++)
00398 for(int col = ((int)colMaskSize); col < cols-((int)colMaskSize); col++)
00399 {
00400 actual = QVIMAGE_PIXEL(src, col, row,0);
00401 if (actual >= threshold)
00402 {
00403 QVIMAGE_PIXEL(dest, col, row, 0) = std::numeric_limits<unsigned char>::max();
00404 for (int j = ((int)row-rowMaskSize); (j < row+((int)rowMaskSize)) && (QVIMAGE_PIXEL(dest, col, row, 0) > 0); j++)
00405 for (int i = ((int)col-colMaskSize); i < col+((int)colMaskSize); i++)
00406 if ( ((i != col) || (j != row)) && (actual <= QVIMAGE_PIXEL(src, i, j, 0)) )
00407 {
00408 QVIMAGE_PIXEL(dest, col, row, 0) = 0;
00409 break;
00410 }
00411 }
00412 }
00413 }
00414
00416 int myFloodFill(QVImage<uChar> &image, uInt x, uInt y, uInt value, uInt minVal, uInt maxVal)
00417 {
00418
00419 Q_ASSERT( (value <= minVal) || (value >= maxVal) );
00420 Q_ASSERT( minVal <= maxVal );
00421
00422
00423 if ( (x >= image.getCols()) || (y >= image.getRows()))
00424 return 0;
00425
00426 if ( (image(x,y) < minVal) || (image(x,y) > maxVal) )
00427 return 0;
00428
00429 image(x,y) = value;
00430
00431 int val = 1;
00432 val += myFloodFill(image, x-1, y, value, minVal, maxVal);
00433 val += myFloodFill(image, x, y-1, value, minVal, maxVal);
00434 val += myFloodFill(image, x+1, y, value, minVal, maxVal);
00435 val += myFloodFill(image, x, y+1, value, minVal, maxVal);
00436
00437 val += myFloodFill(image, x-1, y-1, value, minVal, maxVal);
00438 val += myFloodFill(image, x-1, y+1, value, minVal, maxVal);
00439 val += myFloodFill(image, x+1, y-1, value, minVal, maxVal);
00440 val += myFloodFill(image, x+1, y+1, value, minVal, maxVal);
00441
00442 return val;
00443 }
00444
00446
00447 #include <QVComponentTree>
00448
00449 void pruneLowComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00450 {
00451 Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00452 Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00453 Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00454
00455 bool prune = false;
00456 int lastValidThreshold = validThreshold;
00457
00458
00459
00460 for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00461 if (componentTree.area(node)[threshold] > 0)
00462 {
00463 if (componentTree.area(node)[threshold] < minArea)
00464 prune = true;
00465 else
00466 lastValidThreshold = threshold;
00467 }
00468
00469
00470 if (prune)
00471 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), lastValidThreshold, 0, lastValidThreshold-1);
00472 else
00473 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00474 pruneLowComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00475 }
00476
00477 void pruneHighComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00478 {
00479 Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00480 Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00481 Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00482
00483 bool prune = false;
00484 int lastValidThreshold = validThreshold;
00485
00486
00487
00488 for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00489 if (componentTree.area(node)[threshold] > 0)
00490 {
00491 if (componentTree.area(node)[threshold] < minArea)
00492 prune = true;
00493 else
00494 lastValidThreshold = threshold;
00495 }
00496
00497
00498 if (prune)
00499 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), 255-lastValidThreshold, 255-lastValidThreshold+1, 255-0);
00500 else
00501 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00502 pruneHighComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00503 }
00504
00505 void FilterPruneComponentTreeSmallRegions(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea)
00506 {
00507 qDebug() << "pruneRegions()";
00508 if (componentTree.isInverseTree())
00509 {
00510 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00511 pruneHighComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00512 }
00513 else {
00514 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00515 pruneLowComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00516 }
00517
00518 qDebug() << "pruneRegions() <~ return";
00519 }
00520
00521 #include <qvmath/qvdisjointset.h>
00522
00523
00524 QMap<sFloat, QPointF> maximalPoints(const QVImage<sFloat> &cornerResponseImage, const double threshold, const int windowRadius)
00525 {
00526 const QRect ROI = cornerResponseImage.getROI();
00527 const int step = cornerResponseImage.getStep() / sizeof(sFloat),
00528 windowSize = windowRadius * 2 +1;
00529
00530 QVIMAGE_INIT_READ(sFloat,cornerResponseImage);
00531
00532 QMap<sFloat, QPointF> sortedPoints;
00533 for(int row = ROI.y() + windowRadius; row < ROI.y() + ROI.height() - windowRadius; row++)
00534 for(int col = ROI.x() + windowRadius; col < ROI.x() + ROI.width() - windowRadius; col++)
00535 {
00536 const sFloat actual = QVIMAGE_PIXEL(cornerResponseImage, col, row, 0);
00537 if (actual >= threshold)
00538 {
00539 bool cond = true;
00540
00541 sFloat const * pixel = & QVIMAGE_PIXEL(cornerResponseImage, col, row, 0) - windowRadius * (1 + step);
00542 for (int j = 0; j < windowSize && cond; j++, pixel += step - windowSize )
00543 for (int i = 0; i < windowSize && cond; i++, pixel++)
00544 if ( ( i != windowRadius || j != windowRadius ) && ( actual <= *pixel) )
00545 cond = false;
00546
00547 if (cond)
00548 sortedPoints.insertMulti(-actual, QPointF(col+2, row+2));
00549 }
00550 }
00551
00552 return sortedPoints;
00553 }
00554
00555 QList<QPointF> FASTFeatures(const QVImage<uChar, 1> & image, const int threshold, const FASTDetectionAlgorithm &fastAlgorithm)
00556 {
00557
00558 const int cols = image.getCols(),
00559 rows = image.getRows(),
00560 step = image.getStep();
00561 const byte *imgData = image.getReadData();
00562
00563 int num_corners;
00564 xy *points;
00565
00566 switch(fastAlgorithm)
00567 {
00568 case Fast9:
00569 points = fast9_detect_nonmax(imgData, cols, rows, step, threshold, &num_corners);
00570 break;
00571 case Fast10:
00572 points = fast10_detect_nonmax(imgData, cols, rows, step, threshold, &num_corners);
00573 break;
00574 case Fast11:
00575 points = fast11_detect_nonmax(imgData, cols, rows, step, threshold, &num_corners);
00576 break;
00577 case Fast12:
00578 points = fast12_detect_nonmax(imgData, cols, rows, step, threshold, &num_corners);
00579 break;
00580 }
00581
00582 QList<QPointF> result;
00583 for (int i = 0; i < num_corners; i++)
00584 result << QPointF(points[i].x, points[i].y);
00585
00586 free(points);
00587
00588 return result;
00589 }
00590
00591
00593
00594 #ifndef DOXYGEN_IGNORE_THIS
00595 class ClassAuxIPE
00596 {
00597 public:
00598 double cost;
00599 int index;
00600 ClassAuxIPE *prev,*next;
00601 };
00602
00603 class ClassAuxIPE_F
00604 {
00605 public:
00606 double cost;
00607 int index;
00608 ClassAuxIPE_F *prev,*next;
00609 };
00610
00611 bool costLessThan(ClassAuxIPE* &s1,ClassAuxIPE* &s2)
00612 {
00613 return s1->cost < s2->cost;
00614 }
00615
00616 bool costLessThanF(ClassAuxIPE_F* &s1,ClassAuxIPE_F* &s2)
00617 {
00618 return s1->cost < s2->cost;
00619 }
00620
00621 bool indexLessThan(ClassAuxIPE* &s1,ClassAuxIPE* &s2)
00622 {
00623 return s1->index < s2->index;
00624 }
00625
00626 bool indexLessThanF(ClassAuxIPE_F* &s1,ClassAuxIPE_F* &s2)
00627 {
00628 return s1->index < s2->index;
00629 }
00630
00631 inline double costElimination(const QVPolyline &polyline,int ia, int ib, int ic)
00632 {
00633 double xA,yA,xB,yB,xC,yC;
00634 xA = polyline[ia].x(); yA=polyline[ia].y();
00635 xB = polyline[ib].x(); yB=polyline[ib].y();
00636 xC = polyline[ic].x(); yC=polyline[ic].y();
00637 if((xA != xC) or (yA != yC))
00638 return ABS(xA*(yC-yB) + xB*(yA-yC) + xC*(yB-yA)) / sqrt((xA-xC)*(xA-xC)+(yA-yC)*(yA-yC));
00639 else
00640 return sqrt((xB-xC)*(xB-xC)+(yB-yC)*(yB-yC));
00641 }
00642
00643 inline double costEliminationF(const QVPolylineF &polyline,int ia, int ib, int ic)
00644 {
00645 double xA,yA,xB,yB,xC,yC;
00646 xA = polyline[ia].x(); yA=polyline[ia].y();
00647 xB = polyline[ib].x(); yB=polyline[ib].y();
00648 xC = polyline[ic].x(); yC=polyline[ic].y();
00649 if((xA != xC) or (yA != yC))
00650 return ABS(xA*(yC-yB) + xB*(yA-yC) + xC*(yB-yA)) / sqrt((xA-xC)*(xA-xC)+(yA-yC)*(yA-yC));
00651 else
00652 return sqrt((xB-xC)*(xB-xC)+(yB-yC)*(yB-yC));
00653 }
00654
00655 class auxLine {
00656 public:
00657 auxLine(double l1,double l2,double l3,bool ok) : l1(l1),l2(l2),l3(l3),ok(ok) {};
00658 double l1,l2,l3;
00659 bool ok;
00660 };
00661
00662 class auxLine_F {
00663 public:
00664 auxLine_F(double l1,double l2,double l3,bool ok) : l1(l1),l2(l2),l3(l3),ok(ok) {};
00665 double l1,l2,l3;
00666 bool ok;
00667 };
00668 #endif // DOXYGEN_IGNORE_THIS
00669
00670 #ifdef QVMATRIXALGEBRA_AVAILABLE
00671 double IterativePointElimination(const QVPolyline &polyline, QVPolyline &result,
00672 const double param, bool maxNumberOfPointsMethod,bool intersectLines,
00673 double *max_removed_cost)
00674 {
00675 const uInt tot_siz = polyline.size();
00676 QList<ClassAuxIPE*> list;
00677
00678
00679 result.clear();
00680
00681
00682 if(max_removed_cost != NULL) *max_removed_cost = 0.0;
00683
00684
00685
00686 if(polyline.size()<3)
00687 {
00688 result = polyline;
00689 return FLT_MAX;
00690 }
00691
00692
00693 for(uInt i=0;i<tot_siz;i++)
00694 list.push_back(new ClassAuxIPE);
00695
00696 for(uInt i=0;i<tot_siz;i++)
00697 {
00698 int ia = (i==0)?tot_siz-1:i-1, ib = i, ic = (i==tot_siz-1)?0:i+1;
00699 list[i]->cost = costElimination(polyline,ia,ib,ic);
00700 list[i]->index = ib;
00701 list[i]->prev = list[ia];
00702 list[i]->next = list[ic];
00703 }
00704 if(not polyline.closed)
00705 {
00706 list[0]->cost = FLT_MAX;
00707 list[tot_siz-1]->cost = FLT_MAX;
00708 }
00709 qSort(list.begin(),list.end(), costLessThan);
00710
00711
00712 while(TRUE)
00713 {
00714
00715 if( (list.size() == 3) or
00716 ((not maxNumberOfPointsMethod) and (list[0]->cost > param)) or
00717 ((maxNumberOfPointsMethod) and
00718 (list.size() <= static_cast<int>(param))) )
00719 break;
00720
00721
00722 ClassAuxIPE *elem = list.takeAt(0),
00723 *elemPrev = list.takeAt(list.indexOf(elem->prev)),
00724 *elemNext = list.takeAt(list.indexOf(elem->next));
00725 elemPrev->next = elem->next;
00726 elemNext->prev = elem->prev;
00727 if(elemPrev->cost != FLT_MAX)
00728 elemPrev->cost = costElimination(polyline,elemPrev->prev->index,
00729 elemPrev->index,
00730 elemPrev->next->index);
00731 if(elemNext->cost != FLT_MAX)
00732 elemNext->cost = costElimination(polyline,elemNext->prev->index,
00733 elemNext->index,
00734 elemNext->next->index);
00735
00736
00737 int here;
00738 for(int i=0;i<2;i++)
00739 {
00740 ClassAuxIPE* newelem = ((i==0)?elemNext:elemPrev);
00741 int first=0,last=list.size()-1;
00742 while (first <= last) {
00743 int mid = (first + last) / 2;
00744 if (newelem->cost > list[mid]->cost)
00745 first = mid + 1;
00746 else if (newelem->cost < list[mid]->cost)
00747 last = mid - 1;
00748 else
00749 {
00750 here = mid;
00751 break;
00752 }
00753 }
00754 if(first>last)
00755 here=first;
00756 list.insert(here,newelem);
00757
00758 }
00759
00760 if(max_removed_cost != NULL)
00761 if(elem->cost > *max_removed_cost)
00762 *max_removed_cost = elem->cost;
00763 delete elem;
00764 }
00765
00766
00767 double return_value = list.first()->cost;
00768
00769
00770 qSort(list.begin(),list.end(),indexLessThan);
00771
00772
00773 QList<ClassAuxIPE*>::iterator it = list.begin();
00774 if(intersectLines)
00775 {
00776
00777 double ratio_eig=1.0;
00778 QList<auxLine> lines;
00779 for(int i=0;i<list.size();i++)
00780 {
00781
00782 if((not polyline.closed) and (i==list.size()-1))
00783 break;
00784 int i1 = list[i]->index;
00785 int i2 = list[(i+1)%list.size()]->index;
00786 if(i2<i1) i2 += tot_siz;
00787 int dist = i2-i1+1;
00788 #define MIN_PIXELS_IPE_LINE 15
00789 if(dist >= MIN_PIXELS_IPE_LINE)
00790 {
00791 i1 = (i1+dist/5)%tot_siz;
00792 i2 = (i2-dist/5)%tot_siz;
00793 dist = dist-2*(dist/5);
00794 }
00795 else
00796 {
00797 dist = i2-i1+1;
00798 i1 = i1%tot_siz;
00799 i2 = i2%tot_siz;
00800 }
00801
00802 double x=0,y=0,xx=0,xy=0,yy=0;
00803 uInt j=i1;
00804 do
00805 {
00806 x += polyline[j].x();
00807 y += polyline[j].y();
00808 xx += polyline[j].x()*polyline[j].x();
00809 xy += polyline[j].x()*polyline[j].y();
00810 yy += polyline[j].y()*polyline[j].y();
00811 j = (j+1)%tot_siz;
00812 } while(j!=(i2+1)%tot_siz);
00813 double l1,l2,l3;
00814 x /= dist; y /= dist; xx /= dist; xy /= dist; yy /= dist;
00815
00816
00817 ratio_eig = homogLineFromMoments(x,y,xx,xy,yy,l1,l2,l3);
00818
00819
00820
00821
00822
00823 lines.push_back(auxLine(l1,l2,l3,ratio_eig < 0.1));
00824 }
00825
00826 for(int i=0;i<list.size();i++)
00827 {
00828 QPoint oldPoint = polyline[list[i]->index];
00829 if( (not polyline.closed) and ((i==0) or (i==list.size()-1)))
00830 {
00831
00832 result.append(oldPoint);
00833 continue;
00834 }
00835 int ant = (i-1+list.size())%list.size();
00836 int post = (i+1)%list.size();
00837 double newx = (lines[i].l2)*(lines[ant].l3) - (lines[i].l3)*(lines[ant].l2);
00838 double newy = -(lines[i].l1)*(lines[ant].l3) + (lines[i].l3)*(lines[ant].l1);
00839 double newz = (lines[i].l1)*(lines[ant].l2) - (lines[i].l2)*(lines[ant].l1);
00840 if ( (not lines[i].ok) or (not lines[ant].ok) or
00841 (fabs(newz) < EPSILON) )
00842 result.append(oldPoint);
00843 else
00844 {
00845 int nx = qRound(newx/newz);
00846 int ny = qRound(newy/newz);
00847
00848
00849
00850
00851 double dist =
00852 sqrt((nx-oldPoint.x())*(nx-oldPoint.x()) +
00853 (ny-oldPoint.y())*(ny-oldPoint.y()));
00854 QPoint prevPoint = polyline[list[ant]->index],
00855 nextPoint = polyline[list[post]->index];
00856 double minDist =
00857 qMin(
00858 sqrt((prevPoint.x()-oldPoint.x())*(prevPoint.x()-oldPoint.x()) +
00859 (prevPoint.y()-oldPoint.y())*(prevPoint.y()-oldPoint.y())),
00860 sqrt((nextPoint.x()-oldPoint.x())*(nextPoint.x()-oldPoint.x()) +
00861 (nextPoint.y()-oldPoint.y())*(nextPoint.y()-oldPoint.y()))
00862 );
00863 if(dist < 0.2*minDist)
00864 result.append(QPoint(nx,ny));
00865 else
00866 result.append(oldPoint);
00867 }
00868 }
00869 }
00870 else
00871 {
00872
00873 it = list.begin();
00874 while(it != list.end())
00875 {
00876 result.append(polyline.at((*it)->index));
00877 it++;
00878 }
00879 }
00880
00881
00882 while (!list.isEmpty())
00883 delete list.takeFirst();
00884
00885
00886 result.closed = polyline.closed;
00887 result.direction = polyline.direction;
00888
00889
00890 return return_value;
00891 }
00892
00893 double IterativePointElimination(const QVPolylineF &polyline, QVPolylineF &result,
00894 const double param, bool maxNumberOfPointsMethod,bool intersectLines,
00895 double *max_removed_cost)
00896 {
00897 const uInt tot_siz = polyline.size();
00898 QList<ClassAuxIPE_F*> list;
00899
00900
00901 result.clear();
00902
00903
00904 if(max_removed_cost != NULL) *max_removed_cost = 0.0;
00905
00906
00907
00908 if(polyline.size()<3)
00909 {
00910 result = polyline;
00911 return FLT_MAX;
00912 }
00913
00914
00915 for(uInt i=0;i<tot_siz;i++)
00916 list.push_back(new ClassAuxIPE_F);
00917
00918 for(uInt i=0;i<tot_siz;i++)
00919 {
00920 int ia = (i==0)?tot_siz-1:i-1, ib = i, ic = (i==tot_siz-1)?0:i+1;
00921 list[i]->cost = costEliminationF(polyline,ia,ib,ic);
00922 list[i]->index = ib;
00923 list[i]->prev = list[ia];
00924 list[i]->next = list[ic];
00925 }
00926 if(not polyline.closed)
00927 {
00928 list[0]->cost = FLT_MAX;
00929 list[tot_siz-1]->cost = FLT_MAX;
00930 }
00931 qSort(list.begin(),list.end(),costLessThanF);
00932
00933
00934 while(TRUE)
00935 {
00936
00937 if( (list.size() == 3) or
00938 ((not maxNumberOfPointsMethod) and (list[0]->cost > param)) or
00939 ((maxNumberOfPointsMethod) and
00940 (list.size() <= static_cast<int>(param))) )
00941 break;
00942
00943
00944 ClassAuxIPE_F *elem = list.takeAt(0),
00945 *elemPrev = list.takeAt(list.indexOf(elem->prev)),
00946 *elemNext = list.takeAt(list.indexOf(elem->next));
00947 elemPrev->next = elem->next;
00948 elemNext->prev = elem->prev;
00949 if(elemPrev->cost != FLT_MAX)
00950 elemPrev->cost = costEliminationF(polyline,elemPrev->prev->index,
00951 elemPrev->index,
00952 elemPrev->next->index);
00953 if(elemNext->cost != FLT_MAX)
00954 elemNext->cost = costEliminationF(polyline,elemNext->prev->index,
00955 elemNext->index,
00956 elemNext->next->index);
00957
00958
00959 int here;
00960 for(int i=0;i<2;i++)
00961 {
00962 ClassAuxIPE_F* newelem = ((i==0)?elemNext:elemPrev);
00963 int first=0,last=list.size()-1;
00964 while (first <= last) {
00965 int mid = (first + last) / 2;
00966 if (newelem->cost > list[mid]->cost)
00967 first = mid + 1;
00968 else if (newelem->cost < list[mid]->cost)
00969 last = mid - 1;
00970 else
00971 {
00972 here = mid;
00973 break;
00974 }
00975 }
00976 if(first>last)
00977 here=first;
00978 list.insert(here,newelem);
00979
00980 }
00981
00982 if(max_removed_cost != NULL)
00983 if(elem->cost > *max_removed_cost)
00984 *max_removed_cost = elem->cost;
00985 delete elem;
00986 }
00987
00988
00989 double return_value = list.first()->cost;
00990
00991
00992 qSort(list.begin(),list.end(),indexLessThanF);
00993
00994
00995 QList<ClassAuxIPE_F*>::iterator it = list.begin();
00996 if(intersectLines)
00997 {
00998
00999 double ratio_eig=1.0;
01000 QList<auxLine_F> lines;
01001 for(int i=0;i<list.size();i++)
01002 {
01003
01004 if((not polyline.closed) and (i==list.size()-1))
01005 break;
01006 int i1 = list[i]->index;
01007 int i2 = list[(i+1)%list.size()]->index;
01008 if(i2<i1) i2 += tot_siz;
01009 int dist = i2-i1+1;
01010 #define MIN_PIXELS_IPE_LINE 15
01011 if(dist >= MIN_PIXELS_IPE_LINE)
01012 {
01013 i1 = (i1+dist/5)%tot_siz;
01014 i2 = (i2-dist/5)%tot_siz;
01015 dist = dist-2*(dist/5);
01016 }
01017 else
01018 {
01019 dist = i2-i1+1;
01020 i1 = i1%tot_siz;
01021 i2 = i2%tot_siz;
01022 }
01023
01024 double x=0,y=0,xx=0,xy=0,yy=0;
01025 uInt j=i1;
01026 do
01027 {
01028 x += polyline[j].x();
01029 y += polyline[j].y();
01030 xx += polyline[j].x()*polyline[j].x();
01031 xy += polyline[j].x()*polyline[j].y();
01032 yy += polyline[j].y()*polyline[j].y();
01033 j = (j+1)%tot_siz;
01034 } while(j!=(i2+1)%tot_siz);
01035 double l1,l2,l3;
01036 x /= dist; y /= dist; xx /= dist; xy /= dist; yy /= dist;
01037
01038
01039
01040 ratio_eig = homogLineFromMoments(x,y,xx,xy,yy,l1,l2,l3);
01041
01042
01043
01044
01045
01046 lines.push_back(auxLine_F(l1,l2,l3,ratio_eig < 0.1));
01047 }
01048
01049 for(int i=0;i<list.size();i++)
01050 {
01051 QPointF oldPoint = polyline[list[i]->index];
01052 if( (not polyline.closed) and ((i==0) or (i==list.size()-1)))
01053 {
01054
01055 result.append(oldPoint);
01056 continue;
01057 }
01058 int ant = (i-1+list.size())%list.size();
01059 int post = (i+1)%list.size();
01060 double newx = (lines[i].l2)*(lines[ant].l3) - (lines[i].l3)*(lines[ant].l2);
01061 double newy = -(lines[i].l1)*(lines[ant].l3) + (lines[i].l3)*(lines[ant].l1);
01062 double newz = (lines[i].l1)*(lines[ant].l2) - (lines[i].l2)*(lines[ant].l1);
01063 if ( (not lines[i].ok) or (not lines[ant].ok) or
01064 (fabs(newz) < EPSILON) )
01065 result.append(oldPoint);
01066 else
01067 {
01068 double nx = newx/newz;
01069 double ny = newy/newz;
01070
01071
01072
01073
01074 double dist =
01075 sqrt((nx-oldPoint.x())*(nx-oldPoint.x()) +
01076 (ny-oldPoint.y())*(ny-oldPoint.y()));
01077 QPointF prevPoint = polyline[list[ant]->index],
01078 nextPoint = polyline[list[post]->index];
01079 double minDist =
01080 qMin(
01081 sqrt((prevPoint.x()-oldPoint.x())*(prevPoint.x()-oldPoint.x()) +
01082 (prevPoint.y()-oldPoint.y())*(prevPoint.y()-oldPoint.y())),
01083 sqrt((nextPoint.x()-oldPoint.x())*(nextPoint.x()-oldPoint.x()) +
01084 (nextPoint.y()-oldPoint.y())*(nextPoint.y()-oldPoint.y()))
01085 );
01086 if(dist < 0.2*minDist)
01087 result.append(QPointF(nx,ny));
01088 else
01089 result.append(oldPoint);
01090 }
01091 }
01092 }
01093 else
01094 {
01095
01096 it = list.begin();
01097 while(it != list.end())
01098 {
01099 result.append(polyline.at((*it)->index));
01100 it++;
01101 }
01102 }
01103
01104
01105 while (!list.isEmpty())
01106 delete list.takeFirst();
01107
01108
01109 result.closed = polyline.closed;
01110 result.direction = polyline.direction;
01111
01112
01113 return return_value;
01114 }
01115 #else
01116 double IterativePointElimination(const QVPolyline &polyline, QVPolyline &result,
01117 const double param, bool maxNumberOfPointsMethod,bool intersectLines,
01118 double *max_removed_cost)
01119 {
01120 result = polyline;
01121 std::cout << "Warning: IterativePointElimination requires GSL, MKL or LAPACK functionality enabled to work properly." << std::endl;
01122 return 0.0;
01123 }
01124
01125
01126 double IterativePointElimination(const QVPolylineF &polyline, QVPolylineF &result,
01127 const double param, bool maxNumberOfPointsMethod,bool intersectLines,
01128 double *max_removed_cost)
01129 {
01130 result = polyline;
01131 std::cout << "Warning: IterativePointElimination requires GSL, MKL or LAPACK functionality enabled to work properly." << std::endl;
01132 return 0.0;
01133 }
01134
01135 #endif // QVMATRIXALGEBRA_AVAILABLE
01136
01138
01139
01140
01141
01142
01143
01144
01145 #ifndef DOXYGEN_IGNORE_THIS
01146 const char coorX8Connect[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };
01147 const char coorY8Connect[8] = { -1, -1, 0, 1, 1, 1, 0, -1 };
01148 const char coorX4Connect[4] = { 0, 1, 0, -1, };
01149 const char coorY4Connect[4] = { -1, 0, 1, 0, };
01150 const char coorX4Diag[8] = { 1, 1, -1, -1 };
01151 const char coorY4Diag[8] = { -1, 1, 1, -1 };
01152 #endif
01153
01154
01155 #ifndef DOXYGEN_IGNORE_THIS
01156 QVPolyline getConnectedSetBorderContourThresholdFromBorderPoint(const QVImage<uChar> &image, const int startPointX, const int startPointY, const uChar threshold)
01157 {
01158 QVPolyline lista;
01159
01160 lista.closed = true;
01161 lista.append(QPoint(startPointX, startPointY));
01162
01163 QVIMAGE_INIT_READ(uChar,image);
01164 QRect roi = image.getROI();
01165
01166 Q_ASSERT_X(roi.contains(startPointX, startPointY), "getContourThresholdFromBorderPoint", "start point out of image ROI");
01167 Q_ASSERT_X(QVIMAGE_PIXEL(image, startPointX, startPointY, 0) >= threshold, "getContourThresholdFromBorderPoint", "start point is not contained in a connected set");
01168
01169
01170
01171 uChar searchDir = 128, numOuterPixels = 0;
01172 for (int i = 0; i<8; i++)
01173 {
01174 int x = startPointX +coorX8Connect[i], y = startPointY +coorY8Connect[i];
01175 if (!roi.contains(x, y))
01176 {
01177 numOuterPixels++;
01178 searchDir = i;
01179 }
01180 else if (QVIMAGE_PIXEL(image, x, y,0) < threshold)
01181 {
01182 numOuterPixels++;
01183 searchDir = i;
01184 }
01185 }
01186
01187
01188 Q_ASSERT_X(searchDir < 8, "getContourThresholdFromBorderPoint", "start point is inside the set, not in the border");
01189
01190
01191 if (numOuterPixels == 8)
01192 return lista;
01193
01194
01195 int sumSearchDir = 0, actualPointX = startPointX, actualPointY = startPointY;
01196 while (true)
01197 {
01198
01199 uChar d;
01200 int nextPointX, nextPointY;
01201 for (d = 0; d < 8; d++)
01202 {
01203 searchDir = (searchDir+1)%8;
01204 nextPointX = actualPointX + coorX8Connect[searchDir];
01205 nextPointY = actualPointY + coorY8Connect[searchDir];
01206 if (roi.contains(nextPointX, nextPointY))
01207 if ( (QVIMAGE_PIXEL(image, nextPointX, nextPointY,0) >= threshold) )
01208 break;
01209 }
01210
01211 sumSearchDir += d - 3;
01212
01213 actualPointX = nextPointX;
01214 actualPointY = nextPointY;
01215
01216 if ( QVIMAGE_PIXEL(image, actualPointX, actualPointY,0) < threshold )
01217 break;
01218
01219 if ( startPointX == actualPointX && startPointY == actualPointY)
01220 break;
01221
01222 lista.append(QPoint(actualPointX, actualPointY));
01223 searchDir = searchDir + 4;
01224 }
01225
01226 lista.direction = (sumSearchDir >= 0);
01227 return lista;
01228 }
01229
01230 QVPolyline getConnectedSetBorderContourThresholdFromBorderPoint(const QVImage<uShort> &image, const int startPointX, const int startPointY, const uShort threshold)
01231 {
01232 QVPolyline lista;
01233
01234 lista.closed = true;
01235 lista.append(QPoint(startPointX, startPointY));
01236
01237 QVIMAGE_INIT_READ(uShort,image);
01238 QRect roi = image.getROI();
01239
01240 Q_ASSERT_X(roi.contains(startPointX, startPointY), "getContourThresholdFromBorderPoint", "start point out of image ROI");
01241 Q_ASSERT_X(QVIMAGE_PIXEL(image, startPointX, startPointY, 0) >= threshold, "getContourThresholdFromBorderPoint", "start point is not contained in a connected set");
01242
01243
01244
01245 uShort searchDir = 128, numOuterPixels = 0;
01246 for (int i = 0; i<8; i++)
01247 {
01248 int x = startPointX +coorX8Connect[i], y = startPointY +coorY8Connect[i];
01249 if (!roi.contains(x, y))
01250 {
01251 numOuterPixels++;
01252 searchDir = i;
01253 }
01254 else if (QVIMAGE_PIXEL(image, x, y,0) < threshold)
01255 {
01256 numOuterPixels++;
01257 searchDir = i;
01258 }
01259 }
01260
01261
01262 Q_ASSERT_X(searchDir < 8, "getContourThresholdFromBorderPoint", "start point is inside the set, not in the border");
01263
01264
01265 if (numOuterPixels == 8)
01266 return lista;
01267
01268
01269 int sumSearchDir = 0, actualPointX = startPointX, actualPointY = startPointY;
01270 while (true)
01271 {
01272
01273 uShort d;
01274 int nextPointX, nextPointY;
01275 for (d = 0; d < 8; d++)
01276 {
01277 searchDir = (searchDir+1)%8;
01278 nextPointX = actualPointX + coorX8Connect[searchDir];
01279 nextPointY = actualPointY + coorY8Connect[searchDir];
01280 if (roi.contains(nextPointX, nextPointY))
01281 if ( (QVIMAGE_PIXEL(image, nextPointX, nextPointY,0) >= threshold) )
01282 break;
01283 }
01284
01285 sumSearchDir += d - 3;
01286
01287 actualPointX = nextPointX;
01288 actualPointY = nextPointY;
01289
01290 if ( QVIMAGE_PIXEL(image, actualPointX, actualPointY,0) < threshold )
01291 break;
01292
01293 if ( startPointX == actualPointX && startPointY == actualPointY)
01294 break;
01295
01296 lista.append(QPoint(actualPointX, actualPointY));
01297 searchDir = searchDir + 4;
01298 }
01299
01300 lista.direction = (sumSearchDir >= 0);
01301 return lista;
01302 }
01303
01304
01305
01306
01307 #endif
01308
01309 QVPolyline getConnectedSetBorderContourThreshold(const QVImage<uChar> &image, const QPoint startPoint, const uChar threshold)
01310 {
01311 QVIMAGE_INIT_READ(uChar,image);
01312 const QRect roi = image.getROI();
01313
01314 int col = startPoint.x(), row = startPoint.y();
01315
01316 if (QVIMAGE_PIXEL(image, col, row,0) < threshold)
01317 return QVPolyline();
01318
01319 while (roi.contains(col+1, row))
01320 {
01321 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
01322 break;
01323 col++;
01324 }
01325
01326 return getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01327 }
01328
01329 QVPolyline getConnectedSetBorderContourThreshold(const QVImage<uShort> &image, const QPoint startPoint, const uShort threshold)
01330 {
01331 QVIMAGE_INIT_READ(uShort,image);
01332 const QRect roi = image.getROI();
01333
01334 int col = startPoint.x(), row = startPoint.y();
01335
01336 if (QVIMAGE_PIXEL(image, col, row,0) < threshold)
01337 return QVPolyline();
01338
01339 while (roi.contains(col+1, row))
01340 {
01341 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
01342 break;
01343 col++;
01344 }
01345
01346 return getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01347 }
01348
01349 QList<QVPolyline> getConnectedSetBorderContoursThreshold(const QVImage <uChar> &image, const uChar threshold)
01350 {
01351 qDebug() << "getPolylinesThreshold()";
01352 QVImage<uChar> mask(image.getCols()+1, image.getRows()+1);
01353 Set(0, mask);
01354
01355 QVIMAGE_INIT_READ(uChar,image);
01356 QVIMAGE_INIT_WRITE(uChar,mask);
01357
01358 const QRect roi = image.getROI();
01359
01360 QList<QVPolyline> polylineList;
01361
01362
01363 for (int row = roi.y(); row < roi.y() + roi.height(); row++)
01364 for (int col = roi.x(); col < roi.y() + roi.width(); col++)
01365 {
01366
01367 if (QVIMAGE_PIXEL(image, col, row,0) >= threshold)
01368 {
01369
01370 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
01371 {
01372 QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01373 polylineList.append(lista);
01374
01375 QListIterator<QPoint> iterator(lista);
01376 for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
01377 {
01378 actual = iterator.next();
01379 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
01380 QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
01381 }
01382 }
01383
01384
01385 while (roi.contains(col+1, row))
01386 {
01387 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
01388 break;
01389 col++;
01390 }
01391
01392
01393 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
01394 {
01395 QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01396 polylineList.append(lista);
01397
01398 QListIterator<QPoint> iterator(lista);
01399 for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
01400 {
01401 actual = iterator.next();
01402 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
01403 QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
01404 }
01405 }
01406 }
01407
01408 }
01409 qDebug() << "getPolylinesThreshold():"<< polylineList.size() << "contours obtained";
01410 qDebug() << "getPolylinesThreshold() <~ return";
01411 return polylineList;
01412 }
01413
01414
01415 QList<QVPolyline> getConnectedSetBorderContoursThreshold(const QVImage <uShort> &image, const uShort threshold)
01416 {
01417 qDebug() << "getPolylinesThreshold()";
01418 QVImage<uShort> mask(image.getCols()+1, image.getRows()+1);
01419 Set(0, mask);
01420
01421 QVIMAGE_INIT_READ(uShort,image);
01422 QVIMAGE_INIT_WRITE(uShort,mask);
01423
01424 const QRect roi = image.getROI();
01425
01426 QList<QVPolyline> polylineList;
01427
01428
01429 for (int row = roi.y(); row < roi.y() + roi.height(); row++)
01430 for (int col = roi.x(); col < roi.y() + roi.width(); col++)
01431 {
01432
01433 if (QVIMAGE_PIXEL(image, col, row,0) >= threshold)
01434 {
01435
01436 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
01437 {
01438 QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01439 polylineList.append(lista);
01440
01441 QListIterator<QPoint> iterator(lista);
01442 for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
01443 {
01444 actual = iterator.next();
01445 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
01446 QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
01447 }
01448 }
01449
01450
01451 while (roi.contains(col+1, row))
01452 {
01453 if ( QVIMAGE_PIXEL(image, col+1, row,0) < threshold )
01454 break;
01455 col++;
01456 }
01457
01458
01459 if ( !QVIMAGE_PIXEL(mask, col, row,0) )
01460 {
01461 QVPolyline lista = getConnectedSetBorderContourThresholdFromBorderPoint(image, col, row, threshold);
01462 polylineList.append(lista);
01463
01464 QListIterator<QPoint> iterator(lista);
01465 for (QPoint previous = iterator.next(), actual; iterator.hasNext(); previous = actual)
01466 {
01467 actual = iterator.next();
01468 foreach (QPoint point, QVPolyline::line(actual.x(), actual.y(), previous.x(), previous.y()))
01469 QVIMAGE_PIXEL(mask, point.x(), point.y(),0) = true;
01470 }
01471 }
01472 }
01473
01474 }
01475 qDebug() << "getPolylinesThreshold():"<< polylineList.size() << "contours obtained";
01476 qDebug() << "getPolylinesThreshold() <~ return";
01477 return polylineList;
01478 }
01479
01480
01481
01483
01484 QVPolyline getLineContourThreshold4Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
01485 {
01486 const uInt cols = image.getCols(), rows = image.getRows();
01487 QVIMAGE_INIT_WRITE(uChar, image);
01488
01489 uInt lastDir = 666, coorX = point.x(), coorY = point.y();
01490
01491 qDebug() << "\tContour: new contour";
01492
01493 forever {
01494 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
01495 if (reverse)
01496 polyline.prepend(QPoint(coorX, coorY));
01497 else
01498 polyline.append(QPoint(coorX, coorY));
01499
01500 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
01501
01502 uInt dir;
01503 int newCoorX, newCoorY;
01504 for (dir = 0; dir < 4; dir++)
01505 {
01506 newCoorX = coorX + coorX4Connect[dir];
01507 newCoorY = coorY + coorY4Connect[dir];
01508
01509
01510 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01511 continue;
01512
01513
01514 if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold) && (lastDir != dir) )
01515 break;
01516 }
01517
01518 if (dir == 4) break;
01519
01520 coorX = newCoorX;
01521 coorY = newCoorY;
01522 lastDir = (dir+2)%4;
01523 }
01524
01525 return polyline;
01526 }
01527
01528 QList<QVPolyline> getLineContoursThreshold4Connectivity(const QVImage<uChar> &image, const uChar threshold)
01529 {
01530 const uInt cols = image.getCols(), rows = image.getRows();
01531 QVImage<uChar> clone = image;
01532
01533 QList<QVPolyline> polylineList;
01534
01535
01536 for(uInt col = 0; col < cols; col++)
01537 for(uInt row = 0; row < rows; row++)
01538 {
01539 QVIMAGE_INIT_READ(uChar, clone);
01540
01541 if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
01542 continue;
01543
01544
01545 QVPolyline polyline;
01546
01547
01548 getLineContourThreshold4Connectivity(clone, QPoint(col, row), polyline, threshold, false);
01549
01550
01551 uInt dir;
01552 int newCoorX, newCoorY;
01553 for (dir = 0; dir < 4; dir++)
01554 {
01555 newCoorX = col + coorX4Connect[dir];
01556 newCoorY = row + coorY4Connect[dir];
01557
01558
01559 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01560 continue;
01561
01562
01563 if ( (clone(newCoorX, newCoorY) >= threshold) )
01564 break;
01565 }
01566
01567
01568 if (dir != 4)
01569 getLineContourThreshold4Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
01570
01571
01572 polylineList.append(polyline);
01573 }
01574
01575 return polylineList;
01576 }
01577
01579
01580 QVPolyline getLineContourThreshold8Connectivity(QVImage<uChar> &image, const QPoint point, QVPolyline &polyline, const uChar threshold, bool reverse)
01581 {
01582 const uInt cols = image.getCols(), rows = image.getRows();
01583 QVIMAGE_INIT_WRITE(uChar, image);
01584
01585 uInt lastDir = 666, coorX = point.x(), coorY = point.y();
01586
01587 qDebug() << "\tContour: new contour";
01588
01589 bool continueCond = true;
01590 while(continueCond)
01591 {
01592 qDebug() << "\tContour:\tAppending point (" << coorX << ", " << coorY << ")";
01593 if (reverse)
01594 polyline.prepend(QPoint(coorX, coorY));
01595 else
01596 polyline.append(QPoint(coorX, coorY));
01597
01598 QVIMAGE_PIXEL(image, coorX, coorY, 0) = 0;
01599
01600
01601 uInt dir;
01602 int newCoorX, newCoorY;
01603 for (dir = 0; dir < 4; dir++)
01604 {
01605 newCoorX = coorX + coorX4Connect[dir];
01606 newCoorY = coorY + coorY4Connect[dir];
01607
01608
01609 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01610 continue;
01611
01612
01613 if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold) && (lastDir != dir) )
01614 break;
01615 }
01616
01617 if (dir == 4)
01618 {
01619
01620 uInt dir;
01621 int newCoorX, newCoorY;
01622 for (dir = 0; dir < 4; dir++)
01623 {
01624 newCoorX = coorX + coorX4Diag[dir];
01625 newCoorY = coorY + coorY4Diag[dir];
01626
01627
01628 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01629 continue;
01630
01631
01632 if ( (QVIMAGE_PIXEL(image, newCoorX, newCoorY, 0) >= threshold) && (lastDir != dir) )
01633 break;
01634 }
01635 if (dir == 4) break;
01636
01637 coorX = newCoorX;
01638 coorY = newCoorY;
01639 lastDir = (dir+2)%4;
01640 }
01641 else {
01642 coorX = newCoorX;
01643 coorY = newCoorY;
01644 lastDir = (dir+2)%4;
01645 }
01646 }
01647
01648 return polyline;
01649 }
01650
01651 QList<QVPolyline> getLineContoursThreshold8Connectivity(const QVImage<uChar> &image, const uChar threshold)
01652 {
01653 const uInt cols = image.getCols(), rows = image.getRows();
01654 QVImage<uChar> clone = image;
01655
01656 QList<QVPolyline> polylineList;
01657
01658
01659 for(uInt col = 0; col < cols; col++)
01660 for(uInt row = 0; row < rows; row++)
01661 {
01662 QVIMAGE_INIT_READ(uChar, clone);
01663
01664 if ( (QVIMAGE_PIXEL(clone, col, row, 0) < threshold) )
01665 continue;
01666
01667
01668 QVPolyline polyline;
01669
01670
01671 getLineContourThreshold8Connectivity(clone, QPoint(col, row), polyline, threshold, false);
01672
01673
01674 uInt dir;
01675 int newCoorX, newCoorY;
01676 for (dir = 0; dir < 4; dir++)
01677 {
01678 newCoorX = col + coorX4Connect[dir];
01679 newCoorY = row + coorY4Connect[dir];
01680
01681
01682 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01683 continue;
01684
01685
01686 if ( (clone(newCoorX, newCoorY) >= threshold) )
01687 break;
01688 }
01689
01690
01691 if (dir != 4)
01692 getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
01693 else {
01694
01695 uInt dir;
01696 int newCoorX, newCoorY;
01697 for (dir = 0; dir < 4; dir++)
01698 {
01699 newCoorX = col + coorX4Diag[dir];
01700 newCoorY = row + coorY4Diag[dir];
01701
01702
01703 if ( (newCoorX < 0) || (newCoorY < 0) || (newCoorX >= ((int)cols)) || (newCoorY >= ((int)rows)) )
01704 continue;
01705
01706
01707 if ( (clone(newCoorX, newCoorY) >= threshold) )
01708 break;
01709 }
01710
01711
01712 if (dir != 4)
01713 getLineContourThreshold8Connectivity(clone, QPoint(newCoorX, newCoorY), polyline, threshold, true);
01714 }
01715
01716
01717 polylineList.append(polyline);
01718 }
01719
01720 return polylineList;
01721 }
01722
01723 QVImage<uChar, 1> FastLaplaceFilter(const QVImage<uChar, 1> image)
01724 {
01725 const int cols = image.getCols(),
01726 rows = image.getRows();
01727
01728 if ( (cols == 0) or (rows == 0) )
01729 return image;
01730
01731 QVImage<uChar, 1> result(cols, rows);
01732
01733
01734 const int srcStep = image.getStep(),
01735 dstStep = result.getStep();
01736 const uChar *srcData = image.getReadData();
01737 uChar *dstData = result.getWriteData(),
01738 *bottomDstRowData = dstData + (rows-1) * dstStep;
01739
01740
01741 for(int j = 0; j < cols; j++)
01742 dstData[j] = bottomDstRowData[j] = 0;
01743
01744
01745 for(int i = 1; i < rows-1; i++)
01746 {
01747 const uChar *srcRowData = srcData + i * srcStep;
01748 uChar *dstRowData = dstData + i * dstStep;
01749
01750
01751 dstRowData[0] = dstRowData[cols-1] = 0;
01752
01753 for(int j = 1; j < cols-1; j++)
01754 {
01755 const int value = int(srcRowData[j]);
01756 const int neighbour1 = int(srcRowData[j - 1]);
01757 const int neighbour2 = int(srcRowData[j + 1]);
01758 const int neighbour3 = int(srcRowData[j - srcStep]);
01759 const int neighbour4 = int(srcRowData[j + srcStep]);
01760 const int minus = neighbour1 + neighbour2 + neighbour3 + neighbour4;
01761
01762 dstRowData[j] = ABS( (value << 2) - minus);
01763 }
01764 }
01765
01766 return result;
01767 }
01768
01769 QVImage<uChar, 1> FastSmoothFilter(const QVImage<uChar, 1> image, const uChar threshold)
01770 {
01771 const int cols = image.getCols(),
01772 rows = image.getRows();
01773
01774 if ( (cols == 0) or (rows == 0) )
01775 return image;
01776
01777 QVImage<uChar, 1> result(cols, rows);
01778
01779
01780 const int srcStep = image.getStep(),
01781 dstStep = result.getStep();
01782 const uChar *srcData = image.getReadData();
01783 uChar *dstData = result.getWriteData(),
01784 *bottomDstRowData = dstData + (rows-1) * dstStep;
01785
01786
01787 for(int j = 0; j < cols; j++)
01788 dstData[j] = bottomDstRowData[j] = 0;
01789
01790
01791 for(int i = 1; i < rows-1; i++)
01792 {
01793 const uChar *srcRowData = srcData + i * srcStep;
01794 uChar *dstRowData = dstData + i * dstStep;
01795
01796
01797 dstRowData[0] = dstRowData[cols-1] = 0;
01798
01799 for(int j = 1; j < cols-1; j++)
01800 {
01801 const int value = int(srcRowData[j]);
01802
01803 if (value < threshold)
01804 {
01805 dstRowData[j] = 0;
01806 continue;
01807 }
01808
01809 dstRowData[j] = ( (value << 1)
01810 + int(srcRowData[j - 1])
01811 + int(srcRowData[j + 1])
01812 + int(srcRowData[j - srcStep])
01813 + int(srcRowData[j + srcStep]) ) / 6;
01814 }
01815 }
01816
01817 return result;
01818 }
01819
01820 QVImage<uChar, 1> SmoothFilter(const QVImage<uChar, 1> image, const uChar threshold)
01821 {
01822 const int cols = image.getCols(),
01823 rows = image.getRows();
01824
01825 if ( (cols == 0) or (rows == 0) )
01826 return image;
01827
01828
01829 QVImage<uChar, 1> result(cols, rows);
01830 const int srcStep = image.getStep(),
01831 dstStep = result.getStep();
01832 const uChar *srcData = image.getReadData();
01833 uChar *dstData = result.getWriteData(),
01834 *bottomDstRowData = dstData + (rows-1) * dstStep;
01835
01836
01837 for(int j = 0; j < cols; j++)
01838 dstData[j] = bottomDstRowData[j] = 0;
01839
01840
01841 for(int i = 1; i < rows-1; i++)
01842 {
01843 const uChar *srcRowData = srcData + i * srcStep;
01844 uChar *dstRowData = dstData + i * dstStep;
01845
01846
01847 dstRowData[0] = dstRowData[cols-1] = 0;
01848
01849 for(int j = 1; j < cols-1; j++)
01850 {
01851 const int value = int(srcRowData[j]);
01852
01853 if (value < threshold)
01854 {
01855 dstRowData[j] = 0;
01856 continue;
01857 }
01858
01859 dstRowData[j] = ( (value << 2)
01860 + (int(srcRowData[j - 1]) << 1)
01861 + (int(srcRowData[j + 1]) << 1)
01862 + (int(srcRowData[j - srcStep]) << 1)
01863 + (int(srcRowData[j + srcStep]) << 1)
01864
01865 + int(srcRowData[j - 1 - srcStep])
01866 + int(srcRowData[j + 1 - srcStep])
01867 + int(srcRowData[j - 1 + srcStep])
01868 + int(srcRowData[j + 1 + srcStep])
01869 ) >> 4;
01870 }
01871 }
01872
01873 return result;
01874 }
01875
01876 QList<QPointF> FastLaplacePoints(const QVImage<uChar, 1> &image, const int threshold, const bool applyPreviousSmooth, const bool smoothResponseImage)
01877 {
01878 const QVImage<uChar, 1> laplaceImage = FastLaplaceFilter(applyPreviousSmooth?FastSmoothFilter(image):image);
01879
01880 return getLocalExtremaPixels(smoothResponseImage?FastSmoothFilter(laplaceImage, threshold):laplaceImage, threshold);
01881 }
01882
01883
01884