一、简介

在图像处理和计算机视觉领域中,如何从当前的图像中提取所需要的特征信息是图像识别的关键所在。在许多应用场合中需要快速准确地检测出直线或者圆。其中一种非常有效的解决问题的方法是霍夫(Hough)变换。

opencv学习之路(18)、霍夫变换-LMLPHP

二、霍夫线变换

opencv学习之路(18)、霍夫变换-LMLPHP

霍夫线变换的原理

opencv学习之路(18)、霍夫变换-LMLPHP

opencv学习之路(18)、霍夫变换-LMLPHP

opencv学习之路(18)、霍夫变换-LMLPHP

opencv学习之路(18)、霍夫变换-LMLPHP

 #include "opencv2/opencv.hpp"
using namespace cv; void main()
{
Mat src=imread("E://1.png");
Mat dst;
imshow("src", src); Canny(src, src, , );
imshow("Canny", src);
//cvtColor(src, dst, CV_BGR2GRAY);//彩色图转灰度图:该语句不能放在canny边缘检测后(canny检测后,图像变为了灰度图)
cvtColor(src, dst, CV_GRAY2BGR);//灰度图转彩色图 //进行霍夫线变换
vector<Vec2f> lines; //定义矢量结构lines用于存放得到的线段矢量集合
HoughLines(src, lines, , CV_PI/, );//超过150的线段才被检测到 //依次在图中绘制出每条线段
for (size_t i = ; i < lines.size(); i++)
{
float rho=lines[i][],theta=lines[i][];
Point pt1,pt2;
double a=cos(theta),b=sin(theta);
double x0=a*rho,y0=b*rho;
pt1.x=cvRound(x0+*(-b));//cvRound(double value) 函数:对一个double型数字四舍五入,返回一个整数
pt1.y=cvRound(y0+*(a));
pt2.x = cvRound(x0 - *(-b));
pt2.y = cvRound(y0 - *(a));
line(dst,pt1,pt2,Scalar(,,),,);
} imshow("dst", dst);
waitKey();
destroyAllWindows();
}

opencv学习之路(18)、霍夫变换-LMLPHP

三、累计概率霍夫变换

opencv学习之路(18)、霍夫变换-LMLPHP

 #include "opencv2/opencv.hpp"
using namespace cv; void main()
{
//HoughLinesP()用法
Mat src = imread("E://1.png");
Mat dstImg = src.clone();
imshow("src", src); cvtColor(src, src, CV_BGR2GRAY);
Canny(src,src, , );
vector<Vec4i> lines; //定义矢量结构lines用于存放得到的线段矢量集合
HoughLinesP(src, lines, , CV_PI/, , , );
//依次在图中绘制出每条线段
for(size_t i = ; i<lines.size(); i++)
{
Vec4i p = lines[i];
line(dstImg, Point(p[0], p[1]), Point(p[2], p[3]), Scalar(0, 255, 0), 2, 8); }
imshow("dst", dstImg);
waitKey();
destroyAllWindows();
}

opencv学习之路(18)、霍夫变换-LMLPHP

四、霍夫圆变换

霍夫圆变换的基本原理和上面讲的霍夫线变化大体上是很类似的,只是点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代。说“大体上类似”的原因是,如果完全用相同的方法的话,累加平面会被三维的累加容器所代替:在这三维中,一维是x,一维是y,另外一维是圆的半径r。这就意味着需要大量的内存而且执行效率会很低,速度会很慢。

对直线来说, 一条直线能由参数极径极角opencv学习之路(18)、霍夫变换-LMLPHP表示. 而对圆来说, 我们需要三个参数来表示一个圆, 也就是:

opencv学习之路(18)、霍夫变换-LMLPHP

这里的(Xcenter,Ycenter)表示圆心的位置 (下图中的绿点) ,而 r 表示半径, 这样我们就能唯一的定义一个圆了, 见下图:

opencv学习之路(18)、霍夫变换-LMLPHPopencv学习之路(18)、霍夫变换-LMLPHP

在OpenCV中,我们一般通过一个叫做“霍夫梯度法”的方法来解决圆变换的问题。

opencv学习之路(18)、霍夫变换-LMLPHP

opencv学习之路(18)、霍夫变换-LMLPHP

  • 第一个参数,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函数检测出来的圆心,然后用额外的一些步骤来进一步确定半径。

 #include "opencv2/opencv.hpp"
using namespace cv; void main()
{
Mat src = imread("E://C.jpg");
Mat dst = src.clone();
imshow("src", src); cvtColor(src, src, CV_BGR2GRAY);
GaussianBlur(src,src,Size(,),); vector<Vec3f> circles;
HoughCircles(src, circles, CV_HOUGH_GRADIENT,1.5, , , );
for(size_t i = ; i<circles.size(); i++)
{
Point center(cvRound(circles[i][]), cvRound(circles[i][]));
int radius = cvRound(circles[i][]);
circle(dst, center, , Scalar(, , ), -, ,);//设置为-1时,画实心圆
circle(dst, center, radius, Scalar(, , ), , ,);//画空心圆
}
imshow("dst", dst);
waitKey();
}

opencv学习之路(18)、霍夫变换-LMLPHP

五、其他

opencv学习之路(18)、霍夫变换-LMLPHP

05-11 22:07