Merge pull request #21407 from sensyn-robotics:feature/weighted_hough

Feature: weighted Hough Transform #21407

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or other license that is incompatible with OpenCV
- [x] The PR is proposed to proper branch
- [x] There is reference to original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Masahiro Ogawa 2025-01-06 21:35:35 +09:00 committed by GitHub
parent 2aee94752a
commit fc994a6ae8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 143 additions and 27 deletions

View File

@ -83,7 +83,7 @@ Arranging the terms: \f$r = x \cos \theta + y \sin \theta\f$
### Standard and Probabilistic Hough Line Transform ### 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** 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$ of the detected lines \f$(x_{0}, y_{0}, x_{1}, y_{1})\f$
- In OpenCV it is implemented with the function **HoughLinesP()** - 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? ### What does this program do?
- Loads an image - Loads an image
- Applies a *Standard Hough Line Transform* and a *Probabilistic Line Transform*. - Applies a *Standard Hough Line Transform* and a *Probabilistic Line Transform*.

View File

@ -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. @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 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. 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, CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold, double rho, double theta, int threshold,
double srn = 0, double stn = 0, 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. /** @brief Finds line segments in a binary image using the probabilistic Hough transform.

View File

@ -120,7 +120,7 @@ static void
HoughLinesStandard( InputArray src, OutputArray lines, int type, HoughLinesStandard( InputArray src, OutputArray lines, int type,
float rho, float theta, float rho, float theta,
int threshold, int linesMax, 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"); CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Internal error");
@ -184,6 +184,7 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type,
irho, tabSin, tabCos); irho, tabSin, tabCos);
// stage 1. fill accumulator // stage 1. fill accumulator
if (use_edgeval) {
for( i = 0; i < height; i++ ) for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ ) for( j = 0; j < width; j++ )
{ {
@ -192,7 +193,20 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type,
{ {
int r = cvRound( j * tabCos[n] + i * tabSin[n] ); int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2; r += (numrho - 1) / 2;
accum[(n+1) * (numrho+2) + r+1]++; 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]++;
}
} }
} }
@ -907,7 +921,7 @@ static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, dou
void HoughLines( InputArray _image, OutputArray lines, void HoughLines( InputArray _image, OutputArray lines,
double rho, double theta, int threshold, 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(); CV_INSTRUMENT_REGION();
@ -922,7 +936,7 @@ void HoughLines( InputArray _image, OutputArray lines,
ocl_HoughLines(_image, lines, rho, theta, threshold, min_theta, max_theta)); ocl_HoughLines(_image, lines, rho, theta, threshold, min_theta, max_theta));
if( srn == 0 && stn == 0 ) 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 else
HoughLinesSDiv(_image, lines, type, (float)rho, (float)theta, threshold, cvRound(srn), cvRound(stn), INT_MAX, min_theta, max_theta); HoughLinesSDiv(_image, lines, type, (float)rho, (float)theta, threshold, cvRound(srn), cvRound(stn), INT_MAX, min_theta, max_theta);
} }

View File

@ -340,6 +340,53 @@ TEST(HoughLines, regression_21983)
EXPECT_NEAR(lines[0][1], 1.57179642, 1e-4); 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<Vec2f> 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<Vec2f> 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" ), INSTANTIATE_TEST_CASE_P( ImgProc, StandartHoughLinesTest, testing::Combine(testing::Values( "shared/pic5.png", "../stitching/a1.png" ),
testing::Values( 1, 10 ), testing::Values( 1, 10 ),
testing::Values( 0.05, 0.1 ), testing::Values( 0.05, 0.1 ),

View File

@ -15,22 +15,27 @@ using namespace std;
/// Global variables /// Global variables
/** General variables */ /** General variables */
Mat src, edges; Mat src, canny_edge, sobel_edge;
Mat src_gray; Mat src_gray;
Mat standard_hough, probabilistic_hough; Mat standard_hough, probabilistic_hough, weighted_hough;
int min_threshold = 50; int min_threshold = 50;
int max_trackbar = 150; int max_trackbar = 150;
int weightedhough_max_trackbar = 100000;
const char* standard_name = "Standard Hough Lines Demo"; const char* standard_name = "Standard Hough Lines Demo";
const char* probabilistic_name = "Probabilistic 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 s_trackbar = max_trackbar;
int p_trackbar = max_trackbar; int p_trackbar = max_trackbar;
int e_trackbar = 60;
int w_trackbar = 60000;
/// Function Headers /// Function Headers
void help(); void help();
void Standard_Hough( int, void* ); void Standard_Hough( int, void* );
void Probabilistic_Hough( int, void* ); void Probabilistic_Hough( int, void* );
void Weighted_Hough( int, void* );
/** /**
* @function main * @function main
@ -53,22 +58,29 @@ int main( int argc, char** argv )
/// Pass the image to gray /// Pass the image to gray
cvtColor( src, src_gray, COLOR_RGB2GRAY ); cvtColor( src, src_gray, COLOR_RGB2GRAY );
/// Apply Canny edge detector /// Apply Canny/Sobel edge detector
Canny( src_gray, edges, 50, 200, 3 ); 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 /// Create Trackbars for Thresholds
char thresh_label[50]; char thresh_label[50];
snprintf( thresh_label, sizeof(thresh_label), "Thres: %d + input", min_threshold ); snprintf( thresh_label, sizeof(thresh_label), "Thres: %d + input", min_threshold );
namedWindow( standard_name, WINDOW_AUTOSIZE ); 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 ); 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 /// Initialize
Standard_Hough(0, 0); Standard_Hough(0, 0);
Probabilistic_Hough(0, 0); Probabilistic_Hough(0, 0);
Weighted_Hough(0, 0);
waitKey(0); waitKey(0);
return 0; return 0;
} }
@ -90,10 +102,10 @@ void help()
void Standard_Hough( int, void* ) void Standard_Hough( int, void* )
{ {
vector<Vec2f> s_lines; vector<Vec2f> s_lines;
cvtColor( edges, standard_hough, COLOR_GRAY2BGR ); cvtColor( canny_edge, standard_hough, COLOR_GRAY2BGR );
/// 1. Use Standard Hough Transform /// 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 /// Show the result
for( size_t i = 0; i < s_lines.size(); i++ ) for( size_t i = 0; i < s_lines.size(); i++ )
@ -117,10 +129,10 @@ void Standard_Hough( int, void* )
void Probabilistic_Hough( int, void* ) void Probabilistic_Hough( int, void* )
{ {
vector<Vec4i> p_lines; vector<Vec4i> p_lines;
cvtColor( edges, probabilistic_hough, COLOR_GRAY2BGR ); cvtColor( canny_edge, probabilistic_hough, COLOR_GRAY2BGR );
/// 2. Use Probabilistic Hough Transform /// 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 /// Show the result
for( size_t i = 0; i < p_lines.size(); i++ ) for( size_t i = 0; i < p_lines.size(); i++ )
@ -131,3 +143,38 @@ void Probabilistic_Hough( int, void* )
imshow( probabilistic_name, probabilistic_hough ); imshow( probabilistic_name, probabilistic_hough );
} }
/**
* @function Weighted_Hough
* This can detect lines based on the edge intensities.
*/
void Weighted_Hough( int, void* )
{
vector<Vec2f> 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 );
}