5.20. 迴歸與相似度¶
Image 類別上還有另外兩項測量,它們將影像概括為像素值分布以外的東西。對閾值化像素進行線性迴歸,可給應用程式一條可據以動作的「直線」 —— 這是循線機器人的典型輸入。相似度測量則給應用程式一個描述兩張影像有多相似的單一數字 —— 這是黃金影像迴歸測試或粗略變化偵測器的天然輸入。
5.20.1. 線性迴歸¶
當影像的前景像素恰好在影格上構成一條「直線」時 —— 機器人所循的軌道上的膠帶、地平線的線條、道路或走廊的邊緣 —— 應用程式通常不想要每一個個別的前景像素,而是想要穿過所有這些像素的「最佳擬合線」,並將其參數化,以便判斷直線的朝向以及它在何處穿越影格。
get_regression() 會進行這項擬合。它採用與 binary() 及 find_blobs() 相同的閾值元組形式,找出每一個符合閾值的像素,並回傳一個描述穿過那些像素的最佳擬合線的 line 結果:
line = img.get_regression([(0, 60)])
if line:
img.draw_line((line.x1(), line.y1(),
line.x2(), line.y2()),
color=(255, 0, 0))
此擬合採用 Theil-Sen 線性迴歸 —— 一種比更為人熟知的最小平方擬合更能容忍離群值的穩健方法。少數遠離真實直線的像素,不會像在最小平方法中那樣扭曲結果,這正符合真實閾值輸出中前景帶有雜訊的實際情況。
line 結果攜帶裁剪至影像矩形範圍的端點(x1、y1、x2、y2)、直線長度與幅值(length、magnitude),以及直線以極座標形式表達的幾何描述(theta、rho) —— 也就是直線相對於水平方向的角度,以及它到原點的垂直距離。極座標形式正是控制迴路通常想要的:theta 告訴機器人直線朝哪個方向傾斜,rho 告訴它直線在何處穿越影像,而對這兩者的回授迴路便能讓機器人保持在直線中央。
少數幾個關鍵字引數可調校穩健性與計算成本。x_stride 與 y_stride 會在擬合過程中跳過像素 —— 步幅越大,迴歸成本越低,代價是擬合的像素較少。area_threshold 與 pixels_threshold 會剔除背後沒有足夠符合像素的直線。target_size 會在擬合前將輸入重新縮放到較小尺寸 —— 迴歸在影像的 80x60 替身上執行得更快,而直線方向的準確度損失不大。
若無法擬合出可接受的直線 —— 例如閾值未符合任何像素,或所符合的圖樣看起來不像直線 —— 該方法會回傳 None。真正的循線程式碼會在取用直線屬性之前,以 None 檢查來防護每一次 get_regression() 呼叫。
5.20.2. 影像相似度¶
另一種測量:不問「影像包含什麼?」,而問「這兩張影像有多相似?」。要使用的運算是 get_similarity(),它會計算來源影像與參考影像之間的「結構相似性指數」(SSIM)。
s = img.get_similarity(reference)
print(s.mean, s.stdev)
SSIM 是影像處理領域通用的標準影像相似度度量,因為它的行為符合人類對相似度的直覺 —— 小幅位移或小幅亮度變化會略微降低分數,而大幅結構變化(物件缺失、場景不同)則會使其大幅下降。分數範圍從 -1 到 +1:+1 表示兩張影像完全相同,0 表示兩者無關,-1 則表示兩者在結構上相反。回傳的 similarity 物件會公開整張影像的平均 SSIM,以及各圖塊(tile)分數的標準差、最小值與最大值。
對於「小」數字優於「大」數字的那類比較 —— 例如迴歸測試應在「沒有任何變化」時回報為零,並隨變化累積而上升 —— dssim=True 旗標會回傳結構不相似度:以 1 減去平均 SSIM,因此完全相同的影像其回傳值為 0.0,並隨兩者差異增大而上升。
5.20.3. SSIM 的使用情境¶
兩種常見的應用:
黃金影像迴歸測試。測試框架在已知良好的條件下擷取一張參考影格,並將其儲存為黃金影像。後續的測試執行會在相同條件下擷取,並以 SSIM 與黃金影像比較。分數高於某個閾值(依容忍度而定為 0.95 或 0.98)即為通過;低於則為失敗。測試框架不需要知道「什麼」改變了 —— SSIM 分數就是訊號。
粗略變化偵測。想要一種較粗略影格差異化的應用程式 —— 忽略小幅亮度變化但對大幅結構變化有反應 —— 可以對參考影格使用 SSIM,而非逐像素的 difference() 後接閾值。SSIM 對照明漂移的敏感度低於逐像素差異化,這使得當目標是偵測「場景看起來有實質不同」而非「任何個別像素有所變化」時,SSIM 成為較佳的選擇。
兩種應用都使用相同的呼叫 —— img.get_similarity(reference) —— 並在回傳分數的某個閾值上觸發。差別僅在於該閾值是高(迴歸測試,尋找近乎相同的匹配)還是低(變化偵測,尋找任何大幅結構變化)。
5.20.4. 轉換並比較的形式¶
一個實用的細微之處:get_similarity() 接受與 draw_image() 相同的 x、y、x_scale、y_scale、roi、rgb_channel、alpha、color_palette、alpha_palette、hint 與 transform 參數。參考影像會先依這些參數進行定位、縮放與轉換,「然後」才執行 SSIM 比較。
這表示應用程式可以詢問「在經過已知的位移/旋轉/縮放『之後』,這個場景與參考影格有多相似」,而無需事先準備一張預先轉換好的參考影像。這是一種省成本的做法,可用來建構一個在參數空間中搜尋、並回報參考影像的哪一種轉換最能匹配目前影格的追蹤器。