霍夫圆变换的基本思路是认为图像上每一个非零像素点都有可能是一个潜在的圆上的一点,跟霍夫线变换一样,也是通过投票,生成累积坐标平面,设置一个累积权重来定位圆。
在笛卡尔坐标系中圆的方程为:
其中(a,b)是圆心,r是半径,也可以表述为:
即:
所以在abr组成的三维坐标系中,一个点可以唯一确定一个圆。 而在笛卡尔的xy坐标系中经过某一点的所有圆映射到abr坐标系中就是一条三维的曲线:
经过xy坐标系中所有的非零像素点的所有圆就构成了abr坐标系中很多条三维的曲线。
在xy坐标系中同一个圆上的所有点的圆方程是一样的,它们映射到abr坐标系中的是同一个点,所以在abr坐标系中该点就应该有圆的总像素N0个曲线相交。通过判断abr中每一点的相交(累积)数量,大于一定阈值的点就认为是圆。
以上是标准霍夫圆变换实现算法,问题是它的累加面是一个三维的空间,意味着比霍夫线变换需要更多的计算消耗。Opencv霍夫圆变换对标准霍夫圆变换做了运算上的优化。它采用的是“霍夫梯度法”。它的检测思路是去遍历累加所有非零点对应的圆心,对圆心进行考量。如何定位圆心呢?圆心一定是在圆上的每个点的模向量上,即在垂直于该点并且经过该点的切线的垂直线上,这些圆上的模向量的交点就是圆心。
霍夫梯度法就是要去查找这些圆心,根据该“圆心”上模向量相交数量的多少,根据阈值进行最终的判断。
备注:以上霍夫圆变换的原理说明引用自:https://blog.csdn.net/dcrmg/article/details/52506538
以下是霍夫圆变换的示例,本示例是《OpenCV3编程入门》中7.2.9的示例程序的C# EMGU 3.4.1版。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;
using Emgu.CV.Structure;
namespace HoughCircles
{
class Program
{
static void Main(string[] args)
{
//载入原始图
Mat srcImage = CvInvoke.Imread("moon.jpg");
//定义临时变量和目标图
Mat tempImage = new Mat(), dstImage = new Mat();
//显示原始图
CvInvoke.Imshow("OriginalImage", srcImage);
//转为灰度图并进行图像平滑
CvInvoke.CvtColor(srcImage, tempImage, ColorConversion.Rgb2Gray);
CvInvoke.GaussianBlur(tempImage, tempImage, new Size(9, 9), 0, 0);
//进行霍夫圆变换
VectorOfPoint3D32F circles = new VectorOfPoint3D32F(); //定义vect存储圆参数(中心点x、y坐标和半径)
CvInvoke.HoughCircles(tempImage, circles, HoughType.Gradient, 2, 20, 200, 200, 0, 0);
//依次在图中绘制出圆
for (int i = 0; i < circles.Size; i++)
{
//参数定义
Point center = new Point((int)Math.Round(circles[i].X), (int)Math.Round(circles[i].Y)); //定义圆心
int radius = (int)Math.Round(circles[i].Z);
//绘制圆心(圆的thickness设置为-1)
CvInvoke.Circle(srcImage, center, 3, new MCvScalar(0, 255, 0), -1);
//绘制圆轮廓
CvInvoke.Circle(srcImage, center, radius, new MCvScalar(155, 50, 255), 3);
}
//显示最终效果图
CvInvoke.Imshow("HoughCircleTransformedImage", srcImage);
CvInvoke.WaitKey(0);
}
}
}
程序运行截图如下:
原始图:
霍夫圆变换后的效果图: