霍夫线条变换
原理
霍夫直线变换一般用来检测直线,它要在边缘检测之后才能应用。
在数学上要定义一条直线通常有2种方法:
- 笛卡尔坐标系:两点确定一条直线,即 ( x 0 , y 0 ) , ( x 1 , y 1 ) (x_0,y_0), (x_1, y_1) (x0,y0),(x1,y1);
 - 极坐标:极角和极径确定一个向量 
      
       
        
        
          ( 
         
        
          r 
         
        
          , 
         
        
          θ 
         
        
          ) 
         
        
       
         (r,\theta) 
        
       
     (r,θ),与该向量垂直,且经过其端点的直线只有一条,即:
y = ( − cos  θ sin  θ ) x + ( r sin  θ ) y=(-\frac{\cos \theta}{\sin \theta})x+(\frac{r}{\sin \theta}) y=(−sinθcosθ)x+(sinθr)
该式变换可得:
r = x cos  θ + y sin  θ r=x \cos \theta + y \sin \theta r=xcosθ+ysinθ
因此对于极坐标系中的任意一点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),经过该点的所有直线都适用下面的等式:
r θ = x 0 ⋅ cos  θ + y 0 ⋅ sin  θ ( 1 ) r_{\theta}=x_0 \cdot \cos \theta + y_0 \cdot \sin \theta \qquad(1) rθ=x0⋅cosθ+y0⋅sinθ(1) 
对于任何确定的点 
     
      
       
       
         ( 
        
        
        
          x 
         
        
          0 
         
        
       
         , 
        
        
        
          y 
         
        
          0 
         
        
       
         ) 
        
       
      
        (x_0,y_0) 
       
      
    (x0,y0), 
     
      
       
       
         ( 
        
       
         1 
        
       
         ) 
        
       
      
        (1) 
       
      
    (1)式中的 
     
      
       
       
         x 
        
       
         , 
        
       
         y 
        
       
      
        x, y 
       
      
    x,y都是确定的,但是 
     
      
       
        
        
          r 
         
        
          θ 
         
        
       
      
        r_{\theta} 
       
      
    rθ和 
     
      
       
       
         θ 
        
       
      
        \theta 
       
      
    θ是有很多解的,如果将这些解画出来,以 
     
      
       
       
         θ 
        
       
      
        \theta 
       
      
    θ为横坐标、 
     
      
       
        
        
          r 
         
        
          θ 
         
        
       
      
        r_{\theta} 
       
      
    rθ为纵坐标作图,可以得到一个正弦曲线,这就是霍夫变换,这个坐标系也叫作霍夫空间。比如,当 
     
      
       
        
        
          x 
         
        
          0 
         
        
       
         = 
        
       
         8 
        
       
         , 
        
        
        
          y 
         
        
          0 
         
        
       
         = 
        
       
         6 
        
       
      
        x_0=8, y_0=6 
       
      
    x0=8,y0=6时,可以得到下图:
 
在图片操作中,也是如此,每个像素的 
     
      
       
       
         ( 
        
       
         x 
        
       
         , 
        
       
         y 
        
       
         ) 
        
       
      
        (x,y) 
       
      
    (x,y)是确定的,因此对多个像素点进行霍夫变换就可以得到一组正弦曲线。如果这些曲线有交叉,说明这些像素点在同一条直线上。比如,在上图中在加上点 
     
      
       
        
        
          x 
         
        
          1 
         
        
       
         = 
        
       
         4 
        
       
         , 
        
       
         y 
        
       
         − 
        
       
         1 
        
       
         = 
        
       
         9 
        
       
      
        x_1 = 4, y-1=9 
       
      
    x1=4,y−1=9和 
     
      
       
        
        
          x 
         
        
          2 
         
        
       
         = 
        
       
         12 
        
       
         , 
        
        
        
          y 
         
        
          2 
         
        
       
         = 
        
       
         3 
        
       
      
        x_2=12, y_2=3 
       
      
    x2=12,y2=3的正弦曲线,可以得到下图:
 
 可以看到3条曲线都相交于点 
     
      
       
       
         ( 
        
       
         0.925 
        
       
         , 
        
       
         9.6 
        
       
         ) 
        
       
      
        (0.925, 9.6) 
       
      
    (0.925,9.6),也就是说点 
     
      
       
       
         ( 
        
        
        
          x 
         
        
          0 
         
        
       
         , 
        
        
        
          y 
         
        
          0 
         
        
       
         ) 
        
       
         , 
        
       
         ( 
        
        
        
          x 
         
        
          1 
         
        
       
         , 
        
        
        
          y 
         
        
          1 
         
        
       
         ) 
        
       
         , 
        
       
         ( 
        
        
        
          x 
         
        
          2 
         
        
       
         , 
        
        
        
          y 
         
        
          2 
         
        
       
         ) 
        
       
      
        (x_0, y_0), (x_1, y_1), (x_2, y_2) 
       
      
    (x0,y0),(x1,y1),(x2,y2)都在同一条直线上,而这条直线对应的极角和极径分别为: 
     
      
       
       
         θ 
        
       
         = 
        
       
         0.925 
        
       
         π 
        
       
         , 
        
        
        
          r 
         
        
          θ 
         
        
       
         = 
        
       
         9.6 
        
       
      
        \theta = 0.925 \pi, r_{\theta}=9.6 
       
      
    θ=0.925π,rθ=9.6。
从上面的例子可以看到,通过正弦曲线的交点可以找到对应点的共同直线。在某个交点相交的曲线越多,说明这个交点对应的直线经过更多的点。这样的话,就可以设置一个阈值来确定相交的曲线的最小数量。
以上就是霍夫直线变换的思路。总结定义如下:
- 霍夫空间中的所有点称为accumulator;
 - 每个点上经过的曲线的数量,即累计器的值称为votes;
 - 曲线交点的纵坐标,即上例中的极径 r θ r_{\theta} rθ,定义为rho
 - 曲线交点的横坐标,即上例中的极角 θ \theta θ,定义为theta
 
标准和概率霍夫变换
OpenCV实现了两种霍夫直线变换:
- 标准霍夫变换:
 
- 包括了上述例子中的大部分步骤,并最终给出向量 ( θ , r θ ) (\theta, r_{\theta}) (θ,rθ)的结果,需要画出检测出的直线,需要自己求出 ( θ , r θ ) (\theta, r_{\theta}) (θ,rθ)对应的直线上的两个点;
 - 在OpenCV中使用
HoughLines()函数实现 
- 概率霍夫直线变换
 
- 霍夫直线变换的更有效率的版本,最终给出直线的两个端点 ( x 0 , y 0 , x 1 , y 1 ) (x_0, y_0, x_1, y_1) (x0,y0,x1,y1),可以直接根据这两个坐标画线;
 - 在OpenCV中使用
HoughLinesP()函数实现 
API
上述OpenCV中的2个函数的原型分别如下:
标准霍夫变换
void cv::HoughLines(InputArray	image,				//8位单通道二值化原图(即,经过边缘检测后的图片)
					OutputArray	lines,				//线条向量数组
					double		rho,				//霍夫空间中的纵坐标的最小单位
					double		theta,				//霍夫空间中的横坐标的最小单位
					int			threshold,			//accumulator上经过的曲线的数量的阈值,即votes的阈值
					double		srn = 0,			//极径rho的除数
					double		stn = 0,			//极角theta的除数
					double		min_theta = 0,		//检测直线的最小角度
					double		max_theta = CV_PI)	//检测直线的最大角度,不超过一个圆周率
 
 
概率霍夫直线变换
void cv::HoughLinesP(	InputArray		image,				//8位单通道二值化原图(即,经过边缘检测后的图片)
						OutiputArray	lines,				//线条向量数组
						double			rho,				//霍夫空间中纵坐标的最小单位
						double			theta,				//霍夫空间中横坐标的最小单位
						int				threshold,			//votes的阈值
						double			minLineLength = 0,	//直线识别的最短长度
						double			maxLineGap = 0)		//同一直线上的最大间隔
 
 
实例
对示例图片:"..\opencv\sources\samples\data\sudoku.png"进行直线检测:
- 导入图片后灰度化(可选),然后进行边缘检测;
 - 接着进行标准霍夫直线变换,并将直线检测结果绘制在边缘检测后的图上;
 - 最后进行概率霍夫直线变换,并同样将结果绘制在边缘检测后的图上。
 
具体实现件代码及注释:
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace std;
int main() {
	Mat src{ imread("sudoku.png") };
	//Canny边缘检测
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	Mat canny;
	Canny(gray, canny, 50, 200, 3);
	//霍夫变换
	vector<Vec2f> lines;
	HoughLines(canny,	//输入图
		lines,			//线条结果
		1,				//rho = 1
		CV_PI / 180,	//theta = 1弧度制
		150,			//votes阈值
		0,				//srn默认为0
		0);				//stn默认为0
	Mat dst, dstP;
	cvtColor(canny, dst, COLOR_GRAY2BGR);	//转变回BGR图片
	dstP = dst.clone();
	//显示检测到的线条为红色
	for (size_t i{ 0 }; i < lines.size(); i++) {
		float rho{ lines[i][0] }, theta{ lines[i][1] };		//分别获取rho和theta值
		Point pt1, pt2;
		double a{ cos(theta) }, b{ sin(theta) };
		double x0 = a * rho, y0 = b * rho;					//通过rho和theta计算极坐标向量的端点
		//计算直线上两点的坐标
		pt1.x = cvRound(x0 + 1000 * (-b));					
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(dst, pt1, pt2, Scalar(0, 0, 255), 3, LINE_AA);
	}
	//概率霍夫变换
	vector<Vec4i> linesP;
	HoughLinesP(canny,	//输入图
		linesP,			//线条结果
		1,				//rho = 1
		CV_PI / 180,	//theta = 1弧度制	
		50,				//votes阈值
		50,				//直线最短长度
		10);			//直线最大间隔
	//显示结果线条
	for (size_t i{ 0 }; i < linesP.size(); i++) {
		Vec4i l{ linesP[i] };
		line(dstP, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 3, LINE_AA);
	}
	imshow("原图", src);
	imshow("标准霍夫变换", dst);
	imshow("概率霍夫变换", dstP);
	waitKey(0);
}
 
运行结果:
 
 中间是标准霍夫变换,右边是概率霍夫变换。
 可以看到因为标准霍夫变换需要我们自己来确定直线的端点,所以一般只能将整条直线都连起来;但是概率霍夫变换能够根据图片中的信息确定直线的起点和终点。










