上一篇博客中简要描述了一下自己对霍夫变换检测直线的原理理解,现在说一下检测圆形的原理。
其实检测圆形和检测直线的原理差别不大,只不过直线是在二维空间,因为y=kx+b,只有k和b两个*度。而圆形的一般性方程表示为(x-a)²+(y-b)²=r²。那么就有三个*度圆心坐标a,b,和半径r。这就意味着需要更多的计算量,而OpenCV中提供的cvHoughCircle()函数里面可以设定半径r的取值范围,相当于有一个先验设定,在每一个r来说,在二维空间内寻找a和b就可以了,能够减少计算量。
具体步骤如下:
1.对输入图像进行边缘检测,获取边界点,即前景点。
2.假如图像中存在圆形,那么其轮廓必定属于前景点(此时请忽略边缘提取的准确性)。
3.同霍夫变换检测直线一样,将圆形的一般性方程换一种方式表示,进行坐标变换。由x-y坐标系转换到a-b坐标系。写成如下形式(a-x)²+(b-y)²=r²。那么x-y坐标系中圆形边界上的一点对应到a-b坐标系中即为一个圆。
4.那x-y坐标系中一个圆形边界上有很多个点,对应到a-b坐标系中就会有很多个圆。由于原图像中这些点都在同一个圆形上,那么转换后a,b必定也满足a-b坐标系下的所有圆形的方程式。直观表现为这许多点对应的圆都会相交于一个点,那么这个交点就可能是圆心(a, b)。
5.统计局部交点处圆的个数,取每一个局部最大值,就可以获得原图像中对应的圆形的圆心坐标(a,b)。一旦在某一个r下面检测到圆,那么r的值也就随之确定。
以下面的图片为例,调用OpenCV中的cvHoughCircles()进行圆形的检测,左边为原图,右边为检测结果,圆形都被圈出。
附上代码:
#include"cv.h"
#include"highgui.h"
#include<math.h>
using namespace cv;
void main()
{
IplImage* srcImg=cvLoadImage("img.jpg", CV_LOAD_IMAGE_GRAYSCALE);
CvMemStorage* storage= cvCreateMemStorage(0);
cvSmooth(srcImg, srcImg, CV_GAUSSIAN, 5, 5);
//函数范围值为指向图像序列的指针
CvSeq* results=cvHoughCircles(srcImg, storage, CV_HOUGH_GRADIENT, 2,srcImg->width/10);
for (int i=0; i<results->total; i++)
{
float* p=(float*) cvGetSeqElem(results, i); //依次获取图像序列中的图像数据
CvPoint pt=cvPoint(cvRound(p[0]), cvRound(p[1])); //获取圆心坐标
cvCircle(srcImg, pt, cvRound(p[2]), CV_RGB(0xff, 0xff, 0xff), 3, 8); //在图片中将圆形画出来
}
cvNamedWindow("Img", 1);
cvShowImage("Img", srcImg);
cvWaitKey(-1);
cvReleaseImage(&srcImg);
cvDestroyAllWindows();
}