diff --git a/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.markdown b/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.markdown index ca4d9b9a17bf..12d23db99498 100644 --- a/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.markdown +++ b/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.markdown @@ -83,7 +83,7 @@ Arranging the terms: \f$r = x \cos \theta + y \sin \theta\f$ ### Standard and Probabilistic Hough Line Transform -OpenCV implements two kind of Hough Line Transforms: +OpenCV implements three kind of Hough Line Transforms: a. **The Standard Hough Transform** @@ -97,6 +97,12 @@ b. **The Probabilistic Hough Line Transform** of the detected lines \f$(x_{0}, y_{0}, x_{1}, y_{1})\f$ - In OpenCV it is implemented with the function **HoughLinesP()** +c. **The Weighted Hough Transform** + +- Uses edge intensity instead binary 0 or 1 values in standard Hough transform. +- In OpenCV it is implemented with the function **HoughLines()** with use_edgeval=true. +- See the example in samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp. + ### What does this program do? - Loads an image - Applies a *Standard Hough Line Transform* and a *Probabilistic Line Transform*. diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index bb0cab7426d8..1010d1d256c8 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2165,11 +2165,13 @@ Must fall between 0 and max_theta. @param max_theta For standard and multi-scale Hough transform, an upper bound for the angle. Must fall between min_theta and CV_PI. The actual maximum angle in the accumulator may be slightly less than max_theta, depending on the parameters min_theta and theta. +@param use_edgeval True if you want to use weighted Hough transform. */ CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn = 0, double stn = 0, - double min_theta = 0, double max_theta = CV_PI ); + double min_theta = 0, double max_theta = CV_PI, + bool use_edgeval = false ); /** @brief Finds line segments in a binary image using the probabilistic Hough transform. diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index 523e4491c5f3..1c52689da589 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -120,7 +120,7 @@ static void HoughLinesStandard( InputArray src, OutputArray lines, int type, float rho, float theta, int threshold, int linesMax, - double min_theta, double max_theta ) + double min_theta, double max_theta, bool use_edgeval = false ) { CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Internal error"); @@ -184,17 +184,31 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type, irho, tabSin, tabCos); // stage 1. fill accumulator - for( i = 0; i < height; i++ ) - for( j = 0; j < width; j++ ) - { - if( image[i * step + j] != 0 ) - for(int n = 0; n < numangle; n++ ) - { - int r = cvRound( j * tabCos[n] + i * tabSin[n] ); - r += (numrho - 1) / 2; - accum[(n+1) * (numrho+2) + r+1]++; - } - } + if (use_edgeval) { + for( i = 0; i < height; i++ ) + for( j = 0; j < width; j++ ) + { + if( image[i * step + j] != 0 ) + for(int n = 0; n < numangle; n++ ) + { + int r = cvRound( j * tabCos[n] + i * tabSin[n] ); + r += (numrho - 1) / 2; + accum[(n + 1) * (numrho + 2) + r + 1] += image[i * step + j]; + } + } + } else { + for( i = 0; i < height; i++ ) + for( j = 0; j < width; j++ ) + { + if( image[i * step + j] != 0 ) + for(int n = 0; n < numangle; n++ ) + { + int r = cvRound( j * tabCos[n] + i * tabSin[n] ); + r += (numrho - 1) / 2; + accum[(n + 1) * (numrho + 2) + r + 1]++; + } + } + } // stage 2. find local maximums findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf ); @@ -907,7 +921,7 @@ static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, dou void HoughLines( InputArray _image, OutputArray lines, double rho, double theta, int threshold, - double srn, double stn, double min_theta, double max_theta ) + double srn, double stn, double min_theta, double max_theta, bool use_edgeval ) { CV_INSTRUMENT_REGION(); @@ -922,7 +936,7 @@ void HoughLines( InputArray _image, OutputArray lines, ocl_HoughLines(_image, lines, rho, theta, threshold, min_theta, max_theta)); if( srn == 0 && stn == 0 ) - HoughLinesStandard(_image, lines, type, (float)rho, (float)theta, threshold, INT_MAX, min_theta, max_theta ); + HoughLinesStandard(_image, lines, type, (float)rho, (float)theta, threshold, INT_MAX, min_theta, max_theta, use_edgeval ); else HoughLinesSDiv(_image, lines, type, (float)rho, (float)theta, threshold, cvRound(srn), cvRound(stn), INT_MAX, min_theta, max_theta); } diff --git a/modules/imgproc/test/test_houghlines.cpp b/modules/imgproc/test/test_houghlines.cpp index 2d784d7a7ac0..02eb2d4379e7 100644 --- a/modules/imgproc/test/test_houghlines.cpp +++ b/modules/imgproc/test/test_houghlines.cpp @@ -340,6 +340,53 @@ TEST(HoughLines, regression_21983) EXPECT_NEAR(lines[0][1], 1.57179642, 1e-4); } +TEST(WeightedHoughLines, horizontal) +{ + Mat img(25, 25, CV_8UC1, Scalar(0)); + // draw lines. from top to bottom, stronger to weaker. + line(img, Point(0, 6), Point(25, 6), Scalar(240)); + line(img, Point(0, 12), Point(25, 12), Scalar(255)); + line(img, Point(0, 18), Point(25, 18), Scalar(220)); + + // detect lines + std::vector lines; + int threshold{220*25-1}; + bool use_edgeval{true}; + HoughLines(img, lines, 1, CV_PI/180, threshold, 0, 0, 0.0, CV_PI, use_edgeval); + + // check results + ASSERT_EQ(3U, lines.size()); + // detected lines is assumed sorted from stronger to weaker. + EXPECT_EQ(12, lines[0][0]); + EXPECT_EQ(6, lines[1][0]); + EXPECT_EQ(18, lines[2][0]); + EXPECT_NEAR(CV_PI/2, lines[0][1], CV_PI/180 + 1e-6); + EXPECT_NEAR(CV_PI/2, lines[1][1], CV_PI/180 + 1e-6); + EXPECT_NEAR(CV_PI/2, lines[2][1], CV_PI/180 + 1e-6); +} + +TEST(WeightedHoughLines, diagonal) +{ + Mat img(25, 25, CV_8UC1, Scalar(0)); + // draw lines. + line(img, Point(0, 0), Point(25, 25), Scalar(128)); + line(img, Point(0, 25), Point(25, 0), Scalar(255)); + + // detect lines + std::vector lines; + int threshold{128*25-1}; + bool use_edgeval{true}; + HoughLines(img, lines, 1, CV_PI/180, threshold, 0, 0, 0.0, CV_PI, use_edgeval); + + // check results + ASSERT_EQ(2U, lines.size()); + // detected lines is assumed sorted from stronger to weaker. + EXPECT_EQ(18, lines[0][0]); // 25*sqrt(2)/2 = 17.67 ~ 18 + EXPECT_EQ(0, lines[1][0]); + EXPECT_NEAR(CV_PI/4, lines[0][1], CV_PI/180 + 1e-6); + EXPECT_NEAR(CV_PI*3/4, lines[1][1], CV_PI/180 + 1e-6); +} + 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 ), diff --git a/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp b/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp index 5d1736a77ad8..91d4266c5a99 100644 --- a/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp +++ b/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp @@ -15,22 +15,27 @@ using namespace std; /// Global variables /** General variables */ -Mat src, edges; +Mat src, canny_edge, sobel_edge; Mat src_gray; -Mat standard_hough, probabilistic_hough; +Mat standard_hough, probabilistic_hough, weighted_hough; int min_threshold = 50; int max_trackbar = 150; +int weightedhough_max_trackbar = 100000; const char* standard_name = "Standard Hough Lines Demo"; const char* probabilistic_name = "Probabilistic Hough Lines Demo"; +const char* weighted_name = "Weighted Hough Lines Demo"; int s_trackbar = max_trackbar; int p_trackbar = max_trackbar; +int e_trackbar = 60; +int w_trackbar = 60000; /// Function Headers void help(); void Standard_Hough( int, void* ); void Probabilistic_Hough( int, void* ); +void Weighted_Hough( int, void* ); /** * @function main @@ -53,22 +58,29 @@ int main( int argc, char** argv ) /// Pass the image to gray cvtColor( src, src_gray, COLOR_RGB2GRAY ); - /// Apply Canny edge detector - Canny( src_gray, edges, 50, 200, 3 ); + /// Apply Canny/Sobel edge detector + Canny( src_gray, canny_edge, 50, 200, 3 ); + Sobel( src_gray, sobel_edge, CV_16S, 1, 0 ); // dx(order of the derivative x)=1,dy=0 /// Create Trackbars for Thresholds char thresh_label[50]; snprintf( thresh_label, sizeof(thresh_label), "Thres: %d + input", min_threshold ); - namedWindow( standard_name, WINDOW_AUTOSIZE ); - createTrackbar( thresh_label, standard_name, &s_trackbar, max_trackbar, Standard_Hough); + createTrackbar( thresh_label, standard_name, &s_trackbar, max_trackbar, Standard_Hough ); namedWindow( probabilistic_name, WINDOW_AUTOSIZE ); - createTrackbar( thresh_label, probabilistic_name, &p_trackbar, max_trackbar, Probabilistic_Hough); + createTrackbar( thresh_label, probabilistic_name, &p_trackbar, max_trackbar, Probabilistic_Hough ); + + char edge_thresh_label[50]; + sprintf( edge_thresh_label, "Edge Thres: input" ); + namedWindow( weighted_name, WINDOW_AUTOSIZE); + createTrackbar( edge_thresh_label, weighted_name, &e_trackbar, max_trackbar, Weighted_Hough); + createTrackbar( thresh_label, weighted_name, &w_trackbar, weightedhough_max_trackbar, Weighted_Hough); /// Initialize Standard_Hough(0, 0); Probabilistic_Hough(0, 0); + Weighted_Hough(0, 0); waitKey(0); return 0; } @@ -90,10 +102,10 @@ void help() void Standard_Hough( int, void* ) { vector s_lines; - cvtColor( edges, standard_hough, COLOR_GRAY2BGR ); + cvtColor( canny_edge, standard_hough, COLOR_GRAY2BGR ); /// 1. Use Standard Hough Transform - HoughLines( edges, s_lines, 1, CV_PI/180, min_threshold + s_trackbar, 0, 0 ); + HoughLines( canny_edge, s_lines, 1, CV_PI/180, min_threshold + s_trackbar, 0, 0 ); /// Show the result for( size_t i = 0; i < s_lines.size(); i++ ) @@ -117,10 +129,10 @@ void Standard_Hough( int, void* ) void Probabilistic_Hough( int, void* ) { vector p_lines; - cvtColor( edges, probabilistic_hough, COLOR_GRAY2BGR ); + cvtColor( canny_edge, probabilistic_hough, COLOR_GRAY2BGR ); /// 2. Use Probabilistic Hough Transform - HoughLinesP( edges, p_lines, 1, CV_PI/180, min_threshold + p_trackbar, 30, 10 ); + HoughLinesP( canny_edge, p_lines, 1, CV_PI/180, min_threshold + p_trackbar, 30, 10 ); /// Show the result for( size_t i = 0; i < p_lines.size(); i++ ) @@ -131,3 +143,38 @@ void Probabilistic_Hough( int, void* ) imshow( probabilistic_name, probabilistic_hough ); } + +/** + * @function Weighted_Hough + * This can detect lines based on the edge intensities. + */ +void Weighted_Hough( int, void* ) +{ + vector s_lines; + + /// prepare + Mat edge_img; + convertScaleAbs(sobel_edge, edge_img ); + // use same threshold for edge with Hough. + threshold( edge_img, edge_img, e_trackbar, 255, cv::THRESH_TOZERO); + cvtColor( edge_img, weighted_hough, COLOR_GRAY2BGR ); + + /// 3. Use Weighted Hough Transform + const bool use_edgeval{true}; + HoughLines( edge_img, s_lines, 1, CV_PI/180, min_threshold + w_trackbar, 0, 0, 0, CV_PI, use_edgeval); + + /// Show the result + for( size_t i = 0; i < s_lines.size(); i++ ) + { + float r = s_lines[i][0], t = s_lines[i][1]; + double cos_t = cos(t), sin_t = sin(t); + double x0 = r*cos_t, y0 = r*sin_t; + double alpha = 1000; + + Point pt1( cvRound(x0 + alpha*(-sin_t)), cvRound(y0 + alpha*cos_t) ); + Point pt2( cvRound(x0 - alpha*(-sin_t)), cvRound(y0 - alpha*cos_t) ); + line( weighted_hough, pt1, pt2, Scalar(255,0,0), 3, LINE_AA ); + } + + imshow( weighted_name, weighted_hough ); +}