對于直線來說幻赚,一條直線能有參數(shù)極徑級角表示,而對圓來說我們需要三個參數(shù)來表示一個圓
在OpenCV中预皇,我們常常通過一個叫“霍夫梯度法”的方法來解決圓變換的問題稳吮。
霍夫梯度法的原理
(1)首先對圖像應(yīng)用邊緣檢測,比如canny邊緣檢測
(2)然后對邊緣圖像中的每一個非零點,考慮其局部梯度,即用sobel函數(shù)計算x和y方向的Sobel一階導(dǎo)數(shù)得到的梯度。
(3)可用得到的梯度缤骨,有斜率指定的直線上的每個點都在累加器中累加,這里的斜率是從一個指定的最小值到指定的最大值得距離
(4)同時標(biāo)記圖像中每一個非0像素的位置
(5)然后從二維累加器中這些點鐘選擇候選的中心尺借,這些中心都大于給定閾值并且大于其所有近鄰绊起,這些候選的中心按照累加值將序排列,以便于最支持像素的中心首先出現(xiàn)
(6)接下來對每一個中心燎斩,考慮所有的非0元素
(7)這些元素按照其與中心的距離排序虱歪。從最大半徑到最小半徑算起,選擇非0像素最支持的一條半徑
(8)如果一個中心收到邊緣圖像非0像素的最充分支持瘫里,并且到前期被選擇的中心有足夠的距離实蔽,那么他就會被保留下來。
這個實現(xiàn)可以使算法執(zhí)行起來更高效谨读,獲取更加重要的是能夠幫助解決三維累加器中國會產(chǎn)生許多噪聲并且使得結(jié)構(gòu)不穩(wěn)定的稀疏分布問題局装。
霍夫梯度法的缺點
(1)在霍夫梯度法中,我們使用Sobel導(dǎo)數(shù)來計算局部梯度,那么隨之而來的假設(shè)是铐尚,它可以視作等同于一條局部切線拨脉,這并不是一個穩(wěn)定的做法,在大多數(shù)的情況下宣增,這樣做會得到正確答案玫膀,但或許會輸出產(chǎn)生一些噪聲。
(2)在邊緣圖像中的整個非0像素集爹脾,被看做每個中心的候選部分帖旨。因此如果把累加器的閾值設(shè)置的偏低,算法需要消耗更長的時間灵妨。因此解阅,每一個中心只會選擇一個圓,如果有同心圓泌霍,就只能選擇一個
(3)因為中心是按照其關(guān)聯(lián)的累加器值得升序排列的货抄,并且如果新的中心過于接近之前已經(jīng)接受的中心的話,就不被保留下來朱转,并且當(dāng)有很多的同心圓或者是近似同心圓時蟹地,霍夫梯度法的傾向是保留最大的一個圓,可以說這也是一種比較極端的算法藤为,因為子在這里默認Sobel導(dǎo)數(shù)會產(chǎn)生噪聲怪与,若是對于無窮分辨率的平滑圖像而言,這是必須的凉蜂。
霍夫圓變換函數(shù):HounghCircles()函數(shù)
void HoughCircles(InputArray image,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OutputArray circles,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int method,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? double dp,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? double minDist,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? double param1 =100,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?double param2 =100,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int minRadius =0,
? ? ? ? ? ? ? ? ? ? ? ? ? ? int maxRadius =0);
函數(shù)參數(shù)詳解
代碼實現(xiàn)
NSString*image =@"dkdk.jpg";
UIImage*image1 = [UIImageimageNamed:image];
Matim;
UIImageToMat(image1, im);
if(im.empty()) {
return;
}
//創(chuàng)建零時變量
Mat midImage,dstImage;
//轉(zhuǎn)換為灰度圖像進行圖像平滑
cvtColor(im, midImage,COLOR_BGR2GRAY);
GaussianBlur(midImage, midImage,cv::Size(9,9),2,2);
//進行霍夫圓變換
std::vector<Vec3f> circle;
HoughCircles(midImage, circle,HOUGH_GRADIENT,1.5,10);
//依次在圖中繪制出圓
for(size_ti =0; i < circle.size(); i++) {
//圓心
cv::Point center(cvRound(circle[i][0]),cvRound(circle[i][1]));
//半徑
int radius =cvRound(circle[i][2]);
//繪制
cv::circle(midImage, center, radius,Scalar(0,88,255));
}
self.secondImageView.image=MatToUIImage(midImage);