diff --git a/modules/ximgproc/src/edge_drawing.cpp b/modules/ximgproc/src/edge_drawing.cpp index 8c943a7887c..62166f98c85 100644 --- a/modules/ximgproc/src/edge_drawing.cpp +++ b/modules/ximgproc/src/edge_drawing.cpp @@ -154,10 +154,7 @@ class EdgeDrawingImpl : public EdgeDrawing int gradThresh; // gradient threshold int anchorThresh; // anchor point threshold - - static void SplitSegment2Lines(double* x, double* y, int noPixels, int segmentNo, std::vector& lines, int min_line_len = 6, double line_error = 1.0); std::vector lines; - std::vector invalidLines; int linesNo; int min_line_len; double line_error; @@ -216,12 +213,11 @@ class EdgeDrawingImpl : public EdgeDrawing void JoinArcs3(); // circle utility functions - static Circle* addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, double* x, double* y, int noPixels); - static Circle* addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, EllipseEquation* pEq, double ellipseFitError, double* x, double* y, int noPixels); + static void addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, double* x, double* y, int noPixels); + static void addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, EllipseEquation* pEq, double ellipseFitError, double* x, double* y, int noPixels); static void sortCircles(Circle* circles, int noCircles); static bool CircleFit(double* x, double* y, int N, double* pxc, double* pyc, double* pr, double* pe); static void ComputeCirclePoints(double xc, double yc, double r, double* px, double* py, int* noPoints); - static void sortCircle(Circle* circles, int noCircles); // ellipse utility functions static bool EllipseFit(double* x, double* y, int noPoints, EllipseEquation* pResult, int mode = FPF); @@ -365,7 +361,6 @@ void EdgeDrawingImpl::detectEdges(InputArray src) anchorNos = 0; anchorPoints.clear(); lines.clear(); - invalidLines.clear(); segmentPoints.clear(); segmentPoints.push_back(vector()); // create empty vector of points for segments srcImage = src.getMat(); @@ -1270,8 +1265,12 @@ int EdgeDrawingImpl::RetrieveChainNos(Chain* chains, int root, int chainNos[]) void EdgeDrawingImpl::detectLines(OutputArray _lines) { + std::vector linePoints; if (segmentPoints.size() < 1) + { + Mat(linePoints).copyTo(_lines); return; + } min_line_len = params.MinLineLength; line_error = params.LineFitErrorThreshold; @@ -1304,17 +1303,8 @@ void EdgeDrawingImpl::detectLines(OutputArray _lines) JoinCollinearLines(); -#define PRECISON_ANGLE 22.5 - precision = (PRECISON_ANGLE / 180) * CV_PI; -#undef PRECISON_ANGLE - - if (nfa->LUTSize == 1 && params.NFAValidation) - { - int lutSize = (width + height) / 8; - double prob = 1.0 / 8; // probability of alignment - nfa = new NFALUT(lutSize, prob, width, height); + if (params.NFAValidation) ValidateLineSegments(); - } // Delete redundant space from lines // Pop them back @@ -1322,16 +1312,14 @@ void EdgeDrawingImpl::detectLines(OutputArray _lines) for (int i = 1; i <= size - linesNo; i++) lines.pop_back(); - std::vector linePoints; for (int i = 0; i < linesNo; i++) { Vec4f line((float)lines[i].sx, (float)lines[i].sy, (float)lines[i].ex, (float)lines[i].ey); linePoints.push_back(line); } - + Mat(linePoints).copyTo(_lines); delete[] x; delete[] y; - Mat(linePoints).copyTo(_lines); } // Computes the minimum line length using the NFA formula given width & height values @@ -1345,7 +1333,7 @@ int EdgeDrawingImpl::ComputeMinLineLength() double logNT = 2.0 * (log10((double)width) + log10((double)height)); return (int)round((-logNT / log10(0.125)) * 0.5); -} //end-ComputeMinLineLength +} //----------------------------------------------------------------- // Given a full segment of pixels, splits the chain to lines @@ -1434,10 +1422,14 @@ void EdgeDrawingImpl::SplitSegment2Lines(double* x, double* y, int noPixels, int index--; ComputeClosestPoint(x[index], y[index], lastA, lastB, lastInvert, ex, ey); + if ((sx == ex) & (sy == ey)) + break; + // Add the line segment to lines lines.push_back(EDLineSegment(lastA, lastB, lastInvert, sx, sy, ex, ey, segmentNo, firstPixelIndex + noSkippedPixels, index - noSkippedPixels + 1)); linesNo++; len = index + 1; + break; } } @@ -1499,6 +1491,17 @@ void EdgeDrawingImpl::JoinCollinearLines() void EdgeDrawingImpl::ValidateLineSegments() { +#define PRECISION_ANGLE 22.5 + precision = (PRECISION_ANGLE / 180) * CV_PI; +#undef PRECISION_ANGLE + + if (nfa->LUTSize == 1) + { + int lutSize = (width + height) / 8; + double prob = 1.0 / 8; // probability of alignment + nfa = new NFALUT(lutSize, prob, width, height); + } + int* x = new int[(width + height) * 4]; int* y = new int[(width + height) * 4]; @@ -1586,9 +1589,7 @@ void EdgeDrawingImpl::ValidateLineSegments() } // Check validation by NFA computation (fast due to LUT) - valid = nfa->checkValidationByNFA(count, aligned); - if (valid == false) - valid = ValidateLineSegmentRect(x, y, ls); + valid = nfa->checkValidationByNFA(count, aligned) || ValidateLineSegmentRect(x, y, ls); } if (valid) @@ -1597,10 +1598,6 @@ void EdgeDrawingImpl::ValidateLineSegments() lines[noValidLines] = lines[i]; noValidLines++; } - else - { - invalidLines.push_back(lines[i]); - } } linesNo = noValidLines; @@ -2295,102 +2292,6 @@ void EdgeDrawingImpl::EnumerateRectPoints(double sx, double sy, double ex, doubl *pNoPoints = noPoints; } -void EdgeDrawingImpl::SplitSegment2Lines(double* x, double* y, int noPixels, int segmentNo, vector& lines, int min_line_len, double line_error) -{ - // First pixel of the line segment within the segment of points - int firstPixelIndex = 0; - - while (noPixels >= min_line_len) - { - // Start by fitting a line to MIN_LINE_LEN pixels - bool valid = false; - double lastA(0), lastB(0), error; - int lastInvert(0); - - while (noPixels >= min_line_len) - { - LineFit(x, y, min_line_len, lastA, lastB, error, lastInvert); - if (error <= 0.5) - { - valid = true; - break; - } - - noPixels -= 1; // Go slowly - x += 1; - y += 1; - firstPixelIndex += 1; - } - - if (valid == false) - return; - - // Now try to extend this line - int index = min_line_len; - int len = min_line_len; - - while (index < noPixels) - { - int startIndex = index; - int lastGoodIndex = index - 1; - int goodPixelCount = 0; - int badPixelCount = 0; - while (index < noPixels) - { - double d = ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert); - - if (d <= line_error) - { - lastGoodIndex = index; - goodPixelCount++; - badPixelCount = 0; - } - else - { - badPixelCount++; - if (badPixelCount >= 5) - break; - } - index++; - } - - if (goodPixelCount >= 2) - { - len += lastGoodIndex - startIndex + 1; - LineFit(x, y, len, lastA, lastB, lastInvert); - index = lastGoodIndex + 1; - } - - if (goodPixelCount < 2 || index >= noPixels) - { - // End of a line segment. Compute the end points - double sx, sy, ex, ey; - - index = 0; - while (ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert) > line_error) - index++; - ComputeClosestPoint(x[index], y[index], lastA, lastB, lastInvert, sx, sy); - int noSkippedPixels = index; - - index = lastGoodIndex; - while (ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert) > line_error) - index--; - ComputeClosestPoint(x[index], y[index], lastA, lastB, lastInvert, ex, ey); - - // Add the line segment to lines - lines.push_back(EDLineSegment(lastA, lastB, lastInvert, sx, sy, ex, ey, segmentNo, firstPixelIndex + noSkippedPixels, index - noSkippedPixels + 1)); - len = index + 1; - break; - } - } - - noPixels -= len; - x += len; - y += len; - firstPixelIndex += len; - } -} - /*--------------------------------------EDPF----------------------------------------*/ //---------------------------------------------------------------------------------- @@ -2511,19 +2412,23 @@ double EdgeDrawingImpl::NFA(double prob, int len) void EdgeDrawingImpl::detectEllipses(OutputArray ellipses) { + vector _ellipses; if (segmentPoints.size() < 1) + { + Mat(_ellipses).copyTo(ellipses); return; + } - vector _ellipses; - Circles.clear(); - Ellipses.clear(); + min_line_len = 6; + Circles.clear(); + Ellipses.clear(); + lines.clear(); // Arcs & circles to be detected // If the end-points of the segment is very close to each other, // then directly fit a circle/ellipse instread of line fitting noCircles1 = 0; circles1 = new Circle[(width + height) * 8]; - // ----------------------------------- DETECT LINES --------------------------------- int bufferSize = 0; for (int i = 0; i < (int)segmentPoints.size(); i++) bufferSize += (int)segmentPoints[i].size(); @@ -2611,9 +2516,10 @@ void EdgeDrawingImpl::detectEllipses(OutputArray ellipses) } } // Otherwise, split to lines - SplitSegment2Lines(x, y, noPixels, i, lines); + SplitSegment2Lines(x, y, noPixels, i); } + min_line_len = params.MinLineLength; segmentStartLines[segmentNos] = (int)lines.size(); // ------------------------------- DETECT ARCS --------------------------------- @@ -3157,7 +3063,6 @@ void EdgeDrawingImpl::DetectArcs() if ((coverage >= FULL_CIRCLE_RATIO && circleFitError <= LONG_ARC_ERROR)) { addCircle(circles1, noCircles1, XC, YC, R, Error, x, y, noPixels); - } else { @@ -3173,7 +3078,6 @@ void EdgeDrawingImpl::DetectArcs() y += noPixels; firstLine = curLine; - info[curLine].taken = false; // may reuse the last line? } firstLine = lastLine; } @@ -3187,15 +3091,10 @@ void EdgeDrawingImpl::DetectArcs() void EdgeDrawingImpl::ValidateCircles(bool validate) { precision = CV_PI / 16; // Alignment precision - double max = width; - if (height > max) - max = height; - double min = width; - if (height < min) - min = height; - double* px = new double[8 * (width + height)]; - double* py = new double[8 * (width + height)]; + int points_buffer_size = 8 * (width + height); + double *px = new double[points_buffer_size]; + double *py = new double[points_buffer_size]; if (nfa->LUTSize == 1 && params.NFAValidation) { @@ -3223,14 +3122,16 @@ void EdgeDrawingImpl::ValidateCircles(bool validate) validateAgain = false; - int noPoints = 0; + int noPoints = (int)(computeEllipsePerimeter(&circle->eq)); - if (circle->isEllipse) + if (noPoints > points_buffer_size) { - noPoints = std::min(static_cast(computeEllipsePerimeter(&circle->eq)), 8 * (width + height)); + i++; + continue; + } - if (noPoints % 2) - noPoints--; + if (circle->isEllipse) + { ComputeEllipsePoints(circle->eq.coeff, px, py, noPoints); } else @@ -3451,7 +3352,7 @@ void EdgeDrawingImpl::ValidateCircles(bool validate) void EdgeDrawingImpl::JoinCircles() { // Sort the circles wrt their radius - sortCircle(circles2, noCircles2); + sortCircles(circles2, noCircles2); noCircles = noCircles2; Circle* circles = circles2; @@ -4449,7 +4350,7 @@ void EdgeDrawingImpl::JoinArcs3() delete[] candidateArcs; } -Circle* EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, double* x, double* y, int noPixels) +void EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, double* x, double* y, int noPixels) { circles[noCircles].xc = xc; circles[noCircles].yc = yc; @@ -4464,11 +4365,9 @@ Circle* EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, d circles[noCircles].isEllipse = false; noCircles++; - - return &circles[noCircles - 1]; } -Circle* EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, EllipseEquation* pEq, double ellipseFitError, double* x, double* y, int noPixels) +void EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, double yc, double r, double circleFitError, EllipseEquation* pEq, double ellipseFitError, double* x, double* y, int noPixels) { circles[noCircles].xc = xc; circles[noCircles].yc = yc; @@ -4485,8 +4384,6 @@ Circle* EdgeDrawingImpl::addCircle(Circle* circles, int& noCircles, double xc, d circles[noCircles].isEllipse = true; noCircles++; - - return &circles[noCircles - 1]; } void EdgeDrawingImpl::sortCircles(Circle* circles, int noCircles) @@ -4862,12 +4759,9 @@ double EdgeDrawingImpl::ComputeEllipseCenterAndAxisLengths(EllipseEquation* eq, // --------------------------------------------------------------------------- // Given an ellipse equation, computes "noPoints" many consecutive points // on the ellipse periferi. These points can be used to draw the ellipse -// noPoints must be an even number. // void EdgeDrawingImpl::ComputeEllipsePoints(double* pvec, double* px, double* py, int noPoints) { - if (noPoints % 2) - noPoints--; int npts = noPoints / 2; double** u = AllocateMatrix(3, npts + 1); @@ -5423,26 +5317,6 @@ void EdgeDrawingImpl::ComputeCirclePoints(double xc, double yc, double r, double *noPoints = count; } -void EdgeDrawingImpl::sortCircle(Circle* circles, int noCircles) -{ - for (int i = 0; i < noCircles - 1; i++) - { - int max = i; - for (int j = i + 1; j < noCircles; j++) - { - if (circles[j].r > circles[max].r) - max = j; - } - - if (max != i) - { - Circle t = circles[i]; - circles[i] = circles[max]; - circles[max] = t; - } - } -} - bool EdgeDrawingImpl::EllipseFit(double* x, double* y, int noPoints, EllipseEquation* pResult, int mode) { double** D = AllocateMatrix(noPoints + 1, 7); diff --git a/modules/ximgproc/test/test_fld.cpp b/modules/ximgproc/test/test_fld.cpp index d2dc8421581..c308a706d70 100644 --- a/modules/ximgproc/test/test_fld.cpp +++ b/modules/ximgproc/test/test_fld.cpp @@ -40,8 +40,12 @@ class ximgproc_FLD: public FLDBase class ximgproc_ED: public FLDBase { public: - ximgproc_ED() { } + ximgproc_ED() + { + detector = createEdgeDrawing(); + } protected: + Ptr detector; }; @@ -214,11 +218,9 @@ TEST_F(ximgproc_ED, whiteNoise) for (int i = 0; i < EPOCHS; ++i) { GenerateWhiteNoise(test_image); - Ptr detector = createEdgeDrawing(); detector->detectEdges(test_image); detector->detectLines(lines); - - if(40u >= lines.size()) ++passedtests; + if(2u >= lines.size()) ++passedtests; } ASSERT_EQ(EPOCHS, passedtests); } @@ -228,10 +230,8 @@ TEST_F(ximgproc_ED, constColor) for (int i = 0; i < EPOCHS; ++i) { GenerateConstColor(test_image); - Ptr detector = createEdgeDrawing(); detector->detectEdges(test_image); - detector->detectLines(lines); - if(0u == lines.size()) ++passedtests; + if(0u == detector->getSegments().size()) ++passedtests; } ASSERT_EQ(EPOCHS, passedtests); } @@ -242,7 +242,6 @@ TEST_F(ximgproc_ED, lines) { const unsigned int numOfLines = 1; GenerateLines(test_image, numOfLines); - Ptr detector = createEdgeDrawing(); detector->detectEdges(test_image); detector->detectLines(lines); if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect @@ -256,7 +255,6 @@ TEST_F(ximgproc_ED, mergeLines) { const unsigned int numOfLines = 1; GenerateBrokenLines(test_image, numOfLines); - Ptr detector = createEdgeDrawing(); detector->detectEdges(test_image); detector->detectLines(lines); if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect @@ -269,13 +267,32 @@ TEST_F(ximgproc_ED, rotatedRect) for (int i = 0; i < EPOCHS; ++i) { GenerateRotatedRect(test_image); - Ptr detector = createEdgeDrawing(); detector->detectEdges(test_image); detector->detectLines(lines); - if(2u <= lines.size()) ++passedtests; + if(6u <= lines.size()) ++passedtests; } ASSERT_EQ(EPOCHS, passedtests); } +TEST_F(ximgproc_ED, ManySmallCircles) +{ + string picture_name = "cv/imgproc/beads.jpg"; + + string filename = cvtest::TS::ptr()->get_data_path() + picture_name; + test_image = imread(filename, IMREAD_GRAYSCALE); + EXPECT_FALSE(test_image.empty()) << "Invalid test image: " << filename; + + vector ellipses; + detector->detectEdges(test_image); + detector->detectLines(lines); + detector->detectEllipses(ellipses); + + size_t segments_size = 6458; + size_t lines_size = 6264; + size_t ellipses_size = 2449; + EXPECT_EQ(detector->getSegments().size(), segments_size); + EXPECT_EQ(lines.size(), lines_size); + EXPECT_EQ(ellipses.size(), ellipses_size); +} }} // namespace