首先建立一个概念,任何镜头下的圆,很多情况下都不是真正的 pi*r*r 的圆,会因各种物体与镜头之间的非完全平行关系或光线与物体不是完全平行等因素,造成相机中成像的图像不是一直完全意义上的圆,更多的情况下,就是一个椭圆,所以opencv只提供了一个椭圆拟合的方法,其实当长短轴相等时,这就是一个真正的正圆了。
在建立这个观点后,采用众所周知的方法,就可以拟合圆了。
下面时我在用的一个方法,若有不足,请指正
//图像文件名strImgName, canny的低阈值dThreshold1, canny的高阈值dThreshold2, canny的核大小iSize, 筛选圆的条件:圆的最小面积iMinArea, 圆的最小面积int iMaxArea
void findCircles(string strImgName, double dThreshold1, double dThreshold2, int iSize, int iMinArea, int iMaxArea)
{
Mat q_MatImage;
Mat q_MatImageGray;
Mat q_MatImageShow;
q_MatImage=imread(strImgName);//读入一张图片
q_MatImage.copyTo(q_MatImageShow);
cvtColor(q_MatImage,q_MatImageGray,CV_RGB2GRAY);
//Canny找边界,也可以阈值二值化
Mat cannyEdge;
Canny(q_MatImageGray, cannyEdge, dThreshold1, dThreshold2, iSize);
//找轮廓
vector<vector<Point>> q_vPointContours;
findContours(cannyEdge, q_vPointContours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE,Point(0,0));
//drawContours(q_MatImageShow, q_vPointContours, -1, Scalar(0,255,0));//显示轮廓
//筛选目标轮廓点
vector<Point> vfindContours;
size_t q_iAmountContours = q_vPointContours.size();
size_t iIndex = 0;
for ( iIndex = 0; iIndex < q_iAmountContours; iIndex++)
{
//根据圆的面积判断是否为目标圆
double ddarea = contourArea(q_vPointContours[iIndex]);
if((iMinArea < ddarea) && (iMaxArea > ddarea))
{
break;
}
}
//存储目标圆的轮廓点
size_t findCount = q_vPointContours[iIndex].size();
for(int i=0; i<findCount; i++)
vfindContours.push_back(q_vPointContours[iIndex][i]);
//采用椭圆拟合来得到圆
RotatedRect rectElli = fitEllipse(vfindContours);
float fR = MIN(rectElli.size.width , rectElli.size.height);// 是否为圆,可以比较这两个值,若十分接近或相等,就是一个正圆
cout << "fitEllipse 中心: " << rectElli.center.x << ", " <<rectElli.center.y << " 半径:"<<fR/2<< endl;
circle(q_MatImageShow, Point(rectElli.center), fR/2, Scalar(0,0,255), 2);//圆周
circle(q_MatImageShow, Point(rectElli.center), 5, Scalar(0,0,255), 3);//圆心
namedWindow("Test"); //创建一个名为Test窗口
imshow("Test",q_MatImageShow);//窗口中显示图像
waitKey();
}
若有什么更好的找圆方法,欢迎一起讨论