首先建立一個概念,任何鏡頭下的圓,很多情況下都不是真正的 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();
}
若有什麼更好的找圓方法,歡迎一起討論