5.27. 尋找圓形與矩形

直線與線段涵蓋了擷取影格中的直線邊緣,但相機要尋找的許多真實世界特徵並非直的。桌上的一枚硬幣是個圓形。一張印刷標籤、一張便利貼、或從斜角觀看的盒子頂面則是個四邊形。image 模組為每一種都提供了專用的偵測器:用於圓形的 Hough 式搜尋,以及衍生自 AprilTag、用於四邊形狀的搜尋。

這兩種方法都遵循與直線偵測器相同的範本——threshold 控制一次偵測所需的票數、roi 縮小搜尋範圍,而傳回的物件同時帶有位置與一個信賴度量值——但兩者的參數空間與適當的預設值差異夠大,值得分別說明。

5.27.1. Hough 圓形

find_circles() 執行 Hough 變換的圓形變體。來自 Sobel 預處理的每一個邊緣像素,都會為所有可能通過它的圓形投票;累積到足夠票數的圓形便會被傳回。

circles = img.find_circles(threshold=3500,
                            x_margin=10, y_margin=10, r_margin=10,
                            r_min=10, r_max=80, r_step=2)

for c in circles:
    img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))

threshold 是候選圓形上 Sobel 邊緣量值的最小總和。較大的圓形會描繪較多的像素,因此需要較高的閾值才能通過;一個能找到半徑 20 像素硬幣的數值,也會在 100 像素邊緣周圍的雜訊上觸發,而為大硬幣調校的數值則會漏掉小硬幣。當已知目標半徑範圍時,適當的閾值會與圓周成比例——大致上 threshold = 50 * 2 * pi * r 提供了合理的起始點,而適當的數值可透過簡短的調校過程得出。

r_minr_maxr_step 設定半徑搜尋。若無界限,偵測器會搜尋從數個像素到影像半寬之間的每一種半徑,這既緩慢又是造成誤判的根源。將 r_minr_max 設定為以寬裕的餘量框住預期目標尺寸(例如已知約 20 像素的硬幣使用 r_min=15, r_max=25),可大幅減少運算量並改善投票的訊雜比。r_step 控制搜尋的粒度;較大的步進執行較快,但可能漏掉真實半徑落在兩個取樣值之間的圓形。預設的 r_step=2 是個合理的折衷。

x_marginy_marginr_margin 控制鄰近偵測結果的合併,方式與直線偵測中 theta_marginrho_margin 的作用相同。影像中單一的實體圓形,會為一群中心與半徑彼此相差數個像素以內的候選圓形投票;在建立結果清單之前,這些餘量會將每個叢集收斂至其峰值。較大的餘量會傳回較少、但更具信賴度的偵測結果;較小的餘量則傳回較多、但可能有近乎重複的偵測結果。

x_stridey_stride 控制投票掃描的步進,方式與在其他偵測器中相同。預設值 21 適用於大多數影像;對於已知含有大圓形的影像,將兩者都調高至 4 是標準的速度權衡作法。

每個傳回的 Circle 都帶有 xyr(中心與半徑)以及 magnitude(票數總和,可作為排序或過濾用的信賴度分數)。將偵測結果繪回影格只需一次呼叫——draw_circle() 接受 (x, y, r) 三元組,可直接從結果以 (c.x, c.y, c.r) 取得。

5.27.2. 矩形

find_rects() 借用了 AprilTag 流程中的四邊形偵測器——也就是定位標籤周圍黑色方框的同一個常式,被單獨提供作為通用的矩形尋找器。

rects = img.find_rects(threshold=12000)

for r in rects:
    img.draw_rectangle(r.rect, color=(0, 255, 0))
    for corner in r.corners:
        img.draw_circle((corner[0], corner[1], 4),
                        color=(0, 255, 0))

threshold 是矩形周界上邊緣量值的最小總和。在光線充足的影格中,一個白底黑色印刷矩形可輕鬆超過 10000;而在帶有紋理背景上的淡薄矩形可能需要降至 2000——以誤判換取靈敏度。與圓形偵測器一樣,適當的數值可在目標物入鏡的情況下透過快速調校過程得出。

此偵測器是投影式的——它會尋找邊為直線、但不必然平行或對齊座標軸的四邊形。從斜角觀看的標籤在影像中看起來像個梯形,而矩形偵測器能正確地找到它;對齊座標軸的矩形只是四個角恰好構成直角方框的退化情形。roi 限制搜尋範圍;其餘的關鍵字引數會採用 AprilTag 流程的預設值,鮮少需要調校。

每個傳回的 Rect 都帶有對齊座標軸的邊界框——xywh,再加上 draw_rectangle() 所預期的 rect 四元組——以及偵測到的四個角,以 corners 提供。應用程式使用邊界框來取得粗略的位置與尺寸;而那些角則描述了投影四邊形本身。當相機從某個角度觀看平面目標,而應用程式需要消除梯形失真時——例如讀取標籤上的文字、或從平坦色塊取樣色彩——這些角可透過 corners= 關鍵字直接送入 rotation_corr()(請參閱 鏡頭與透視校正),其輸出便是已校正好、可供後續任何分析使用的矩形。

警告

由於此偵測器是針對 AprilTag 流程的需求所調校——也就是具有強烈、高對比邊界的四邊形,例如白紙上的黑色標籤輪廓——它並非一個能找出每一個矩形的處理過程。對比柔和、邊緣帶有紋理、或周遭環境繁雜的矩形可能完全無法被偵測到。它的效果好壞視情況而定:請及早針對實際目標物進行測試,再圍繞它建立流程。

5.27.3. 當偵測器誤觸發時

圓形尤其能受益於對輸入施加預先過濾。雜訊多的影格會產生大量零散的邊緣像素,它們全都會投票,導致產生的 Hough 空間出現寬闊而模糊的峰值,使合併器難以將它們區分開來。在 find_circles() 之前先施加一道 gaussian()mean() 處理,可將雜訊平滑掉並保留真實的邊緣完好無損;偵測器便能在更短的時間內傳回更乾淨的峰值。

對於矩形,常見的失效模式則相反:矩形與其背景之間的對比過低,意味著邊緣量值的總和始終無法超過 threshold。施加一道 histeq() 處理,將亮度範圍重新分布至完整的 0 到 255 範圍,便能恢復偵測器所需的對比。(對比必須存在於影像中的某處;直方圖均衡化只能放大已經存在的部分。)