OpenCV-图像处理(21、霍夫圆变换)

霍夫圆检测原理

OpenCV-图像处理(21、霍夫圆变换)_第1张图片

  • 对直线来说, 一条直线能由参数极径极角 ( r , θ r,θ r,θ) 表示.
    而对圆来说, 从平面坐标到极坐标转换需要三个参数, 也就是: ( x x xcenter , y y ycenter, r r r )。其中 x x xcenter , y y ycenter 表示圆心, r r r 表示圆的半径。
  • 在这三维中,一维是x,一维是y,另外一维是圆的半径r。这就意味着需要大量的内存而且执行效率会很低,速度会很慢。
  • 因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波。
  • 为了提高效率,OpenCV实现了一种比标准Hough变换稍微复杂的检测方法:Hough梯度方法,它由两个主要阶段组成。
    第一阶段:检测边缘,发现可能的圆心
    第二阶段:基于第一步的基础上从候选圆心开始计算最佳半径大小
    OpenCV-图像处理(21、霍夫圆变换)_第2张图片

霍夫梯度法的原理

【1】首先对图像应用边缘检测,比如用canny边缘检测。

【2】然后,对边缘图像中的每一个非零点,考虑其局部梯度,即用Sobel()函数计算x和y方向的Sobel一阶导数得到梯度。

【3】利用得到的梯度,由斜率指定的直线上的每一个点都在累加器中被累加,这里的斜率是从一个指定的最小值到指定的最大值的距离。

【4】同时,标记边缘图像中每一个非0像素的位置。

【5】然后从二维累加器中这些点中选择候选的中心,这些中心都大于给定阈值并且大于其所有近邻。这些候选的中心按照累加值降序排列,以便于最支持像素的中心首先出现。

【6】接下来对每一个中心,考虑所有的非0像素。

【7】这些像素按照其与中心的距离排序。从到最大半径的最小距离算起,选择非0像素最支持的一条半径。

【8】如果一个中心收到边缘图像非0像素最充分的支持,并且到前期被选择的中心有足够的距离,那么它就会被保留下来。

这个实现可以使算法执行起来更高效,或许更加重要的是,能够帮助解决三维累加器中会产生许多噪声并且使得结果不稳定的稀疏分布问题。

霍夫梯度法的缺点

<1>在霍夫梯度法中,我们使用Sobel导数来计算局部梯度,那么随之而来的假设是,其可以视作等同于一条局部切线,并这个不是一个数值稳定的做法。在大多数情况下,这样做会得到正确的结果,但或许会在输出中产生一些噪声。

<2>在边缘图像中的整个非0像素集被看做每个中心的候选部分。因此,如果把累加器的阈值设置偏低,算法将要消耗比较长的时间。第三,因为每一个中心只选择一个圆,如果有同心圆,就只能选择其中的一个。

<3>因为中心是按照其关联的累加器值的升序排列的,并且如果新的中心过于接近之前已经接受的中心的话,就不会被保留下来。且当有许多同心圆或者是近似的同心圆时,霍夫梯度法的倾向是保留最大的一个圆。可以说这是一种比较极端的做法,因为在这里默认Sobel导数会产生噪声,若是对于无穷分辨率的平滑图像而言的话,这才是必须的。

HoughCircles( )函数详解

HoughCircles函数可以利用霍夫变换算法检测出灰度图中的圆。它和之前的HoughLines和HoughLinesP比较明显的一个区别是它不需要源图是二值的,而HoughLines和HoughLinesP都需要源图为二值图像。

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

  • 第一个参数,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,表示圆半径的最大值。

HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像
OutputArray circles, // 输出结果,发现的圆信息
Int method, // 方法 - HOUGH_GRADIENT
Double dp, // dp = 1;
Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8
Double param1, // canny edge detection low threshold
Double param2, // 中心点累加器阈值 – 候选圆心
Int minradius, // 最小半径
Int maxradius//最大半径
)

需要注意的是,使用此函数可以很容易地检测出圆的圆心,但是它可能找不到合适的圆半径。我们可以通过第八个参数minRadius和第九个参数maxRadius指定最小和最大的圆半径,来辅助圆检测的效果。或者,我们可以直接忽略返回半径,因为它们都有着默认值0,单单用HoughCircles函数检测出来的圆心,然后用额外的一些步骤来进一步确定半径。

程序代码

#include
#include

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
     
	//1. 读取图像
    Mat src, midBlur,gray_src, dst;
    src = imread("E:/Experiment/OpenCV/Pictures/CircleTest2.jpg");
    imshow("src", src);

    //2. 中值滤波,去除噪声影响,不去噪的话,霍夫圆检测的结果会不准确
	medianBlur(src, midBlur, 3);
    imshow("midBlur", midBlur);
	//3. 转成灰度图像
	cvtColor(midBlur, gray_src, CV_BGR2GRAY);//将二值图转换为RGB图颜色空间,这里重新创建一张空Mat也行
	
	//4. 霍夫变换检测
	 vector<Vec3f> circles;//存放检测出的圆信息,每个元素 Vec3f 的三个值表示,圆心x,y,圆半径,类型必须是Vec3f
    HoughCircles(gray_src, circles, CV_HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50);//霍夫圆检测
    // 绘制圆
    src.copyTo(dst); // cvtColor(gray_src,dst,CV_GRAY2BGR);
    for (int i = 0; i < circles.size(); i++)
    {
     
        Vec3f cc = circles[i];
        circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);//在原图上画出圆
        circle(dst, Point(cc[0], cc[1]), 2, Scalar(198, 23, 155), 2, LINE_AA);//在原图上画出圆心
    }

    imshow("Circle Test", dst);

    waitKey(0);
	return 0;
}

运行结果

OpenCV-图像处理(21、霍夫圆变换)_第3张图片

参考博客

  1. https://blog.csdn.net/poem_qianmo/article/details/26977557?utm_source=tuicool&utm_medium=referral
  2. https://blog.csdn.net/LYKymy/article/details/83186989
  3. https://blog.csdn.net/huanghuangjin/article/details/81166182

其他

  • 如果读者自己有兴趣,也可以自己尝试调不同的参数值,查看效果,进一步理解每个参数的意义
#include
#include
using namespace std;
using namespace cv;

Mat src, midBlur,gray_src, dst;
char output_windows[]="HoughCircle Demo Windows";

int dp_value=1, max_dp=5;//第四个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
int minDist_value=10, max_minDist=100;//第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
int param1_value=100, max_param1=255;//第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
int param2_value=30, max_param2= 100;//第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
int minRadius_value=5, max_minRadius=100;//第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
int maxRadius_value=50, max_maxRadius=100;//第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。

void HoughCircle_Demo(int , void* );
void MyMessage();
int main(int argc, char** argv)
{
     
	//1. 读取图像
    src = imread("E:/Experiment/OpenCV/Pictures/CircleTest.jpg");
    imshow("src", src);
	namedWindow(output_windows,CV_WINDOW_AUTOSIZE);

    //2. 中值滤波,去除噪声影响,不去噪的话,霍夫圆检测的结果会不准确
	medianBlur(src, midBlur, 3);
    imshow("midBlur", midBlur);
	//3. 转成灰度图像
	cvtColor(midBlur, gray_src, CV_BGR2GRAY);//将二值图转换为RGB图颜色空间,这里重新创建一张空Mat也行
	
	createTrackbar("图像分辨率(倍数)", output_windows, &dp_value, max_dp, HoughCircle_Demo);
	createTrackbar("圆心间的最小距离", output_windows, &minDist_value, max_minDist, HoughCircle_Demo);
	createTrackbar("canny算子的高阈值", output_windows, &param1_value, max_param1, HoughCircle_Demo);
	createTrackbar("圆心的累加器阈值", output_windows, &param2_value, max_param2, HoughCircle_Demo);
	createTrackbar("圆半径的最小值", output_windows, &minRadius_value, max_minRadius, HoughCircle_Demo);
	createTrackbar("圆半径的最大值", output_windows, &maxRadius_value, max_maxRadius, HoughCircle_Demo);
	
	MyMessage();
	HoughCircle_Demo(0,0);
    waitKey(0);
	return 0;
}

void HoughCircle_Demo(int ,void* ){
     
	//4. 霍夫变换检测
	vector<Vec3f> circles;//存放检测出的圆信息,每个元素 Vec3f 的三个值表示,圆心x,y,圆半径,类型必须是Vec3f
	HoughCircles(gray_src, circles, CV_HOUGH_GRADIENT, dp_value, minDist_value, param1_value, param2_value, minRadius_value, maxRadius_value);//霍夫圆检测
    // 5. 绘制圆
    src.copyTo(dst); 
	//cvtColor(gray_src,dst,CV_GRAY2BGR);
    for (int i = 0; i < circles.size(); i++)
    {
     
		Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
		int radius = cvRound(circles[i][2]);
		//绘制圆心
		circle( dst, center, 3, Scalar(0,255,0), -1, 8, 0 );
		//绘制圆轮廓
		circle( dst, center, radius, Scalar(155,50,255), 3, 8, 0 );
    }

    imshow("dst", dst);
}

void MyMessage(){
     
	cout<<"第四个参数,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。"<<endl;
	cout<<"第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。"<<endl;
	cout<<"第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。"<<endl;
	cout<<"第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。"<<endl;
	cout<<"第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。"<<endl;
	cout<<"第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。"<<endl;
	cout<<endl<<endl;
}

你可能感兴趣的:(OpenCV,OpenCV,图像处理,霍夫圆变化)