霍夫檢測圓:霍夫梯度法

cosmic_potato發表於2020-11-05

承接上篇博文,在基本搞懂霍夫檢測直線是怎麼進化到檢測圓後,開始(痴心妄想)自己寫程式碼了!雖說最後的效果不是很好,但是重要的是在碼程式碼過程中發現和解決的一些問題(不一定有共性,但兄弟萌可以避免下這些bug)。

霍夫梯度法

演算法步驟

上篇博文已經闡述了我們是如何從三維計數表格轉到霍夫梯度法的,該演算法主要分為兩步,先找圓心疑似點,再對疑似點進行半徑確定:
假設已經得到影像的邊緣資訊(包含角度和梯度值)
1.利用邊緣點的梯度資訊,沿著梯度方向畫線,將將線段經過的所有累加器中的點(a,b)的Hough(h,w)+=1。
此步原理是:圓上的點沿著梯度方向畫線,這些線會交於圓心。
2.統計排序霍夫累加器中的投票時,得票是越高,說明更多得邊緣梯度線經過該點,是圓心的可能性就更大。
3.針對某個圓心計算所有邊緣點到其的距離,認為頻數較大的距離為可能半徑值。(個人的簡化做法)

問題

1.如何沿著邊緣點的梯度方向畫線?
博主採用的斜截式來表示梯度線(即為邊緣的法線),首先我們獲得的梯度方向角為:

theta = np.arctan2(I2,(I1+0.0000000000001))*180/np.pi

帶著這個賊小的數是為了不出現分母為零的情況!這裡排除了k=inf的情況,但是還是會出現k值比較大的情況,為了畫線方便做了這樣一步操作:

#篩選k值(即把小於0.05或者超過50的k值剔除)
index = []
for i in range(len(k)):
    if abs(k[i])<0.05 or abs(k[i])>50:
        index.append(i)
new_k = np.delete(k,index)
new_location = np.delete(location,index,axis=0)

在影像中畫直線時其經過的不是質點而是畫素點(有物理長寬),所以本文先求取了各個邊緣點梯度線的k和b值,因此經過邊緣點(x0,y0)的直線可表示為:y0=k*x0+b,以一個畫素為單位不斷增加x值,利用直線方程求取對應的y值,以此來求得直線經過的所有畫素點。

2.對k值進行分類處理
第一問已經剔除了難以利用的k值及其對應的邊緣點座標,接下來要對k值進行分類。由第一問已經知道博文求取梯度線經過的畫素點的方法是步進法(自己胡謅的名字),會出現一個新的問題,是在x軸上步進好還是在y軸上步進好呢?這就是為什麼要對k值進行一個分類。
當k的絕對值小於1時,意味著每增加一個畫素值,y的增加(或減少)不超過一個畫素值,是可以遍歷該直線經過的所有畫素的。但如果是增加y值,那麼x會增加幾個畫素值,這樣會跳過好幾畫素塊,故博主將k小於1的分出來,對於分出來的邊緣點,進行x軸上的遞進。(本處偷了懶~其實按照原理也可以對k=0或k=inf的邊緣點進行操作的)

3.在尋找一些需要注意的問題
博主初步畫梯度直線時,針對的是canny識別出來的邊緣進行操作的,但是識別的圓心不準。改為對梯度幅值不為零的所有畫素點畫梯度方向直線後發現效果好了很多,即從上面的程式碼改為下面的:

location = np.argwhere(edge==255)
location = np.argwhere(G)

對比:
在這裡插入圖片描述
在這裡插入圖片描述
4.半徑求取的辦法:
博主用了最簡單的思路,求取所有的邊緣點到可能圓心的距離,認為半徑值是距離的眾數字。

d = np.sqrt((location[:,0]-h[0])**2+(location[:,1]-w[0])**2)
d = d.astype(int)
c = []
for i in d:
     if i not in c:
        c.append(i)
#進行統計,生成二維列表
a = {}
for i in d:
    if i in a:
        a[i] = a[i] + 1
    else:
        a[i] = 1
    # 使用sorted對字典進行排序
b = sorted(a.items(),key=lambda item:item[1],reverse=True)
r = b[0][0]

最後效果圖:
在這裡插入圖片描述
可以看到其實最後的結果不太理想,反思問題可能是中間只對k值小於一的邊緣點進行了處理,而且在演算法中我直接認定投票值最多的畫素點為圓心,沒有進行下一步處理,在獲取半徑時候也只是簡單取了眾數,總而言之還有很多紕漏沒解決啦~僅作參考哈,多有不足望大家指出。

相關文章