OpenCV学习笔记13

OpenCV学习笔记13,第1张

OpenCV学习笔记13

霍夫变换的原理:

1、检测直线

推导过程:

直线y=kx+b为斜率为k截距为b的直线,假设已知一个点(m,n)在直线上,
代入直线方程那么n=mk+b,转换一下形式得k=n/m-b/m,m与n为常数,上式变为k=C1b+C2。
图像为:

同理在(m1,n1)点有k=C3b+C4
图像为:

当k=k0,b=b0时三条线相交,则三点在k=k0,b=b0时候在同一直线上,由此可判断各点是否在一条直线上。

霍夫变换检测直线就是列出所有点的k,b坐标系方程,
找出相交的线,对应的点就在一个直线上。
由于k的范围为[0,无穷),后续投票箱算法无法对无穷的k投票。

所以改为
r=x cos⁡θ+y sin⁡θ
其中r为原点到直线的垂线的长度,θ为r相对于横轴的角度。
具体实现方法分为7步:

1、设置θ与r的步长(r最大值为图像对角线长度);
2、建立r与θ的投票箱(二维数组);
3、根据步长算出每个点的θ值与r值;
4、投票箱一一对应计算出的θ值与r值如果相同则加1;
5、设置数量阈值
6、获取数组中值大于阈值的数的θ与r值;
7、通过当前θ与r画出直线。

注意:需要理解的一点是笛卡尔坐标系下的一条直线的θ与r值(r垂直于直线)在极坐标系下是不变的。

霍夫变换检测直线有两个参数(θ值与r值);
霍夫变换检测圆需要三个参数(圆心坐标值(x0,y0),半径r值)

2、检测圆

步骤为(霍夫梯度法):

1、使用sobel算子得到图像的梯度值,得出梯度方向;
2、在所有检测到的点的在梯度方向上画出直线;
3、建立圆心点坐标投票箱;
4、建立圆心坐标点与半径r的投票箱;
5、设置圆心坐标投票箱的数量阈值;
6、找出直线相交大于阈值的点为待选圆心;
7、计算所有点距离待选圆心的距离;
8、设置圆心与半径投票箱的阈值;
9、非极大值抑制。

OpenCV中的函数:

①普通霍夫变换;

CV_EXPORTS_W void 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 );

参数解释:
1、InputArray image:输入图像,8位的二值图;
2、OutputArray lines:存放2或3个元素的vector,(r,θ )或(r,θ,vote(投票数));
3、double rho:r的最小步长(像素距离分辨率);
4、double theta:θ的最小分辨率,θ的范围为[0,360],一般取1度,也就是pi/180;
5、int threshold:交点个数阈值,小于阈值的交点忽略;
6、double srn = 0:对于多尺度 hough 变换,它是距离分辨率r的除数;
7、double stn = 0:对于多尺度 hough 变换,它是角度分辨率θ的除数;
8,9、double min_theta = 0, double max_theta = CV_PI:θ的最大最小值;

使用步骤:

1、将源图像转化为灰度图,使用边缘检测算子检测边缘并返回二值检测图;

2、设置接收vector,使用HoughLines函数,设置HoughLines函数的参数;

3、接收到lines之后循环将点画到源图像上,具体 *** 作为:

假设已知θ与r,可以求出待求线的垂直交点坐标为r0(rcos(θ),rsin(θ));
根据直线求斜率的一些定理:

而:

假设

(相当于两点间的最小距离设为了1,再扩大n倍,相应坐标也扩大,线段长度也扩大)
则可以在r0点向上或向下找到不同两个在此直线上的点,画出相应的直线;

具体代码:

double x_0 = r*cos(theta);
double y_0 = r*sin(theta);

Point P1, P2;
P1.x = cvRound(x_0 + 1000 * sin(theta));//四舍五入
P1.y = cvRound(y_0 - 1000 * cos(theta));
P2.x = cvRound(x_0 - 1000 * sin(theta));
P2.y = cvRound(y_0 + 1000 * cos(theta));
line(src, P1, P2, Scalar(255, 0, 0));

②累计概率霍夫变换

函数定义:

CV_EXPORTS_W void HoughLinesP( 
InputArray image, 
OutputArray lines,
double rho, double theta, int threshold,
double minLineLength = 0, double maxLineGap = 0 );

参数解释:
1、InputArray image:输入图像,8位的二值图;
2、OutputArray lines:存放4个元素的vector,(x1,y1,x2,y2);
3、double rho:r的最小步长(像素距离分辨率);
4、double theta:θ的最小分辨率,θ的范围为[0,360],一般取1度,也就是pi/180;
5、int threshold:交点个数阈值,小于阈值的交点忽略
6、minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.线段的最小长度
7、maxLineGap:线段上最近两点之间的阈值

因为接收的lines为x1,y1,x2,y2两端点的坐标:
所以可直接使用cv::Point类型设置x与y值;

3、霍夫变换检测圆

函数定义:

CV_EXPORTS_W void HoughCircles( 
InputArray image, OutputArray circles,
int method, double dp, double minDist,
double param1 = 100, double param2 = 100,
int minRadius = 0, int maxRadius = 0 );

参数解释:
1、InputArray image:输入图像为8位单通道灰度图;
2、OutputArray circles:3个或4个元素的vector,(x, y, radius),圆心坐标与半径;
3、int method:使用的运算方法;有霍夫梯度法等
4、Double dp:dp = 1; 输入图像与累加器分辨率的比值,且此参数允许创建一个比输入图像分辨率低的累加器。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
5、Double mindist: 检测到的圆心之间的最小距离。如果参数太小,除了一个真实的圆之外,多个相邻圆可能会被错误地检测到。如果太大的话,有些圆圈可能会漏掉。
6、Double param1:它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
7、Double param2:投票数量阈值,它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
8、Int minradius:最小半径
9、Int maxradius:最大半径

3、代码:

#include 
using namespace std;
using namespace cv;


void main()
{
	Mat src = imread("test_Morphological.png");
	Mat dst,cdst,hough,hough_p,hough_G,src_p, src_c;
	src.copyTo(src_p);
	src.copyTo(src_c);
	cvtColor(src, dst, COLOR_BGR2GRAY);
	Canny(dst, cdst, 50, 200, 3);
//1、霍夫变换检测直线
	vector lines;
	HoughLines(cdst, lines, 1, CV_PI / 180,100);
	for (size_t i = 0 ; i < lines.size();i++)//size返回值为size_t类型
	{
		float r = lines[i][0];//vector中的第一个向量为返回的直线个数,第二个为每个直线的r值与theta值;
		float theta = lines[i][1];//第一个为r值,第二个为theta值;
#if 0
		double k =double(-cos(theta) / sin(theta));
		double b = double( r/ sin(theta));

		printf("%ft%f n", k, b);
		if (k > 0&&b>0)
		{
			line(src, Point(0,cvRound(b)), Point(cvRound((src.rows -b)/k), src.rows), Scalar(0, 255, 0), 1, LINE_AA);
		}
		if (k>0&&b<0)
		{
			line(src, Point( cvRound(b / -k),0), Point(src.cols,cvRound( k*(src.cols+b/k))), Scalar(0, 255, 0), 1, LINE_AA);
		}
		if (k<0&&b>0)
		{
			line(src, Point(0, cvRound(src.rows + (b / -k))), Point(cvRound(src.rows + b), 0), Scalar(0, 255, 0), 1, LINE_AA);
		}
		if (k>=0&&k<2)
		{

			line(src, Point(0, cvRound(b)), Point(src.cols,b), Scalar(0, 255, 0), 1, LINE_AA);

		}
		if (theta==0)//大于一个很大的数,约等于正无穷
		{
			line(src, Point(r, 0), Point(r, src.cols), Scalar(0, 255, 0), 1, LINE_AA);
		}

		if (k<0 && b >src.rows)//大于一个很大的数,约等于正无穷
		{
			line(src, Point(src.cols,cvRound(b +k*src.cols)), Point(cvRound((b-src.rows) / -k), src.rows), Scalar(0, 255, 0), 1, LINE_AA);
		}

		//通过坐标轴相交两点画线(不推荐,感兴趣可以完善一下,我放弃了)
#else 
		double x_0 = r*cos(theta);
		double y_0 = r*sin(theta);

		Point P1, P2;
		P1.x = cvRound(x_0 + 1000 * sin(theta));
		P1.y = cvRound(y_0 - 1000 * cos(theta));
		P2.x = cvRound(x_0 - 1000 * sin(theta));
		P2.y = cvRound(y_0 + 1000 * cos(theta));

		line(src, P1, P2, Scalar(255, 0, 0));


#endif		

	}

	//2、基于概率分布的霍夫变换
	vector lines_p;
	HoughLinesP(cdst, lines_p, 1, CV_PI / 180, 100, 50, 100);

	for (size_t i = 0;i circle;
	
	HoughCircles(dst, circle, HOUGH_GRADIENT,1,200);
	//(x, y, radius)

// 	@param image 8 - bit, single - channel, grayscale input image.
// 	@param circles Output vector of found circles.Each vector is encoded as  3 or 4 element
// 	floating - point vector f$(x, y, radius)f$ or f$(x, y, radius, votes)f$ .
// 	@param method Detection method, see #HoughModes.The available methods are #HOUGH_GRADIENT and #HOUGH_GRADIENT_ALT.
	for (size_t i = 0; i < circle.size(); i++)
	{
		cv::circle(src_c,Point(circle[i][0], circle[i][1]), circle[i][2],Scalar(0,255,255),3);

	}
	imshow("src_c", src_c);
	imshow("src_p", src_p);
	imshow("dst", dst);
	imshow("src", src);
 	waitKey(0);

}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5496139.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-13

发表评论

登录后才能评论

评论列表(0条)

保存