天天看点

20 OpenCV霍夫圆变换HoughCircles一、霍夫圆检测原理二、霍夫梯度法三、HoughCircles四、示例

一、霍夫圆检测原理

  • 对直线来说, 一条直线能由参数极径极角 (r,θ) 表示.

    而对圆来说, 从平面坐标到极坐标转换需要三个参数, 也就是: ( xcenter , ycenter, r )。其中xcenter , ycenter 表示圆心,r 表示圆的半径。这就意味着需要大量的内存而且执行效率会很低,速度会很慢。

    20 OpenCV霍夫圆变换HoughCircles一、霍夫圆检测原理二、霍夫梯度法三、HoughCircles四、示例
  • 霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波
  • 为了提高效率,OpenCV实现了一种比标准Hough变换稍微复杂的检测方法:Hough梯度方法,它由两个主要阶段组成。

    第一阶段:检测边缘,发现可能的圆心

    第二阶段:基于第一步的基础上从候选圆心开始计算最佳半径大小

二、霍夫梯度法

  • 原理

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

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

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

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

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

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

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

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

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

  • 缺点

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

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

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

三、HoughCircles

void cv::HoughCircles   (   InputArray      image,
        OutputArray     circles,
        int     method,
        double      dp,
        double      minDist,
        double      param1 = 100,
        double      param2 = 100,
        int     minRadius = 0,
        int     maxRadius = 0 
    )   
           
  • image: 输入图像为8位单通道灰度图
  • circles: 检测到圆的输出矢量,每个矢量都是三个浮点元素构成(x,y,radius)
  • method: 检测方法,可以查看HoughModes查看具体细节,但目前只有HOUGH_GRADIENT(霍夫梯度)一种方法可以使用。
  • dp: 用来检测圆心的累加器图像的分辨率与输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。例如,如果dp=1,累加器和输入图像具有相同的分辨率,如果dp=2累加器便有输入图像一半那么大的宽度和高度。
  • minDist: 霍夫检测到的圆的圆心之间的最小距离,即让算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误的检测成了一个重合的圆。反之这个参数设置太大,某些圆就不能被检测出来了。也就是超过这个距离就是两个圆,否则是一个圆
  • param1: 指定检测方法的第一个参数,当前可用方法是HOUGH_GRADIENT,它表示传递给Canny边缘检测算子的高阈值(低阈值是高阈值的一半),有默认值100
  • param2: 指定检测方法的第二个参数,对于HOUGH_GRADIENT方法,它表示在检测阶段圆心的累加器阈值。它越小就越可以检测到更多根本不存在的圆,而它越大的话能通过检测的圆就更加接近完美的圆形了。有默认值100
  • minRadius: 圆的最小半径
  • maxRadius: 圆的最大半径

四、示例

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	//1. 读取图像
	Mat src, midBlur, gray_src, dst;
	src = imread("images/13.png");
	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;
}


           
20 OpenCV霍夫圆变换HoughCircles一、霍夫圆检测原理二、霍夫梯度法三、HoughCircles四、示例

继续阅读