opencv —— HoughCircles 霍夫圓變換原理及圓檢測


霍夫圓變換原理

霍夫圓變換的基本原理與霍夫線變換(https://www.cnblogs.com/bjxqmy/p/12331656.html)大體類似。

  • 對直線來說,一條直線能由極徑極角(r,θ)表示,而對於圓來說,我們需要三個參數:圓心(a,b),半徑 r。

笛卡爾坐標系中圓的方程為:

(x-a)2 + (y-b)2 = r2

化簡便可得到: 

a = x - r·cosθ 

b = y - r·sinθ

  • 對於(x0,y0),我們可以將通過這一點的所有圓統一定義為:

a = x0 - r·cosθ

b = y0 - r·sinθ

這就意味着每一組(a,b,r)代表一個通過點(x0,y0)的圓。 

  • 對於一個給定點(x0,y0),我們可以在三維直角坐標系中,繪出所有通過它的圓。最終我們將得到一條三維的曲線。

  • 我們可以對圖像中所有的點進行上述操作.。如果兩個不同點進行上述操作后得到的曲線在空間 a - b - r 相交, 即它們有一組公共的(a,b,r),這就意味着它們在同一個圓上。

  • 越多曲線交於一點,也就意味着這個交點表示的圓由更多的點組成。我們可以設置一個閾值,來決定多少條曲線交於一點我們才認為檢測到了一個圓。

  • 這就是霍夫圓變換要做的.。它追蹤圖像中每個點對應曲線間的交點.。如果交於一點的曲線的數量超過了閾值, 那么可以認為這個交點所代表的參數(a,b,r)在原圖像中為一個圓。 

以上是標准霍夫圓變換實現算法。問題是它的累加面(繪制三維曲線的空間)是一個三維的空間,意味着比霍夫線變換需要更多的計算消耗。OpenCV 霍夫圓變換對標准霍夫圓變換做了運算上的優化。它采用的是 “霍夫梯度法”。

 

 

霍夫梯度法的原理

  • 估計圓心

1.把原圖做一次 Canny 邊緣檢測,得到邊緣檢測的二值圖。

2.對原始圖像執行一次 Sobel 算子,計算出所有像素的鄰域梯度值。

3.初始化圓心空間 N(a,b),令所有的 N(a,b)=0。

4.遍歷 Canny 邊緣二值圖中的所有非零像素點,沿着梯度方向 ( 切線的垂直方向 )畫線,將線段經過的所有累加器中的點 (a,b) 的 N(a,b)+=1。                

5.統計排序 N(a,b),得到可能的圓心(N(a,b) 越大,越有可能是圓心)。                  

  • 估計半徑(針對某一個圓心 (a,b))

1.計算 Canny 圖中所有非 0 點距離圓心的距離。

2.距離從小到大排序,根據閾值,選取合適的可能半徑(比如 3 和 3.5 都被划為半徑值 3 中)。

3.初始化半徑空間 r, N(r)=0。

4.遍歷 Canny 圖中的非 0 點,N( 距離 )+=1。

5.統計得到可能的半徑值(N(r) 越大,說明這個距離值出現次數越多,越有可能是半徑值)。

 

 

霍夫梯度法缺點

1.在霍夫梯度法中,我們使用 Sobel 導數來計算局部梯度,那么隨之而來的假設是,其可以視作等同於一條局部切線,並這個不是一個數值穩定的做法。在大多數情況下,這樣做會得到正確的結果,但或許會在輸出中產生一些噪聲。

2.在邊緣圖像中的整個非0像素集被看做每個中心的候選部分。因此,如果把累加器的閾值設置偏低,算法將要消耗比較長的時間。

3.因為每一個中心只選擇一個圓,如果有同心圓,就只能選擇其中的一個。

4.因為中心是按照其關聯的累加器值的升序排列的,並且如果新的中心過於接近之前已經接受的中心的話,就不會被保留下來。且當有許多同心圓或者是近似的同心圓時,霍夫梯度法的傾向是保留最大的一個圓。可以說這是一種比較極端的做法,因為在這里默認Sobel導數會產生噪聲,若是對於無窮分辨率的平滑圖像而言的話,這才是必須的。

 

 

霍夫圓變換:HoughCircles 函數

HoughCircles 函數可以利用霍夫變換算法檢測出灰度圖中的圓。它相比之前的 HoughLines 和HoughLinesP,比較明顯的一個區別是不需要源圖像是二值的,而 HoughLines 和HoughLinesP 都需要源圖像是二值圖像。

void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1 = 100, double param2 = 100, int minRadius = 0, int maxRadius = 0);

  • image,輸入圖像,即源圖像,需要為 8 位的灰度單通道圖像。
  • circles,調用 HoughCircles 函數后此參數存儲了檢測到的圓的輸出矢量,每個矢量由包含了 3 個元素的浮點矢量(x,y,radius)表示。
  • method,使用的檢測方法,目前 OpenCV 中就霍夫梯度法一種可以使用,標識符為 HOUGH_GRADIENT。
  • dp,累加面分辨率(大小) = 原始圖像分辨率(大小) × 1/dp。默認 dp = 1 時,兩者分辨率相同。
  • minDist,兩個圓心之間的最小距離。若兩圓心距離 < minDist,則認為是同一個圓。
  • param1,Canny 邊緣檢測的高閾值,低閾值被自動置為高閾值的一半,默認為 100。
  • param2,累加平面某點是否是圓心的判定閾值。它越大,能通過檢測的圓就更接近完美的圓形,默認為 100。
  • minRadius,圓半徑的最小值。默認為 0。
  • maxRadius,圓半徑的最大值,默認為 0。

此函數可以很容易地檢測出圓心,但是可能找不到合適地圓半徑。我們可以通過 minRadius 和 maxRadius 兩個參數指定最大和最小圓半徑,來輔助圓檢測的結果。或者可以直接忽略返回半徑,讓二者均為默認值,只用 HoughCircles 函數檢測出圓心,用額外步驟進一步確定半徑。

 

 

代碼示例:

#include<opencv.hpp> #include<iostream>
using namespace std; using namespace cv; int hough_value = 55; Mat src, gray; void hough_change(int, void*) { vector<Vec3f>circles;

//minDist 和 param2 數值的設定是關鍵 HoughCircles(gray, circles, HOUGH_GRADIENT,
1, 10, 110, hough_value, 0, 0); Mat show = src.clone(); for (int i = 0; i < circles.size(); i++) { circle(show, Point(circles[i][0], circles[i][1]), circles[i][2], Scalar(0, 0, 255), 2); } imshow("show", show); } int main() { src = imread("C:/Users/齊明洋/Desktop/1.jpg"); GaussianBlur(src, src, Size(3, 3), 0, 0); imshow("src", src);
cvtColor(src, gray, COLOR_BGR2GRAY); namedWindow(
"show"); createTrackbar("hough_value", "show", &hough_value, 200, hough_change); hough_change(0, 0); waitKey(0); }

 

效果演示:

 

 

借鑒博客:https://www.cnblogs.com/yzl050819/p/7967526.html

https://www.cnblogs.com/lancer2015/p/6852488.html

https://blog.csdn.net/weixin_41049188/article/details/92422241?utm_source=distribute.pc_relevant.none-task

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM