霍夫变换的原理:
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、代码:
#includeusing 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); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)