您的位置:首页 > 汽车 > 时评 > 【OpenCV C++20 学习笔记】霍夫直线变换-Hough Line Transform

【OpenCV C++20 学习笔记】霍夫直线变换-Hough Line Transform

2024/11/16 4:07:19 来源:https://blog.csdn.net/TeamLee/article/details/140975861  浏览:    关键词:【OpenCV C++20 学习笔记】霍夫直线变换-Hough Line Transform

霍夫线条变换

  • 原理
    • 标准和概率霍夫变换
  • API
    • 标准霍夫变换
    • 概率霍夫直线变换
  • 实例

原理

霍夫直线变换一般用来检测直线,它要在边缘检测之后才能应用。

在数学上要定义一条直线通常有2种方法:

  1. 笛卡尔坐标系:两点确定一条直线,即 ( x 0 , y 0 ) , ( x 1 , y 1 ) (x_0,y_0), (x_1, y_1) (x0,y0),(x1,y1)
  2. 极坐标:极角和极径确定一个向量 ( 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θ=x0cosθ+y0sinθ(1)

r θ r_{\theta} rθ表示垂直于直线的极径, θ \theta θ表示极径的极角,如下图:
极坐标中的直线

对于任何确定的点 ( 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时,可以得到下图:
极角与极径的关系

这里只考虑 r > 0 r>0 r>0 0 < θ < 2 π 0<\theta<2 \pi 0<θ<2π的情况

在图片操作中,也是如此,每个像素的 ( x , y ) (x,y) (x,y)是确定的,因此对多个像素点进行霍夫变换就可以得到一组正弦曲线。如果这些曲线有交叉,说明这些像素点在同一条直线上。比如,在上图中在加上点 x 1 = 4 , y − 1 = 9 x_1 = 4, y-1=9 x1=4,y1=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

从上面的例子可以看到,通过正弦曲线的交点可以找到对应点的共同直线。在某个交点相交的曲线越多,说明这个交点对应的直线经过更多的点。这样的话,就可以设置一个阈值来确定相交的曲线的最小数量。

以上就是霍夫直线变换的思路。总结定义如下:

  1. 霍夫空间中的所有点称为accumulator
  2. 每个点上经过的曲线的数量,即累计器的值称为votes
  3. 曲线交点的纵坐标,即上例中的极径 r θ r_{\theta} rθ,定义为rho
  4. 曲线交点的横坐标,即上例中的极角 θ \theta θ,定义为theta

标准和概率霍夫变换

OpenCV实现了两种霍夫直线变换:

  1. 标准霍夫变换:
  • 包括了上述例子中的大部分步骤,并最终给出向量 ( θ , r θ ) (\theta, r_{\theta}) (θ,rθ)的结果,需要画出检测出的直线,需要自己求出 ( θ , r θ ) (\theta, r_{\theta}) (θ,rθ)对应的直线上的两个点;
  • 在OpenCV中使用HoughLines()函数实现
  1. 概率霍夫直线变换
  • 霍夫直线变换的更有效率的版本,最终给出直线的两个端点 ( 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)	//检测直线的最大角度,不超过一个圆周率
  • 返回的数组lines中为检测到的线条的向量,每个线条向量都有2-3个元素,即 ( ρ , θ ) (\rho, \theta) (ρ,θ) ( ρ , θ , v o t e s ) (\rho, \theta, votes) (ρ,θ,votes)
  • rhotheta分别为上述霍夫空间中的纵横坐标的最小单位,这两个参数确定了霍夫空间中的accumulator的精度,即rhotheta越小,霍夫空间中的accumulator就越多。
  • srnstn分别为rho和theta的除数。在经典霍夫变换中,rho和theta就是累加器accumulator的极径和极角,但是在多尺度霍夫变换中,精确的accumulator的极径和极角则分别为 r h o / s r n , t h e t a / s t n rho/srn,theta/stn rho/srntheta/stn。如果将这个两个参数都设置为0,则使用的就是经典霍夫变换;如果设为正数,则为多尺度霍夫变换。
  • 只有 v o t e s > t h r e s h o l d votes>threshold votes>threshold的线条才会被返回到输出结果中

概率霍夫直线变换

void cv::HoughLinesP(	InputArray		image,				//8位单通道二值化原图(即,经过边缘检测后的图片)OutiputArray	lines,				//线条向量数组double			rho,				//霍夫空间中纵坐标的最小单位double			theta,				//霍夫空间中横坐标的最小单位int				threshold,			//votes的阈值double			minLineLength = 0,	//直线识别的最短长度double			maxLineGap = 0)		//同一直线上的最大间隔
  • 返回的数组lines中为检测到的线条的向量,每个线条向量都有4个元素, ( x 1 , y 1 , x 2 , y 2 ) (x_1, y_1, x_2, y_2) (x1,y1,x2,y2),即直线的两个端点的坐标
  • miniLineLength参数确定了最少多长的直线能放到检测结果中,如果没有达到这个长度,就不会识别为有效直线,默认值为0,即只要有长度都会被识别;
  • maxLineGap参数确定在一条直线上的同一系列点之间,多大的距离才不会被用同一条直线连接;如果两个点之间的距离大于这个参数,即使它们属于同一条直线也不会连在一起。

实例

对示例图片:"..\opencv\sources\samples\data\sudoku.png"进行直线检测:

  1. 导入图片后灰度化(可选),然后进行边缘检测;
  2. 接着进行标准霍夫直线变换,并将直线检测结果绘制在边缘检测后的图上;
  3. 最后进行概率霍夫直线变换,并同样将结果绘制在边缘检测后的图上。

具体实现件代码及注释:

#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 = 1CV_PI / 180,	//theta = 1弧度制150,			//votes阈值0,				//srn默认为00);				//stn默认为0Mat 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 = 1CV_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);
}

运行结果:
直线检测结果
中间是标准霍夫变换,右边是概率霍夫变换。
可以看到因为标准霍夫变换需要我们自己来确定直线的端点,所以一般只能将整条直线都连起来;但是概率霍夫变换能够根据图片中的信息确定直线的起点和终点。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com