原文鏈接:http://www.juzicode.com/opencv-python-houghcircles
回傳OpenCV-Python教程
在OpenCV中HoughCircles()方法可以用來查找圓形,找到的圓形通過圓心位置和半徑進行描述,
1、介面
介面形式:
cv2.HoughCircles(image,method,dp,minDist[,circles[,param1[,param2[,minRadius[,maxRadius]]]]])->circles
- 引數含義:
- image:輸入影像,8bit單通道影像,
- method:檢測方法,當前有cv2.HOUGH_GRADIENT和cv2.HOUGH_GRADIENT_ALT 2種方法,后者是前者的改進方法,
- dp:檢測圓心的累加器精度和影像精度比的倒數,比如dp=1時累加器和輸入影像有相同的解析度,dp=2時累加器是輸入影像一半大的寬高;method=cv2.HOUGH_GRADIENT_ALT時推薦設定dp=1.5,
- minDist:檢測到圓心的間距,設定的越小可能檢測的圓形越多,設定的越大可能會錯過一些圓形的檢測,
- param1:特定方法引數,和method配合;當method=cv2.HOUGH_GRADIENT或method=cv2.HOUGH_GRADIENT_ALT時,該引數是canny檢測的高閾值,低閾值是該引數的一半;method=cv2.HOUGH_GRADIENT_ALT時,內部使用Scharr計算影像梯度,這個值通常要設定得更大,
- param2:特定方法引數,和method配合;當method=cv2.OUGH_GRADIENT,它表示檢測階段圓心的累加器閾值,越小就會檢測到更多的圓,越大能通過檢測的圓就更加精確,當method=cv2.HOUGH_GRADIENT_ALT時,該引數可以看做是圓的“完美性”度量,它越接近1演算法選擇的圓形形狀越好,一般可以設定在0.9,如果想要更好地檢測小圓,可以設定在0.85、0.8甚至更小,通過限制搜索范圍[minRadius,maxRadius]可以避免出現許多假圓,
- minRadius:最小圓半徑,
- maxRadius:最大圓半徑,如果設定為<=0,使用最大影像尺寸;如果<0時且method=cv2.HOUGH_GRADIENT用來查找圓心而忽略半徑的查找,method=cv2.HOUGH_GRADIENT_ALT不受影響,始侄訓去找半徑,
- circles:回傳的圓形的點,是一個三維陣列,HOUGH_GRADIENT和HOUGH_GRADIENT_ALT 2種不同方法回傳的圓形陣列形式有差異,后文有詳細介紹,
用HoughCircles()方法找到的圓形,通常圓心的準確率比較高,半徑的準確率比較低,可以借助minRadius和maxRadius 2個引數來限制半徑的范圍提高查找的精確度,
2、HOUGH_GRADIENT方法
因為method引數不一樣,param1或param2引數的含義不一樣,內部實作找圓的方法也不一樣,我們先來看下HOUGH_GRADIENT方法找圓形,
用HOUGH_GRADIENT方法時,先分別找x和y方向的Sobel梯度,再用Canny找邊沿:
Sobel(_image, dx, CV_16S, 1, 0, kernelSize, 1, 0, BORDER_REPLICATE);
Sobel(_image, dy, CV_16S, 0, 1, kernelSize, 1, 0, BORDER_REPLICATE);
Canny(dx, dy, edges, std::max(1, cannyThreshold / 2), cannyThreshold, false);
下面這個例子找出圖中的圓形并在原圖中繪制出來:
import numpy as np
import matplotlib.pyplot as plt
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')
plt.rc('axes',unicode_minus='False')
#讀入影像
img_src = cv2.imread('..\\samples\\picture\\houghcircle-basic.bmp')
print('img_src.shape:',img_src.shape)
img_disp=img_src.copy()
#轉為灰度圖
img_gray = cv2.cvtColor(img_src,cv2.COLOR_BGR2GRAY)
#找出圓形
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
#畫出圓形
circles = np.uint16(np.around(circles))
for cir in circles[0]:
print('circle.shape:',cir.shape,'circle:',cir)
cv2.circle(img_disp,(cir[0],cir[1]),cir[2],(0,255,0),2)
cv2.circle(img_disp,(cir[0],cir[1]),2,(0,0,255),3)
#顯示影像
fig,ax = plt.subplots(2,2)
ax[0,0].set_title('img_src')
ax[0,0].imshow(cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB))
ax[0,1].set_title('img_disp')
ax[0,1].imshow(cv2.cvtColor(img_disp,cv2.COLOR_BGR2RGB))
plt.show()
運行結果:
VX公眾號: 桔子code / juzicode.com
cv2.__version__: 4.5.3
img_src.shape: (312, 584, 3)
circles.shape: (1, 5, 3)
circles:
[[[346.5 215.5 67.6]
[160.5 189.5 66.8]
[ 59.5 38.5 29.9]
[442.5 43.5 28.6]
[534.5 42.5 29. ]]]
circle.shape: (3,) circle: [346 216 68]
circle.shape: (3,) circle: [160 190 67]
circle.shape: (3,) circle: [60 38 30]
circle.shape: (3,) circle: [442 44 29]
circle.shape: (3,) circle: [534 42 29]

從運行結果看,回傳的圓形是一個3維numpy陣列,shape屬性為(1, 5, 3),shape[0]固定為1,shape[1]為5表示包含5組圓形的引數,shape[2]的3個數值包含圓形的x,y坐標和半徑的值,
引數dp
回到前面的例子,僅僅修改dp引數為0.5:
#找出圓形 dp設定為1.5
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,0.5,20,param1=50,param2=30,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
當dp=0.5時,仍然是原來的5個圓形:
img_src.shape: (312, 584, 3)
circles.shape: (1, 5, 3)
circles:
[[[346.5 215.5 67.6]
[160.5 189.5 66.8]
[ 59.5 38.5 29.9]
[442.5 43.5 28.6]
[534.5 42.5 29. ]]]
但是當修改dp=1.5時:
#找出圓形 dp設定為1.5
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1.5,20,param1=50,param2=30,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
這時能找到33個圓形:
circles.shape: (1, 33, 3)
circles:
[[[159.75 191.25 65.399994]
[345.75 215.25 67.8 ]
[ 59.25 38.25 29.699999]
[534.75 42.75 29.699999]
[441.75 44.25 28.650002]
[321.75 192.75 98.25 ]
[318.75 216.75 93. ]
[176.25 177.75 47.699997]......

這些非預期的“圓形”都是從其他的圓形上“借來”的點構成的,

引數minDist
接下來看下minDist引數的改變引起的變化,
因為該引數表示檢測圓形的最小間距,如果將該間距增大,就會導致在該引數minDist范圍內只會檢測到一個圓形,比如在上例中,用畫圖板程式觀察右上角的2個小圓圓心間距為90個像素,我們將minDist設定為100時,就會導致只能檢測到一個圓形:
#找出圓形,minDist設定為100
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,100,param1=50,param2=30,minRadius=0,maxRadius=0)

將間距再增大到200時,下方的2個大圓也只能找到1個:
#找出圓形,minDist設定為100
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,100,param1=50,param2=30,minRadius=0,maxRadius=0)

引數param2
仍然回到最開始的例子,僅僅修改param2引數,當method=cv2.OUGH_GRADIENT,它表示檢測階段圓心的累加器閾值,越小就會檢測到更多的圓,越大能通過檢測的圓就更加精確,下面的例子將param2從30修改為25:
#找出圓形 param2設定為25
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=25,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
circles.shape: (1, 11, 3)
circles:
[[[346.5 215.5 67.6]
[160.5 189.5 66.8]
[ 59.5 38.5 29.9]
[442.5 43.5 28.6]
[534.5 42.5 29. ]
[151.5 207.5 50. ]
[341.5 193.5 47.8]
[366.5 216.5 47.8]
[326.5 209.5 47.7]
[174.5 211.5 43.6]
[350.5 244.5 38.6]]]

引數minRadius,maxRadius
同樣在原來的代碼基礎上,只修改minRadius:
#找出圓形 限制minRadius maxRadius
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=50,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
circles.shape: (1, 2, 3)
circles:
[[[346.5 215.5 67.6]
[160.5 189.5 66.8]]]
circle.shape: (3,) circle: [346 216 68]
circle.shape: (3,) circle: [160 190 67]
這時只找到2個半徑大于50的圓,其他3個半徑小于50的圓被忽略掉了:

同樣的用法,我們把maxRadius限制在35,就只會找到3個半徑小于35的圓形:

當maxRadius的值設定小于0時,HoughCircles()只找圓心不找半徑:
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=-1)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
circles.shape: (1, 5, 3)
circles:
[[[160.5 191.5 0. ]
[347.5 212.5 0. ]
[534.5 42.5 0. ]
[ 59.5 38.5 0. ]
[442.5 45.5 0. ]]]
circle.shape: (3,) circle: [160 192 0]
circle.shape: (3,) circle: [348 212 0]
circle.shape: (3,) circle: [534 42 0]
circle.shape: (3,) circle: [60 38 0]
circle.shape: (3,) circle: [442 46 0]
回傳的圓形陣列中,每組圓形資料中表示半徑的第3個數值為0,表示半徑為0,所以圓形的外圍就沒有再被標注為綠色,只標注了圓心的位置:

3、HOUGH_GRADIENT_ALT方法
用HOUGH_GRADIENT_ALT方法時,先分別找x和y方向的Scharr梯度,再用Canny找邊沿:
Scharr(img, Dx, CV_16S, 1, 0);
Scharr(img, Dy, CV_16S, 0, 1);
Canny(Dx, Dy, edges, cannyThreshold/2, cannyThreshold, true);
源圖片仍然是用HOUGH_GRADIENT方法的例子中使用的同一個圖片,method引數這時改為HOUGH_GRADIENT_ALT,param2=0.9,另外需要注意回傳圓形陣列形式的差異:
import numpy as np
import matplotlib.pyplot as plt
import cv2
print('VX公眾號: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)
plt.rc('font',family='Youyuan',size='9')
plt.rc('axes',unicode_minus='False')
#讀入影像
img_src = cv2.imread('..\\samples\\picture\\houghcircle-basic.bmp')
print('img_src.shape:',img_src.shape)
img_disp=img_src.copy()
#轉為灰度圖
img_gray = cv2.cvtColor(img_src,cv2.COLOR_BGR2GRAY)
#找出圓形 HOUGH_GRADIENT_ALT 方法
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT_ALT,1,20,param1=50,param2=0.9,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
#畫出圓形
circles = np.uint16(np.around(circles))
for cir_ in circles:
cir = cir_[0]
print('circle.shape:',cir.shape,'circle:',cir)
cv2.circle(img_disp,(cir[0],cir[1]),cir[2],(0,255,0),2)
cv2.circle(img_disp,(cir[0],cir[1]),2,(0,0,255),3)
img_bin = cv2.Canny(img_gray,50,150,apertureSize = 3)
#顯示影像
fig,ax = plt.subplots(2,2)
ax[0,0].set_title('img_src')
ax[0,0].imshow(cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB))
ax[0,1].set_title('img_disp')
ax[0,1].imshow(cv2.cvtColor(img_disp,cv2.COLOR_BGR2RGB))
ax[1,0].set_title('img_bin')
ax[1,0].imshow(img_bin,'gray')
plt.show()
運行結果:
VX公眾號: 桔子code / juzicode.com
cv2.__version__: 4.5.3
img_src.shape: (312, 584, 3)
circles.shape: (5, 1, 3)
circles:
[[[160. 192. 67.132744]]
[[348. 213. 67.159256]]
[[443. 45. 28.429234]]
[[535. 43. 28.45556 ]]
[[ 59. 38. 28.443163]]]
circle.shape: (3,) circle: [160 192 67]
circle.shape: (3,) circle: [348 213 67]
circle.shape: (3,) circle: [443 45 28]
circle.shape: (3,) circle: [535 43 28]
circle.shape: (3,) circle: [59 38 28]

從運行結果可以看到找到有的圓形位置和半徑跟HOUGH_GRADIENT方法是一樣的,但是要注意的是回傳的結果組織形式是有差異的,HOUGH_GRADIENT_ALT方法找到的圓形陣列的shape屬性為(5, 1, 3),其shape[0]表示找到圓形的個數,shape[1]則固定為1,shape[2]是圓的3個引數,分別表示圓形的x,y坐標和半徑;而HOUGH_GRADIENT方法找到的圓形陣列的shape屬性為(1, 5, 3),shape[0]固定為1,shape[1]為5表示包含5組圓形的引數,shape[2]是圓的3個引數,
入參dp
在上面的例子種修改dp的值為20:
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT_ALT,20,20,param1=50,param2=0.9,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
img_src.shape: (312, 584, 3)
circles.shape: (4, 1, 3)
circles:
[[[340. 220. 72.84901 ]]
[[440. 40. 28.715666]]
[[ 60. 40. 28.445187]]
[[540. 40. 31.973608]]]
circle.shape: (3,) circle: [340 220 73]
circle.shape: (3,) circle: [440 40 29]
circle.shape: (3,) circle: [60 40 28]
circle.shape: (3,) circle: [540 40 32]
得到的圓心精確度下降,某些圓心已經偏離了原來的位置:

引數minDist
和HOUGH_GRADIENT方法一樣,增大minDist入參的值會導致右上角的找到的圓形減少一個:
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT_ALT,1,100,param1=50,param2=0.9,minRadius=0,maxRadius=0)
運行結果:
circles.shape: (4, 1, 3)
circles:
[[[160. 192. 67.132744]]
[[348. 213. 67.159256]]
[[535. 43. 28.45556 ]]
[[ 59. 38. 28.443163]]]
circle.shape: (3,) circle: [160 192 67]
circle.shape: (3,) circle: [348 213 67]
circle.shape: (3,) circle: [535 43 28]
circle.shape: (3,) circle: [59 38 28]
引數param2
當method=HOUGH_GRADIENT_ALT時,param2引數的最大取值到1時表示最高精確度找圓,所以該數值越接近1,找到的圓就會越少,下面將該引數該為0.99:
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT_ALT,1,20,param1=50,param2=0.99,minRadius=0,maxRadius=0)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
circles.shape: (3, 1, 3)
circles:
[[[160. 192. 67.03482 ]]
[[348. 213. 67.37982 ]]
[[535. 43. 28.421442]]]
circle.shape: (3,) circle: [160 192 67]
circle.shape: (3,) circle: [348 213 67]
circle.shape: (3,) circle: [535 43 28]
這時只能找到3個圓形:

桔子菌嘗試修改param2=1.0時一個圓形也找不到,這時要求被查找的圓形十分“完美”,
而當修改param2=0.1或0.01時,仍然只找到5個圓形,并沒有像HOUGH_GRADIENT方法那樣param2引數急劇變化后找到了非常多“錯誤”的圓形,
引數minRadius,maxRadius
這2個引數的用法和在method=HOUGH_GRADIENT方法中一樣,可以限制圓形的半徑,
但是maxRadius有個差異的地方在于即使設定為-1,HOUGH_GRADIENT_ALT方法仍然會找圓的半徑:
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT_ALT,1,20,param1=50,param2=0.9,minRadius=0,maxRadius=-1)
print('circles.shape:',circles.shape)
print('circles:\n',circles)
運行結果:
circles.shape: (5, 1, 3)
circles:
[[[160. 192. 67.132744]]
[[348. 213. 67.159256]]
[[443. 45. 28.429234]]
[[535. 43. 28.45556 ]]
[[ 59. 38. 28.443163]]]
小結:houghCircles()找圓的方法有2種:HOUGH_GRADIENT和HOUGH_GRADIENT_ALT,通過method引數傳入來區分,二者回傳結果的組織形式存在極大差別,其陣列的shape屬性下標0和1的含義相互做了調換,使用時需要特別注意,minDist、minRadius引數對于2種方法而言沒有什么差異,maxRadius為負數時HOUGH_GRADIENT方法不找半徑,HOUGH_GRADIENT_ALT仍然查找半徑,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/400398.html
標籤:其他
上一篇:Python爬蟲案例50篇-第18篇-使用PySimpleGUI對視頻爬蟲做個可視化界面
下一篇:目標檢測之YOLOv4
