From 03407a9da033a91b9684f580f81cca960d084ee9 Mon Sep 17 00:00:00 2001 From: take1014 Date: Fri, 9 Feb 2018 04:54:43 +0900 Subject: [PATCH] Merge pull request #10646 from take1014:master * Add a new interface for hough transform * Fixed warning code * Fix HoughLinesUsingSetOfPoints based on HoughLinesStandard * Delete memset * Rename HoughLinesUsingSetOfPoints and add common function * Fix test error * Change static function name * Change using CV_Assert instead of if-block and add integer test case * I solve the conflict and delete 'std :: tr1' and changed it to use 'tuple' * I deleted std::tr1::get and changed int to use 'get' * Fixed sample code * revert test_main.cpp * Delete sample code in comment and add snippets * Change file name * Delete static function * Fixed build error --- modules/imgproc/include/opencv2/imgproc.hpp | 21 +++ modules/imgproc/src/hough.cpp | 123 +++++++++++++++--- modules/imgproc/test/test_houghlines.cpp | 78 +++++++++++ .../snippets/imgproc_HoughLinesPointSet.cpp | 33 +++++ 4 files changed, 235 insertions(+), 20 deletions(-) create mode 100644 samples/cpp/tutorial_code/snippets/imgproc_HoughLinesPointSet.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index d682369d58..0004485691 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2072,6 +2072,27 @@ CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0 ); +/** @brief Finds lines in a set of points using the standard Hough transform. + +The function finds lines in a set of points using a modification of the Hough transform. +@include snippets/imgproc_HoughLinesPointSet.cpp +@param _point Input vector of points. Each vector must be encoded as a Point vector \f$(x,y)\f$. Type must be CV_32FC2 or CV_32SC2. +@param _lines Output vector of found lines. Each vector is encoded as a vector \f$(votes, rho, theta)\f$. +The larger the value of 'votes', the higher the reliability of the Hough line. +@param lines_max Max count of hough lines. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ) +@param min_rho Minimum Distance value of the accumulator in pixels. +@param max_rho Maximum Distance value of the accumulator in pixels. +@param rho_step Distance resolution of the accumulator in pixels. +@param min_theta Minimum angle value of the accumulator in radians. +@param max_theta Maximum angle value of the accumulator in radians. +@param theta_step Angle resolution of the accumulator in radians. + */ +CV_EXPORTS_W void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, int threshold, + double min_rho, double max_rho, double rho_step, + double min_theta, double max_theta, double theta_step ); + /** @example houghcircles.cpp An example using the Hough circle detector */ diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index e4c8b3f198..2e4a582627 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -68,6 +68,32 @@ struct hough_cmp_gt const int* aux; }; +static void +createTrigTable( int numangle, double min_theta, double theta_step, + float irho, float *tabSin, float *tabCos ) +{ + float ang = static_cast(min_theta); + for(int n = 0; n < numangle; ang += (float)theta_step, n++ ) + { + tabSin[n] = (float)(sin((double)ang) * irho); + tabCos[n] = (float)(cos((double)ang) * irho); + } +} + +static void +findLocalMaximums( int numrho, int numangle, int threshold, + const int *accum, std::vector& sort_buf ) +{ + for(int r = 0; r < numrho; r++ ) + for(int n = 0; n < numangle; n++ ) + { + int base = (n+1) * (numrho+2) + r+1; + if( accum[base] > threshold && + accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] && + accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] ) + sort_buf.push_back(base); + } +} /* Here image is an input raster; @@ -125,21 +151,17 @@ HoughLinesStandard( const Mat& img, float rho, float theta, } #endif - AutoBuffer _accum((numangle+2) * (numrho+2)); + + Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 ); std::vector _sort_buf; AutoBuffer _tabSin(numangle); AutoBuffer _tabCos(numangle); - int *accum = _accum; + int *accum = _accum.ptr(); float *tabSin = _tabSin, *tabCos = _tabCos; - memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) ); - - float ang = static_cast(min_theta); - for(int n = 0; n < numangle; ang += theta, n++ ) - { - tabSin[n] = (float)(sin((double)ang) * irho); - tabCos[n] = (float)(cos((double)ang) * irho); - } + // create sin and cos table + createTrigTable( numangle, min_theta, theta, + irho, tabSin, tabCos ); // stage 1. fill accumulator for( i = 0; i < height; i++ ) @@ -155,15 +177,7 @@ HoughLinesStandard( const Mat& img, float rho, float theta, } // stage 2. find local maximums - for(int r = 0; r < numrho; r++ ) - for(int n = 0; n < numangle; n++ ) - { - int base = (n+1) * (numrho+2) + r+1; - if( accum[base] > threshold && - accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] && - accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] ) - _sort_buf.push_back(base); - } + findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf ); // stage 3. sort the detected lines by accumulator value std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum)); @@ -883,6 +897,76 @@ void HoughLinesP(InputArray _image, OutputArray _lines, Mat(lines).copyTo(_lines); } +void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, int threshold, + double min_rho, double max_rho, double rho_step, + double min_theta, double max_theta, double theta_step ) +{ + std::vector lines; + std::vector point; + _point.copyTo(point); + + CV_Assert( _point.type() == CV_32FC2 || _point.type() == CV_32SC2 ); + if( lines_max <= 0 ) { + CV_Error( Error::StsBadArg, "lines_max must be greater than 0" ); + } + if( threshold < 0) { + CV_Error( Error::StsBadArg, "threshold must be greater than 0" ); + } + if( ((max_rho - min_rho) <= 0) || ((max_theta - min_theta) <= 0) ) { + CV_Error( Error::StsBadArg, "max must be greater than min" ); + } + if( ((rho_step <= 0)) || ((theta_step <= 0)) ) { + CV_Error( Error::StsBadArg, "step must be greater than 0" ); + } + + int i; + float irho = 1 / (float)rho_step; + float irho_min = ((float)min_rho * irho); + int numangle = cvRound((max_theta - min_theta) / theta_step); + int numrho = cvRound((max_rho - min_rho + 1) / rho_step); + + Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 ); + std::vector _sort_buf; + AutoBuffer _tabSin(numangle); + AutoBuffer _tabCos(numangle); + int *accum = _accum.ptr(); + float *tabSin = _tabSin, *tabCos = _tabCos; + + // create sin and cos table + createTrigTable( numangle, min_theta, theta_step, + irho, tabSin, tabCos ); + + // stage 1. fill accumlator + for( i = 0; i < (int)point.size(); i++ ) + for(int n = 0; n < numangle; n++ ) + { + int r = cvRound( point.at(i).x * tabCos[n] + point.at(i).y * tabSin[n] - irho_min); + accum[(n+1) * (numrho+2) + r+1]++; + } + + // stage 2. find local maximums + findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf ); + + // stage 3. sort the detected lines by accumulator value + std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum)); + + // stage 4. store the first min(total,linesMax) lines to the output buffer + lines_max = std::min(lines_max, (int)_sort_buf.size()); + double scale = 1./(numrho+2); + for( i = 0; i < lines_max; i++ ) + { + LinePolar line; + int idx = _sort_buf[i]; + int n = cvFloor(idx*scale) - 1; + int r = idx - (n+1)*(numrho+2) - 1; + line.rho = static_cast(min_rho) + r * (float)rho_step; + line.angle = static_cast(min_theta) + n * (float)theta_step; + lines.push_back(Vec3d((double)accum[idx], (double)line.rho, (double)line.angle)); + } + + Mat(lines).copyTo(_lines); +} + /****************************************************************************************\ * Circle Detection * \****************************************************************************************/ @@ -1611,7 +1695,6 @@ void HoughCircles( InputArray _image, OutputArray _circles, { HoughCircles(_image, _circles, method, dp, minDist, param1, param2, minRadius, maxRadius, -1, 3); } - } // \namespace cv diff --git a/modules/imgproc/test/test_houghlines.cpp b/modules/imgproc/test/test_houghlines.cpp index fa3a51fa99..916b9223a4 100644 --- a/modules/imgproc/test/test_houghlines.cpp +++ b/modules/imgproc/test/test_houghlines.cpp @@ -139,6 +139,29 @@ public: } }; +typedef tuple HoughLinesPointSetInput_t; +class HoughLinesPointSetTest : public testing::TestWithParam +{ +protected: + void run_test(); + double Rho; + double Theta; + double rhoMin, rhoMax, rhoStep; + double thetaMin, thetaMax, thetaStep; +public: + HoughLinesPointSetTest() + { + rhoMin = get<0>(GetParam()); + rhoMax = get<1>(GetParam()); + rhoStep = (rhoMax - rhoMin) / 360.0f; + thetaMin = get<2>(GetParam()); + thetaMax = get<3>(GetParam()); + thetaStep = CV_PI / 180.0f; + Rho = 320.00000; + Theta = 1.04719; + } +}; + void BaseHoughLineTest::run_test(int type) { string filename = cvtest::TS::ptr()->get_data_path() + picture_name; @@ -195,6 +218,50 @@ void BaseHoughLineTest::run_test(int type) #endif } +void HoughLinesPointSetTest::run_test(void) +{ + Mat lines_f, lines_i; + vector pointf; + vector pointi; + vector line_polar_f, line_polar_i; + const float Points[20][2] = { + { 0.0f, 369.0f }, { 10.0f, 364.0f }, { 20.0f, 358.0f }, { 30.0f, 352.0f }, + { 40.0f, 346.0f }, { 50.0f, 341.0f }, { 60.0f, 335.0f }, { 70.0f, 329.0f }, + { 80.0f, 323.0f }, { 90.0f, 318.0f }, { 100.0f, 312.0f }, { 110.0f, 306.0f }, + { 120.0f, 300.0f }, { 130.0f, 295.0f }, { 140.0f, 289.0f }, { 150.0f, 284.0f }, + { 160.0f, 277.0f }, { 170.0f, 271.0f }, { 180.0f, 266.0f }, { 190.0f, 260.0f } + }; + + // Float + for (int i = 0; i < 20; i++) + { + pointf.push_back(Point2f(Points[i][0],Points[i][1])); + } + + HoughLinesPointSet(pointf, lines_f, 20, 1, + rhoMin, rhoMax, rhoStep, + thetaMin, thetaMax, thetaStep); + + lines_f.copyTo( line_polar_f ); + + // Integer + for( int i = 0; i < 20; i++ ) + { + pointi.push_back( Point2i( (int)Points[i][0], (int)Points[i][1] ) ); + } + + HoughLinesPointSet( pointi, lines_i, 20, 1, + rhoMin, rhoMax, rhoStep, + thetaMin, thetaMax, thetaStep ); + + lines_i.copyTo( line_polar_i ); + + EXPECT_EQ((int)(line_polar_f.at(0).val[1] * 100000.0f), (int)(Rho * 100000.0f)); + EXPECT_EQ((int)(line_polar_f.at(0).val[2] * 100000.0f), (int)(Theta * 100000.0f)); + EXPECT_EQ((int)(line_polar_i.at(0).val[1] * 100000.0f), (int)(Rho * 100000.0f)); + EXPECT_EQ((int)(line_polar_i.at(0).val[2] * 100000.0f), (int)(Theta * 100000.0f)); +} + TEST_P(StandartHoughLinesTest, regression) { run_test(STANDART); @@ -205,6 +272,11 @@ TEST_P(ProbabilisticHoughLinesTest, regression) run_test(PROBABILISTIC); } +TEST_P(HoughLinesPointSetTest, regression) +{ + run_test(); +} + INSTANTIATE_TEST_CASE_P( ImgProc, StandartHoughLinesTest, testing::Combine(testing::Values( "shared/pic5.png", "../stitching/a1.png" ), testing::Values( 1, 10 ), testing::Values( 0.05, 0.1 ), @@ -219,4 +291,10 @@ INSTANTIATE_TEST_CASE_P( ImgProc, ProbabilisticHoughLinesTest, testing::Combine( testing::Values( 0, 4 ) )); +INSTANTIATE_TEST_CASE_P( Imgproc, HoughLinesPointSetTest, testing::Combine(testing::Values( 0.0f, 120.0f ), + testing::Values( 360.0f, 480.0f ), + testing::Values( 0.0f, (CV_PI / 18.0f) ), + testing::Values( (CV_PI / 2.0f), (CV_PI * 5.0f / 12.0f) ) + )); + }} // namespace diff --git a/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesPointSet.cpp b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesPointSet.cpp new file mode 100644 index 0000000000..e0f9337b87 --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesPointSet.cpp @@ -0,0 +1,33 @@ +#include + +using namespace cv; +using namespace std; + +int main() +{ + Mat lines; + vector line3d; + vector point; + const static float Points[20][2] = { + { 0.0f, 369.0f }, { 10.0f, 364.0f }, { 20.0f, 358.0f }, { 30.0f, 352.0f }, + { 40.0f, 346.0f }, { 50.0f, 341.0f }, { 60.0f, 335.0f }, { 70.0f, 329.0f }, + { 80.0f, 323.0f }, { 90.0f, 318.0f }, { 100.0f, 312.0f }, { 110.0f, 306.0f }, + { 120.0f, 300.0f }, { 130.0f, 295.0f }, { 140.0f, 289.0f }, { 150.0f, 284.0f }, + { 160.0f, 277.0f }, { 170.0f, 271.0f }, { 180.0f, 266.0f }, { 190.0f, 260.0f } + }; + + for (int i = 0; i < 20; i++) + { + point.push_back(Point2f(Points[i][0],Points[i][1])); + } + + double rhoMin = 0.0f, rhoMax = 360.0f, rhoStep = 1; + double thetaMin = 0.0f, thetaMax = CV_PI / 2.0f, thetaStep = CV_PI / 180.0f; + + HoughLinesPointSet(point, lines, 20, 1, + rhoMin, rhoMax, rhoStep, + thetaMin, thetaMax, thetaStep); + + lines.copyTo(line3d); + printf("votes:%d, rho:%.7f, theta:%.7f\n",(int)line3d.at(0).val[0], line3d.at(0).val[1], line3d.at(0).val[2]); +}