0
点赞
收藏
分享

微信扫一扫

51单片机-LED实验二

程序员漫画编程 2024-08-12 阅读 32

霍夫线条变换

原理

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

在数学上要定义一条直线通常有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)

对于任何确定的点 ( 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,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)	//检测直线的最大角度,不超过一个圆周率

概率霍夫直线变换

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"进行直线检测:

  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 = 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);
}

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

举报

相关推荐

0 条评论