原理:
我们要从一副图像中检测出半径以知的圆形来。这个问题比前一个还要直观。我们可以取和图像平面一样的参数平面,以图像上每一个前景点为圆心,以已知的半径在参数平面上画圆,并把结果进行累加。最后找出参数平面上的峰值点,这个位置就对应了图像上的圆心。在这个问题里,图像平面上的每一点对应到参数平面上的一个圆。
把上面的问题改一下,假如我们不知道半径的值,而要找出图像上的圆来。这样,一个办法是把参数平面扩大称为三维空间。就是说,参数空间变为x--y--R三维,对应圆的圆心和半径。图像平面上的每一点就对应于参数空间中每个半径下的一个圆,这实际上是一个圆锥。最后当然还是找参数空间中的峰值点。不过,这个方法显然需要大量的内存,运行速度也会是很大问题。
有什么更好的方法么?我们前面假定的图像都是黑白图像(2值图像),实际上这些2值图像多是彩色或灰度图像通过边缘提取来的。我们前面提到过,图像边缘除了位置信息,还有方向信息也很重要,这里就用上了。根据圆的性质,圆的半径一定在垂直于圆的切线的直线上,也就是说,在圆上任意一点的法线上。这样,解决上面的问题,我们仍采用2维的参数空间,对于图像上的每一前景点,加上它的方向信息,都可以确定出一条直线,圆的圆心就在这条直线上。这样一来,问题就会简单了许多。
代码:
// 加载原图
IplImage *pSrcImage = cvLoadImage("C:\\Users\\徐图之\\Desktop\\33.bmp", -1);
IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
//cvSmooth(pGrayImage, pGrayImage);
// 圆检测(灰度图)
CvMemStorage *pcvMStorage = cvCreateMemStorage();
double fMinCircleGap = pGrayImage->height / 40;
CvSeq *pcvSeqCircles = cvHoughCircles(pGrayImage, pcvMStorage, CV_HOUGH_GRADIENT, 1.8, fMinCircleGap);
//每个圆由三个浮点数表示:圆心坐标(x,y)和半径
// 绘制直线
IplImage *pColorImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 3);
cvCvtColor(pGrayImage, pColorImage, CV_GRAY2BGR);
for (int i = 0; i < pcvSeqCircles->total; i++)
{
float* p = (float*)cvGetSeqElem(pcvSeqCircles, i);
CvPoint pt = cvPoint(cvRound(p[0]), cvRound(p[1]));
cvCircle(pColorImage, pt, cvRound(p[2]), CV_RGB(255, 0, 0), 2);
}
cvNamedWindow("pstrWindowsSrcTitle", CV_WINDOW_AUTOSIZE);
cvShowImage("pstrWindowsSrcTitle", pGrayImage);
cvNamedWindow("Name", CV_WINDOW_AUTOSIZE);
cvShowImage("Name", pColorImage);
cvWaitKey(0);
cvReleaseMemStorage(&pcvMStorage);
cvDestroyWindow("pstrWindowsSrcTitle");
cvDestroyWindow("pstrWindowsLineName");
cvReleaseImage(&pGrayImage);
cvReleaseImage(&pColorImage);
cvHoughCircles这个函数的函数原形如下:
CVAPI(CvSeq*) cvHoughCircles(
CvArr* image, void* circle_storage,
int method,
double dp,
double min_dist,
double param1 CV_DEFAULT(100),
double param2 CV_DEFAULT(100),
int min_radius CV_DEFAULT(0),
int max_radius CV_DEFAULT(0)
);
可以看出cvHoughCircles与上一章的cvHoughLines2函数比较类似,因此讲下部分参数的意思就可以了:
第二个参数表示Hough变换方式,目前只能用CV_HOUGH_GRADIENT。
第三个参数表示寻找圆弧圆心的累计分辨率,通常设置成1就可以了,如果检测不出来,可以设置大一点
第四个参数表示两个不同圆之间的最小距离,由于是按圆心来计算距离的,因此对同心圆的检测就无能为力了。
注意,圆检测函数可以使用灰度图。