【OpenCV】Hough变换

Hough(霍夫)变换是一种用于检测线、圆或者图像中其它简单形状的方法。最初Hough变换是一种线变换,这是一种相对较快的检测二值图像中直线的方法。
Hough线变换的基本理论是:二进制图像中的任何点都可能属于某些可能的线。如果我们将每一条线参数化,如斜率为a,截距为b,原始图像中的点就可以转换为对应于通过该点的所有线在该平面(a,b)中的点的轨迹。当然也可能是一部分轨迹。如果我们将原图中每个非0像素转换成输出图像中这样的一系列点,并将所有这些贡献相加,那么原图((x,y)平面)中的线将显示为输出图像((a,b)平面)中的局部最大值。由于我们将每个点的贡献进行求和,所以(a,b)平面通常称为"累加器平面"。
斜截式方程并不是代表所有穿过某个点的线的最佳方式(因为图像中线的密度与斜率有很大的不同),还有一个相关事实是,斜率的区间可能从负无穷到正无穷。正因如此,图像变换时数值计算实际用到的参数有所不同。最好的参数形式是用极坐标中的点(ρ,θ)表示线,每条直线经过这个点并且垂直于它与原点的连线。
斜率,亦称“角系数”,表示一条直线相对于横轴的倾斜程度。一条直线与某平面直角坐标系横轴正半轴方向的夹角的正切值即该直线相对于该坐标系的斜率。在四个象限的直线方程,如果知道原点到这条直线的代数距离ρ和与x轴的夹角θ,则直线方程可由以下方式表示:
ρ=xcosθ+ysinθ
当然,反过来也可以,如果知道平面内的一条直线,那么可以计算出唯一的ρ和θ, 即xoy平面内的任意一条直线对应参数空间(或称霍夫空间)θoρ中的一点(ρ,θ)。从另个角度考虑,过xoy平面内的一点(x1,y1)有无数条直线,则对应霍夫空间中的无数个点,这无数个点连接起来就是θoρ平面内的曲线ρ=x1cosθ+y1sinθ.
通过上述讨论可以解答一个问题:如何验证xoy平面内的(x1,y1),(x2,y2),…是否共线?只要曲线ρ=xicosθ+yisinθ,i=1,2,…在θoρ平面内相交于一个点就可以了。

标准霍夫直线检测的核心思想:判断xoy平面内哪些点共线的,首先求出每一个点对应到霍夫空间的曲线,然后判断哪几条曲线相较于一点,最后将相交于一点的曲线反过来对应到xoy平面内的点。

在图像中要解决的霍夫直线检测是针对二值图的,验证哪些前景或者边缘像素点是共线的。霍夫变换利用点和线之间的对偶性,将图像空间中直线上离散的像素点通过参数方程映射为霍夫空间中的曲线,并将霍夫空间中多条曲线的交点作为直线方程的参数映射为图像空间中的直线。
点和线的对偶性
(1)图像空间中的点,对应霍夫空间中的直线;
(2)图像中的直线,对应霍夫空间中的点;
(3)共点的直线,在霍夫空间中对应的点在一条直线上;
(4)共线的点,在霍夫空间中对应的直线交与一点。

霍夫圆检测
霍夫直线检测推广到霍夫圆的检测方法,称为标准的霍夫圆检测。与霍夫直线检测类似,图像的霍夫圆检测就是检测哪些前景或边缘像素点在同一个圆上,并给出对应圆的圆心坐标及圆的半径;而且仍然需要计数器来完成该过程只是这里的计数器从二维变成了三维。三维坐标分别是圆心的位置x、y和圆的半径r,这就需要大量的存储空间和巨大的计算量,基于梯度的霍夫圆检测就是对标准霍夫圆检测的改进。
Hough梯度法工作过程如下:首先,对图像进行边缘检测(这里使用cv::Canny());然后,对每个轮廓图像中的非零点,考虑局部梯度(通过cv::sobel()计算一阶Sobel x-导数和y-导数来计算梯度)。通过这个梯度,我们沿着这个斜率表示的线在累加器内从一个最小值到一个最大值遍历每个点,同时,记录轮廓图像中每个非零像素所在的位置。然后候选圆心就从这些(二维)累加器中分离出来,这些点都高于一个阈值且同时大于其所有直接相邻的点。这些点根据其累加器值的降序排列,使得最有可能是圆心的点排在前面。然后,对于每个圆心,考虑所有非零像素点(之前已经构建好该列表),将这些像素根据离圆心的距离排序。从最小距离到最大半径中选择一个最好的值作为圆的半径。如果有足够数量的点组合成一个并且其圆心与之圆心的距离足够大,就保留这个圆心。这个算法有一些需要注意的问题:
第一,用Sobel导数计算局部梯度—伴随的假设是这可以被认为相当于一个局部切线—不是一个数值稳定的命题,甚至“大部分时间”都是这样,但是你应该期望在输出中产生一些噪音。
第二,对每个候选圆心进行判断时要考虑轮廓图像中所有非零像素。因此,如果累加器阈值过低,算法就会很慢。
第三,因为对每个圆心都只能选择一个圆,所以如果出现同心圆,最终将只能得到一个。
第四,由于圆心根据累加器值升序排列,并且圆心距离先前被接受的圆心太近时会被舍去,因而出现同心或近似同心圆时,算法更倾向于保留大圆。

大体步骤是首先定位圆心(两个参数),然后计算半径(一个参数)。在代码实现中,首先构建一个二维计数器,然后再构造一个一维计数器。
void HoughCircles(InputArray image, OutputArray circles,int method,double dp,double minDist, double param1=100,double param2=100,int minRadius =0,int maxRadius=0)
image //输入图像矩阵
circles //返回圆的信息,类型为vector,代表(x,y,radius)
method //只有CV_HOUGH_GRADIENT,即2-1霍夫圆检测
dp //计数器的分辨率
minDist //圆心之间的距离,如果距离太小,则会产生很多相交的圆;如果距离太大,则会漏掉正确的圆
param1 //Canny 边缘检测的双阈值中的高阈值,低阈值默认是它的一半
param2 //最小投票数(基于圆心的投票数)
minRadius //需要检测圆的最小半径
maxRadius //需要检测圆的最大半径

猜你喜欢

转载自blog.csdn.net/weixin_42104289/article/details/84334141