本次作業目的在於實作Harris corner detection,並分為以下四點說明:
Harris corner detection theory:
說明cornerHarris()原理及演算架構
Code Segment Explanation:
說明實作cornerHarris()的程式碼
Result Comparison And Analysis:
調整四項參數,threshold, block size, kernal size, alpha,並分析調整每種參數所帶來的變化
additional testing:
實作三個和cornerHarris()相關的函式分別為cornerEigenValsAndVecs(), cornerMinEigenVal(), cornerSubPix()
作業環境:
Windows 11
OpenCV 4.6.0
Visual Studio 2022
在辨識一張影像時,首先要先提取影像的特徵,特徵點隱含的意思是,在一張影像中找不到重複的區域,也意味著特徵點和周圍顏色變化大,所以開始發展Corner detection。Corner 位於兩條邊緣的交點,特色是往任意方向移動,梯度值均會發生變化。
根據Corner的特性列出以下算式,欲找出和周圍梯度值變化大的區域。首先,先計算sum of square difference,求出intensity變化情形。
接著,對式子進行簡化,先用Taylor Series求得近似,再將近似後的式子以矩陣表示。因此sum of square difference可以近似如下。
E(u, v)又可近似於quadratic functions,矩陣M為positive define matrix,所以為橢圓形,再來,因為M矩陣為對稱矩陣,可以使用spectral decomposition,分解成eigenvalue 和方向向量,以橢圓標準式表示後了解到可以由eigenvalue求出兩軸軸長。
若將整張影像帶入式子計算,再將近似的橢圓疊加上去,以下三張圖可以觀察到當window選擇到不同區域時的變化,軸長均很短且圖形接近圓形則位於plat region,短軸長和長軸軸常差異很大則位於edge region,短軸長和長軸軸常均很長則位於corner region。
flat region
edge region
corner region
但求出eigenvalue 計算量太龐大,所以須找出其他判斷機制,如右,改為求出 corner response function。根據下圖,可以看出corner response function計算出的R和特徵的關係。若R很小則位於plat region,若R小於0則位於edge region,若R很大則位於corner region。
以下為實現Harris corner detection的流程圖,由OpenCV的函式cornerHarris()完成。
右圖為我所選擇的輸入影像,因為corner detection可以應用在人臉辨識,所以我想模擬此應用的效果,但真正的人臉太精細怕效果不顯著,所以我選擇人臉的立體模擬圖,它簡化了頭像但頭像該有的特徵均有清楚的呈現出來。
以下為輸入影像的程式碼,和之前練習的程式碼不同,作者使用CommandLineParser() ,將固定的參數放在這個class裡,以簡化程式碼。
CommandLineParser(argc, argv[], keys)
argc:參數的數量(來自maim())
argv:參數的陣列(來自main())
keys:{參數名稱 | 參數 | 描述參數}
右圖為cornerHarris()的片段程式碼,在函式裡是使用Sobel operator來取得x和y方向的gradient,參數格式如下:
cv::cornerHarris(InputArray , OutputArray, Neighborhood size, kernalsize, k, borderType)
輸入影像為8bit的灰階圖,所以要使用前須先使用cvtColor(src, src_gray, COLOR_BGR2GRAY); 將圖片由彩色照片轉換成8bit灰階類型
操作完corner detection後接下來使用 normalize(), convertScaleAbs()兩個函式來正規化讓結果得以顯示
normalize() : 讓影像正規化在0到255之間
convertScaleAbs() : 將結果正規化使得結果以8bit空間儲存,正規化公式如下:
dst(I)=saturate\_cast<uchar>(|src(I)∗alpha+beta|)
操作完正規化後的結果如左圖,影像只留下邊緣線,白色點標示的是corner
將corener使用 circle() 標示原點為corner半徑為5pixel。我在程式碼的最後加入addWeighted() 將結果與原圖疊加,corner在原圖裡的位置,方便觀察結果。
作者在程式碼裡加入createTrackbar("Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo); 可以直接在影像上拖曳滑桿,改變threshold數值。
程式執行完的結果如下,第一張圖為輸入影像,可以看到上方有滑改可以直接改變threshold數值,第二張圖是在留下物件邊緣並圈出corner,第三張圖標示在輸入影像中corner的位置。
int thresh = 165;
int blockSize = 2;
int apertureSize = 5
double k = 0.04;
以上是左圖的設定的參數,由結果會發現,畫面全黑且在執行的時候耗費很多時間,代表說threshold 設定過低,整個畫面均被標示為corner
int thresh = 165;
int blockSize --> change
int apertureSize = 3;
double k = 0.04;
參數設定如左,固定三項參數,改變不同的block size,block size意思是做corner一次考慮多少個neighbourhood。結果如下,隨著一次考慮的範圍增加,偵測到corner減少,推測是因為block size增加後,需是intensity變化劇烈的地方才會被偵測為corner。
block size = 2
block size = 3
block size = 5
int thresh = 200;
int blockSize = 2;
int apertureSize --> change
double k = 0.04;
參數設定如左,固定三項參數,改變不同的apertureSize,意思是改變sobel的kernel size, 在前幾次的作業中知道,若調大sobel的kernel size會偵測到更多的細節,結果如下,偵測到的corner也會隨著sobel的kernel size增加而增加。
kernal size = 3
kernal size = 5
int thresh = 200;
int blockSize = 2;
int apertureSize = 3
double k --> change;
參數設定如左,固定三項參數,改變不同的k值,意思是改變corner response function中的alpha值,隨著k值增加偵測到的corner數增加。
alpha constant = 0.04
alpha constant = 0.05
alpha constant = 0.06
int thresh --> change
int blockSize = 2;
int apertureSize = 3
double k = 0.04;
參數設定如左,固定三項參數,改變不同的threshold值,計算完corner response function後的數值需大於threshold才會被判定為corner,因此結果如下,隨著threshold增加,corner點減少。
Threshold = 155
Threshold = 165
Threshold = 200
從以上模擬可以了解到,使用corner detection對人臉提取特徵,所偵測到的特徵點多數聚集在眼窩、鼻翼、兩側嘴角、眉骨。
此函式使用 Sobel計算輸入影像的autocorrelation matrix,並求出autocorrelation matrix 的eigenvectors 和 eigenvalues,回傳值為( λ1, λ2, x1, y1, x2, y2 ),共兩組eigenvectors 和 eigenvalues。
和前面實作的cornerHarris()差異很大,cornerEigenValsAndVecs()只做了Harris corner detection的流程圖中的前兩步驟,實踐corner dection的程式碼如右。
求出( λ1, λ2, x1, y1, x2, y2 ) 六個值以後,依照原理,使用λ1, λ2計算corner response function,接著找出區域內最大值,最後標示出corner點。
以下為模擬結果,我將參數設定為:
int thresh = 200;
int blockSize = 2;
int apertureSize = 3
此函示和cornerHarris()使用上差異不大,差異在cornerMinEigenVal() 是計算autocorrelation matrix的最小eigenvalue。
將影像傳入cornerMinEigenVal()計算後,讓做完corner detection的影像可視需做正規化,為了讓做完正規化後的結果清晰可辨,我將數值平移100,最後將corner標示出來。
以下為模擬結果,參數設定為:
int thresh = 200;
int blockSize = 2;
int apertureSize = 3;
可以觀察到,第二張結果圖和前面不同的是邊緣線段較不清晰,但有一些白色點,corner則呈現最白。
這個函式和前面則是不同概念,cornerSubPix() 目的在於找出精確的corner位置。
首先先使用goodFeaturesToTrack()找出 strong corners ,並標示出來,之後才使用cornerSubPix()計算出corner精確的位置,並將結果打印出來。
以下為模擬結果,參數設定為:
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
gradientSize = 3;
double k = 0.04;
結果如下,從打印結果可以看到精確度到小數點後三位。
參數均設定為:
int thresh = 200;
int blockSize = 2;
int apertureSize = 3;
比較三張結果圖,cornerHarris()和 cornerEigenValsAndVecs()的兩張圖因為三數設定相同,產生的結果圖視覺上也相同,使用cornerMinEigenVal()雖然參數也設定相同,但所偵測到的corner則減少很多。
cornerHarris()
cornerEigenValsAndVecs()
cornerMinEigenVal()
接下來我在實踐cornerHarris()的程式碼中也加入一行程式碼將corner位置打印出來,計算一下數量,共有14個corner,因此我將cornerSubPix()跑出來的結果拖曳滑桿讓corner也同樣呈現14個,結果如下。
首先先看打出來的結果,cornerHarris()僅呈現整數cornerSubPix()則計算到小數點後三位,大致上整數部分數值都很接近。在看到結果圖,標示出的corner幾乎都有重複,差異最多的是嘴角的部分,cornerSubPix()沒有偵測到兩側嘴角,右眼的部分則像是cornerSubPix()因為更精確,所以將cornerHarris()重疊的點分開。
cornerHarris()參數均設定為:
int thresh = 200;
int blockSize = 2;
int apertureSize = 3;
cornerHarris()
cornerSubPix()
因為有先寫過Harris corner detection的數學原理和演算架構的作業,這次在解讀程式碼的部分沒有太大的問題,比較花時間研究的是到cornerEigenValsAndVecs(), cornerMinEigenVal(), cornerSubPix(),一開始我以為三種函使是cornerHarris()的改良版所以直接以cornerHarris()的程式碼做改寫,但失敗,所以就去參考OpenCV所提供的程式碼,才發現之間的差異。
我嘗試使用Matlab完成Harris corner detection,但目前未成功。程式碼似乎不完整,因為有幾個必須自己寫的函數,而且每個函數都會相互影響,因此我無法確定真正的問題在哪裡。不過,我猜測問題可能出在最後一個步驟,也就是取區域內最大值作為corner的函數。
以下是我使用Matlab進行Harris角點檢測但未成功的結果圖:
Harris corner detector , OpenCV, 4.6.0 Open Source Computer Vision
Harris Corner Detection , OpenCV, 4.6.0 Open Source Computer Vision
cv::CommandLineParser Class Reference , OpenCV, 4.6.0 Open Source Computer Vision
A Beginner-friendly Overview of Corner Detection Algorithms , Aug 30, 2021 , Medium
OpenCV基础(27)CommandLineParser使用 , 2021-12-01 , CSDN
Operations on arrays Core functionality , OpenCV, 4.6.0 Open Source Computer Vision
Drawing Functions Image Processing , OpenCV, 4.6.0 Open Source Computer Vision
Operations on arrays Core functionality , OpenCV, 4.6.0 Open Source Computer Vision
Feature Detection Image Processing , OpenCV, 4.6.0 Open Source Computer Vision
Creating your own corner detector , OpenCV, 4.6.0 Open Source Computer Vision
Detecting corners location in subpixels , OpenCV, 4.6.0 Open Source Computer Vision
samples/cpp/lkdemo.cpp , OpenCV, 4.6.0 Open Source Computer Vision
Creating your own corner detector , OpenCV, 4.6.0 Open Source Computer Vision
source:im_harris.m@317 , TUDelf