霍夫变换是图像处理中识别几何形状的一种方法, 主要用来检测直线和圆形。
1 平面坐标和极坐标
1) 平面坐标的点 <=> 极坐标(平面化)的曲线
所谓极坐标平面化是指, 将ρ-θ的关系像x-y那样在平面内展开。
公式推导: x-y坐标中的点(x0, y0), 代入极坐标ρ-θ中得
由上述公式可以明显的看出ρ与θ之间的函数关系。
2) 极坐标(平面化)的点 <=> 平面坐标的直线
公式推导: ρ-θ极坐标中的点(ρ0, θ0), 代入平面坐标x-y中得
由此可知, 极坐标中的一个确定点(ρ0, θ0), 对应于平面坐标内的一条直线。若极坐标中有N条曲线相交于一点(如下左图), 则说明在平面坐标中, 该点对应的直线包含N条曲线对应的N个点, 也即这N个点组成了一条直线。
上面左图中, 是平面坐标中的三个点 (8,6), (4, 9), (12,3) 分别对应的三条曲线。
因此, 霍夫变换的原理就是将平面坐标内的直线检测, 转换为极坐标内的交点检测, 显然寻找一个交点要比寻找直线(实际上是寻找许多点)容易得多了。
霍夫变换的具体算法, 就属于学术范畴了, 本人就不求甚解了, 感兴趣的可以去看相关的论文或者OpenCV的源码。
2 OpenCV中的函数
摘自OpenCV 3.1.0中HoughLines和HoughLinesP两个函数的介绍。
http://docs.opencv.org/3.1.0/d9/db0/tutorial_hough_lines.html#gsc.tab=0
1) HoughLines
void cv::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 |
||
) |
image | 8-bit, single-channel binary source image. The image may be modified by the function. |
lines | Output vector of lines. Each line is represented by a two-element vector (ρ,θ). ρ is the distance from the cordinate origin(0,0)(top-left corner of the image). θ is the line rotation angle in radians(0 ~ vertical line, Π/2 ~ horizontal line) |
rho | Distance resolution of the accumulator in pixels. |
theta | Angle resolution of the accumulator in radians. |
threshold | Accumulator threshold parameter. Only those lines are returned that get enough votes(>threshold). |
srn | For the multi-scale Hough transform, it is a divisor for the distance resolution rho. The coarse accumulator distance resolution is rho and the accurate accumulator resolution is rho/srn. If both srn=0 and stn=0, the classical Hough transform is used. Otherwise, both these parameters should be positive. |
stn | For the multi-scale Hough transform, it is a divisor for the distance resolution theta. |
min_theta | For standard and multi-scale Hough transform, minimum angle to check for lines. Must fall between 0 and max_theta. |
max_theta | For standard and multi-scale Hough transform, maximum angle to check for lines. Must fall between min_theta and CV_PI. |
2) HoughLinesP
void cv::HoughLinesP | ( | InputArray | image, |
OutputArray | lines, | ||
double | rho, | ||
double | theta, | ||
int | threshold, | ||
double | minLineLength = 0 , |
||
double | maxLineGap = 0 |
||
) |
Parameters
image | 8-bit, single-channel binary source image. The image may be modified by the function. |
lines | Output vector of lines. Each line is represented by a 4-element vector x1, y1, x2,y2), where (x1, y1) and (x2, y2) are the ending points of each detected line segment. |
rho | Distance resolution of the accumulator in pixels. |
theta | Angle resolution of the accumulator in radians. |
threshold | Accumulator threshold parameter. Only those lines are returned that get enough votes(>threshold). |
minLineLength | Minimum line length. Line segments shorter than that are rejected. |
MaxLineGap | Maximum allowed gap between points on the same line to link them. |
3 范例
1) 简单
1 #include "opencv2/imgcodecs.hpp" 2 #include "opencv2/highgui/highgui.hpp" 3 #include "opencv2/imgproc/imgproc.hpp" 4 5 #include <iostream> 6 7 using namespace cv; 8 using namespace std; 9 10 static void help() 11 { 12 cout << "\nThis program demonstrates line finding with the Hough transform.\n" 13 "Usage:\n" 14 "./houghlines <image_name>, Default is ../data/pic1.png\n" << endl; 15 } 16 17 int main(int argc, char** argv) 18 { 19 cv::CommandLineParser parser(argc, argv, 20 "{help h||}{@image|../data/pic1.png|}" 21 ); 22 if (parser.has("help")) 23 { 24 help(); 25 return 0; 26 } 27 string filename = parser.get<string>("@image"); 28 if (filename.empty()) 29 { 30 help(); 31 cout << "no image_name provided" << endl; 32 return -1; 33 } 34 Mat src = imread(filename, 0); 35 if(src.empty()) 36 { 37 help(); 38 cout << "can not open " << filename << endl; 39 return -1; 40 } 41 42 Mat dst, cdst; 43 Canny(src, dst, 50, 200, 3); 44 cvtColor(dst, cdst, COLOR_GRAY2BGR); 45 46 #if 0 47 vector<Vec2f> lines; 48 HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 ); 49 for( size_t i = 0; i < lines.size(); i++ ) 50 { 51 float rho = lines[i][0], theta = lines[i][1]; 52 Point pt1, pt2; 53 double a = cos(theta), b = sin(theta); 54 double x0 = a*rho, y0 = b*rho; 55 pt1.x = cvRound(x0 + 1000*(-b)); 56 pt1.y = cvRound(y0 + 1000*(a)); 57 pt2.x = cvRound(x0 - 1000*(-b)); 58 pt2.y = cvRound(y0 - 1000*(a)); 59 line( cdst, pt1, pt2, Scalar(0,0,255), 3, CV_AA); 60 } 61 #else 62 vector<Vec4i> lines; 63 HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 ); 64 for( size_t i = 0; i < lines.size(); i++ ) 65 { 66 Vec4i l = lines[i]; 67 line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA); 68 } 69 #endif 70 imshow("source", src); 71 imshow("detected lines", cdst); 72 73 waitKey(); 74 75 return 0; 76 }
2) 复杂
摘自 https://github.com/Itseez/opencv/blob/master/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp
1 /** 2 * @file HoughLines_Demo.cpp 3 * @brief Demo code for Hough Transform 4 * @author OpenCV team 5 */ 6 7 #include "opencv2/imgcodecs.hpp" 8 #include "opencv2/highgui/highgui.hpp" 9 #include "opencv2/imgproc/imgproc.hpp" 10 #include <iostream> 11 #include <stdio.h> 12 13 using namespace cv; 14 using namespace std; 15 16 /// Global variables 17 18 /** General variables */ 19 Mat src, edges; 20 Mat src_gray; 21 Mat standard_hough, probabilistic_hough; 22 int min_threshold = 50; 23 int max_trackbar = 150; 24 25 const char* standard_name = "Standard Hough Lines Demo"; 26 const char* probabilistic_name = "Probabilistic Hough Lines Demo"; 27 28 int s_trackbar = max_trackbar; 29 int p_trackbar = max_trackbar; 30 31 /// Function Headers 32 void help(); 33 void Standard_Hough( int, void* ); 34 void Probabilistic_Hough( int, void* ); 35 36 /** 37 * @function main 38 */ 39 int main( int, char** argv ) 40 { 41 /// Read the image 42 src = imread( argv[1], 1 ); 43 44 if( src.empty() ) 45 { help(); 46 return -1; 47 } 48 49 /// Pass the image to gray 50 cvtColor( src, src_gray, COLOR_RGB2GRAY ); 51 52 /// Apply Canny edge detector 53 Canny( src_gray, edges, 50, 200, 3 ); 54 55 /// Create Trackbars for Thresholds 56 char thresh_label[50]; 57 sprintf( thresh_label, "Thres: %d + input", min_threshold ); 58 59 namedWindow( standard_name, WINDOW_AUTOSIZE ); 60 createTrackbar( thresh_label, standard_name, &s_trackbar, max_trackbar, Standard_Hough); 61 62 namedWindow( probabilistic_name, WINDOW_AUTOSIZE ); 63 createTrackbar( thresh_label, probabilistic_name, &p_trackbar, max_trackbar, Probabilistic_Hough); 64 65 /// Initialize 66 Standard_Hough(0, 0); 67 Probabilistic_Hough(0, 0); 68 waitKey(0); 69 return 0; 70 } 71 72 /** 73 * @function help 74 * @brief Indications of how to run this program and why is it for 75 */ 76 void help() 77 { 78 printf("\t Hough Transform to detect lines \n "); 79 printf("\t---------------------------------\n "); 80 printf(" Usage: ./HoughLines_Demo <image_name> \n"); 81 } 82 83 /** 84 * @function Standard_Hough 85 */ 86 void Standard_Hough( int, void* ) 87 { 88 vector<Vec2f> s_lines; 89 cvtColor( edges, standard_hough, COLOR_GRAY2BGR ); 90 91 /// 1. Use Standard Hough Transform 92 HoughLines( edges, s_lines, 1, CV_PI/180, min_threshold + s_trackbar, 0, 0 ); 93 94 /// Show the result 95 for( size_t i = 0; i < s_lines.size(); i++ ) 96 { 97 float r = s_lines[i][0], t = s_lines[i][1]; 98 double cos_t = cos(t), sin_t = sin(t); 99 double x0 = r*cos_t, y0 = r*sin_t; 100 double alpha = 1000; 101 102 Point pt1( cvRound(x0 + alpha*(-sin_t)), cvRound(y0 + alpha*cos_t) ); 103 Point pt2( cvRound(x0 - alpha*(-sin_t)), cvRound(y0 - alpha*cos_t) ); 104 line( standard_hough, pt1, pt2, Scalar(255,0,0), 3, LINE_AA); 105 } 106 107 imshow( standard_name, standard_hough ); 108 } 109 110 /** 111 * @function Probabilistic_Hough 112 */ 113 void Probabilistic_Hough( int, void* ) 114 { 115 vector<Vec4i> p_lines; 116 cvtColor( edges, probabilistic_hough, COLOR_GRAY2BGR ); 117 118 /// 2. Use Probabilistic Hough Transform 119 HoughLinesP( edges, p_lines, 1, CV_PI/180, min_threshold + p_trackbar, 30, 10 ); 120 121 /// Show the result 122 for( size_t i = 0; i < p_lines.size(); i++ ) 123 { 124 Vec4i l = p_lines[i]; 125 line( probabilistic_hough, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255,0,0), 3, LINE_AA); 126 } 127 128 imshow( probabilistic_name, probabilistic_hough ); 129 }