Programming 4
Harris Corner Detection
Goal
使用 OpenCV 練習 Harris corner detector 實作。
了解 Harris corner detector 各參數設定造成的不同結果。
作業環境
硬體規格:
CUP:12th Gen Intel(R) Core(TM) i5-12400 2.50 GHz
RAM:32G
軟體規格:
作業系統:Windows 10 專業版
C++ IDE:Visual Studio Community 2022 (64 位元) 版本 17.5.1
OpenCV:4.7.0/vc16
初始設定
將命名空間"cv"和"syd"中的所有元素(例如函數、類、變量等)引入當前的作用域,使得在代碼中可以直接使用這些元素而不需要指定命名空間。
圖像定義、閥值設定、視窗設定以及cornerHarris_demo的函式原型。
主程式
這邊參考 OpenCV 官網的程式碼,在主程式中有兩個新認識的物件CommandLineParser 和函式createTrackbar ,針對此二部分做一些簡單說明紀錄。
CommandLineParser 是 OpenCV 提供的一個類別,可以用來解析命令列參數。
argc: 代表傳遞給程式的命令列參數的數量。
argv: 代表傳遞給程式的命令列參數的陣列,其中第一個元素為程式名稱,其餘元素為使用者傳入的參數。
@input: 為自定義的參數名稱,用於取得使用者傳入的圖片檔案路徑。
building.jpg: 若使用者未傳入圖片檔案路徑時,預設使用的圖片檔案路徑。
input image: 在印出說明訊息時,顯示給使用者的訊息,提醒使用者該參數的用途。
createTrackbar 是 OpenCV 中用來創建一個滑動條的函數,用於在視窗中調整某個變量的值,通常應用在圖像處理中對閥值或者其他參數進行調節。
const String& trackbarname:滑動條的名稱。
const String& winname:要在哪個視窗中創建滑動條。
int* value:指向變量的指針,通常是一個整型變量,表示滑動條的初始值,調節後會改變該變量的值。
int count:變量可取的最大值,一般為 255。
TrackbarCallback onChange:當滑動條的值發生變化時,會調用的回調函數。
這邊做個小測試,將目錄下放入兩個影像檔案building和car。
直接執行會默認讀取先前設定的 building.jpg。
接著進入屬性設定>偵錯>命令參數。
輸入自定義的-input和希望讀取的影像名稱。
命令引數設定完成後重新建置專案並執行,就會讀取我所設定的影像了。
這個功能的好處是不需把影像路徑寫死,需要更改影像時也不需要修改程式碼,直接從命令行給指令就可以了。
同時也看到 createTrackbar 函式的調整拉桿,可以拉動拉桿調整 Thresh 值,而當滑動條的值發生變化時,會再次調整 cornerHarris_demo 函數的結果。
cornerHarris_demo函式
參數設定:
blockSize:區域大小。
apertureSize:kernel大小。
k:經驗參數。
使用 cornerHarris 函數對灰度圖(src_gray)進行角點檢測,將結果儲存在 dst 圖像中。
進行圖像的歸一化,將角點強度的數值範圍縮放到0到255之間,並將歸一化後的結果儲存在 dst_norm 中。
將 dst_norm 轉換為8位無符號整數,儲存在 dst_norm_scaled 中,目的是為了方便後續的展示和處理。
通過迴圈遍歷 dst_norm 中的每個像素,如果某個像素的值大於閾值(thresh),就在 dst_norm_scaled 中繪製一個圓圈表示這是一個角點。
將 dst_norm_scaled 圖像顯示出來,展示檢測到的角點。
主程式執行
原圖尺寸500*375。
Thresh=200。
blockSize = 2。
apertureSize = 3。
k = 0.04。
在這樣的設定下,偵測出的角點非常少,接下來會更改各項參數觀察變化,每次只更改一個參數,其餘參數皆為固定的情況。
調整Thresh,降低最後被視為角點的最低閥值。(直接顯示於原圖,並且改為紅圈,比較好觀察)
可見閾值調低後,更多被認為可能是角點的地方被標示出來了,但要注意閾值不能低於一定數值,否則會變成整張圖會被辨識成角點,因為在梯度計算中整張圖像都是有數值存在的。
調整 Thresh 是針對同一個梯度圖產生不同的結果,所以並不會影響梯度圖本身的結果。
調整blocksize(將Thresh固定在150較好觀察)
blocksize=2
blocksize=6
blocksize=10
blockSize 是在計算角點響應函數時要考慮的像素鄰域大小,鄰域越大可以更好地平滑圖像,減少噪聲的干擾,但同時可能也會減少角點的數量,而越小則可以更好地檢測細小的角點,但同時也會更加敏感於噪聲和圖像中的細節。
調整ksize
ksize=3
ksize=5
ksize=7
ksize是計算角點響應函數時使用的 Sobel 算子的大小,越大可以更好地捕捉到角點的紋理信息,但同時可能也會捕捉到不需要的信息,導致誤檢測。越小則可以更好地捕捉角點的細節,但可能會因為細節信息不足而遺漏一些角點。因此,調整 ksize 可以在不同場景下獲得更好的角點檢測效果。
若不調整其他參數的情況使用 7 的 ksize 導致整張圖的誤判,必須同時對其他參數做調整才能得到理想的結果。
調整經驗參數k
k=0.04。
k=0.06。
k=0.09。
k 參數控制了 Harris 角點檢測算法中對角點響應函數中參數的加權,通常取較小的值(0.04 ~ 0.06)。較大的 k 值會增加角點的響應,但同時也會增加噪聲的響應,導致誤檢測。
For the Matlab code
透過MathWorks.com-Detectharrisfeatures網站了解在MATLAB中可以使用detectharrisfeatures函數做出類似在OpenCV使用CornerHarris的效果。
讀入影像轉為灰階型態,使用 detectharrisfeatures 的默認參數設定尋找角點,並用紅圈標示出來。
默認參數為:
FilterSize: 3,kernel大小為3。
K: 0.04,Harris角點響應中的 k 值為0.04。
NumOctaves: 1,金字塔的層數為1。
ROI:整個圖像,不設定感興趣區域。
MinQuality: 0.01,角點最小品質為0.01。
FilterCoefficients:濾波器係數,自動計算。
代碼段解釋
centers = points.Location
在 points 中得到所有角點的座標,儲存在 centers 中。
radii = repmat(5, size(points, 1), 1)
使用 repmat 函數將半徑設為 5,存在 radii 中。
viscircles(centers, radii, 'Color', 'r');
使用 viscircles 函數在影像上繪製紅色圓圈,以標示出所有角點的位置。
這是默認參數的結果,看起來多了許多不需要的角點。
接著設定了 MinQuality 值從原本的默認 0.01 改為 0.1 和 0.5,此值在 Matlab 中指的是角點的"最低品質",類似OpenCV的閥值,在這範圍為0~1。
MinQuality = 0.1
MinQuality = 0.5
Matlab 和 OpenCV 結果比較
將 Matlab 程式碼中的 MinQuality 參數設定為 0.58 (類似 OpenCV 的 Thresh=150)
Matlab
kSize = 3
k = 0.04
MinQuality = 0.58
OpenCV
blockSize = 2
kSize = 3
k = 0.04
Thresh = 150
結果分析
我將兩個程式碼分別調整成相近的設定,由於 Matlab 的 detectharrisfeatures 函數沒有 blockSize 參數能夠設置,以及閥值設定有所不同,造成的結果有些差異,但這也有可能是 OpenCV 的 cornerHarris 函式以 Sobel 為濾波方式,而 Matlab 的 detectharrisfeatures 則是以高斯為濾波方式而成為差異的因素。
而另外,在觀察完所有結果時發現此實驗的正確性似乎不夠高,在許多應該為明顯角點的地方並未偵測出來,這或許是我挑選了一張不適合進行此實驗的影像,由於影像中的雜訊和細節太多了,導致正確性降低。