opencv学习之路(18)、霍夫变换
一、简介
在图像处理和计算机视觉领域中,如何从当前的图像中提取所需要的特征信息是图像识别的关键所在。在许多应用场合中需要快速准确地检测出直线或者圆。其中一种非常有效的解决问题的方法是霍夫(Hough)变换。
二、霍夫线变换
霍夫线变换的原理
以上原理部分,转自http://blog.csdn.net/poem_qianmo/article/details/26977557
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 Mat src=imread("E://1.png"); 7 Mat dst; 8 imshow("src", src); 9 10 Canny(src, src, 50, 200); 11 imshow("Canny", src); 12 //cvtColor(src, dst, CV_BGR2GRAY);//彩色图转灰度图:该语句不能放在canny边缘检测后(canny检测后,图像变为了灰度图) 13 cvtColor(src, dst, CV_GRAY2BGR);//灰度图转彩色图 14 15 //进行霍夫线变换 16 vector<Vec2f> lines; //定义矢量结构lines用于存放得到的线段矢量集合 17 HoughLines(src, lines, 1, CV_PI/180, 150);//超过150的线段才被检测到 18 19 //依次在图中绘制出每条线段 20 for (size_t i = 0; i < lines.size(); i++) 21 { 22 float rho=lines[i][0],theta=lines[i][1]; 23 Point pt1,pt2; 24 double a=cos(theta),b=sin(theta); 25 double x0=a*rho,y0=b*rho; 26 pt1.x=cvRound(x0+1000*(-b));//cvRound(double value) 函数:对一个double型数字四舍五入,返回一个整数 27 pt1.y=cvRound(y0+1000*(a)); 28 pt2.x = cvRound(x0 - 1000*(-b)); 29 pt2.y = cvRound(y0 - 1000*(a)); 30 line(dst,pt1,pt2,Scalar(55,100,195),2,8); 31 } 32 33 imshow("dst", dst); 34 waitKey(0); 35 destroyAllWindows(); 36 }
三、累计概率霍夫变换
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 //HoughLinesP()用法 7 Mat src = imread("E://1.png"); 8 Mat dstImg = src.clone(); 9 imshow("src", src); 10 11 cvtColor(src, src, CV_BGR2GRAY); 12 Canny(src,src, 50, 200); 13 vector<Vec4i> lines; //定义矢量结构lines用于存放得到的线段矢量集合 14 HoughLinesP(src, lines, 1, CV_PI/180, 150, 50, 10); 15 //依次在图中绘制出每条线段 16 for(size_t i = 0; i<lines.size(); i++) 17 { 18 Vec4i p = lines[i]; 19 line(dstImg, Point(p[0], p[1]), Point(p[2], p[3]), Scalar(0, 255, 0), 2, 8);20 } 21 imshow("dst", dstImg); 22 waitKey(0); 23 destroyAllWindows(); 24 }
四、霍夫圆变换
霍夫圆变换的基本原理和上面讲的霍夫线变化大体上是很类似的,只是点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代。说“大体上类似”的原因是,如果完全用相同的方法的话,累加平面会被三维的累加容器所代替:在这三维中,一维是x,一维是y,另外一维是圆的半径r。这就意味着需要大量的内存而且执行效率会很低,速度会很慢。
对直线来说, 一条直线能由参数极径极角表示. 而对圆来说, 我们需要三个参数来表示一个圆, 也就是:
这里的(Xcenter,Ycenter)表示圆心的位置 (下图中的绿点) ,而 r 表示半径, 这样我们就能唯一的定义一个圆了, 见下图:
在OpenCV中,我们一般通过一个叫做“霍夫梯度法”的方法来解决圆变换的问题。
- 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的灰度单通道图像。
- 第二个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
- 第三个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。
- 第四个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
- 第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
- 第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
- 第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
- 第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
- 第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。
需要注意的是,使用此函数可以很容易地检测出圆的圆心,但是它可能找不到合适的圆半径。我们可以通过第八个参数minRadius和第九个参数maxRadius指定最小和最大的圆半径,来辅助圆检测的效果。或者,我们可以直接忽略返回半径,因为它们都有着默认值0,单单用HoughCircles函数检测出来的圆心,然后用额外的一些步骤来进一步确定半径。
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 Mat src = imread("E://C.jpg"); 7 Mat dst = src.clone(); 8 imshow("src", src); 9 10 cvtColor(src, src, CV_BGR2GRAY); 11 GaussianBlur(src,src,Size(9,9),2); 12 13 vector<Vec3f> circles; 14 HoughCircles(src, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100); 15 for(size_t i = 0; i<circles.size(); i++) 16 { 17 Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); 18 int radius = cvRound(circles[i][2]); 19 circle(dst, center, 3, Scalar(0, 0, 255), -1, 8,0);//设置为-1时,画实心圆 20 circle(dst, center, radius, Scalar(0, 255, 0), 3, 8,0);//画空心圆 21 } 22 imshow("dst", dst); 23 waitKey(0); 24 }
五、其他