一文搞定opencv中常見的關(guān)鍵點檢測算法(附代碼)
作者:K.Fire | 來源:計算機視覺工坊
前言
角點時圖像中存在物體邊緣角落位置的點或者一些特殊位置的點,角點檢測(Corner Detection)是計算機視覺系統(tǒng)中獲取圖像特征的一種方法,是運動檢測、圖像匹配、視頻跟蹤、三維重建和目標識別的基礎(chǔ)。
本篇文章將介紹opencv中常用的幾種角點檢測方法的原理和基于C++的實現(xiàn)。
一、Harris角點檢測
Harris角點原理:設(shè)置一個矩形框,將這個矩形框放置在圖像中,將矩形框內(nèi)像素進行求和,然后移動矩形框,當(dāng)相鄰兩次求和得到的值差別較大時,判定矩形框內(nèi)存在Harris角點。
通常出現(xiàn)的位置:直線的端點處、直線的交點處、直線的拐點處。
但是如果按照上述檢測原理檢測,會很麻煩,很難確定矩形框移動的方向和當(dāng)框內(nèi)出現(xiàn)角點時,角點的具體位置。
因此,在程序中一般是用以下方式進行檢測:1.定義Harris角點檢測函數(shù)為:
其中I(x,y)為(x,y)位置的像素值,w(x,y)為矩形框的權(quán)重矩陣,定義這個矩陣的原因是:當(dāng)矩形框內(nèi)檢測到可能存在角點時,權(quán)重矩陣中權(quán)重更大的位置更有可能是角點。2.將上述公式表示為矩陣形式:
其中M為梯度協(xié)方差矩陣,表示為以下公式:
3.然后使用Harris角點評價函數(shù)判斷矩形框內(nèi)是否存在角點:
其中det是矩陣的行列式,k是一個自定義的常數(shù),tr是矩陣的跡。使用上述方式計算起來計算消耗比較大,因此換用以下方式進行評價:
其中λ是梯度協(xié)方差矩陣M的特征向量,根據(jù)這個評價系數(shù),當(dāng)評價系數(shù)值較大時矩形框內(nèi)有可能存在角點,但當(dāng)評價系數(shù)較小時舉行框內(nèi)一定不存在角點。具體原因如下圖所示,當(dāng)λ1和λ2都是一個較大的值時才能確定一定存在角點,而當(dāng)其中一個值遠遠大于另一個時,也會使得R值很大,但這時很有可能位于“邊緣”區(qū)域,而且評價精度也跟自定義的常數(shù)k有很大關(guān)系。
Harris角點檢測的代碼如下:
#include?<iostream>#include?<openCV2/opencv.hpp>using?namespace?std;using?namespace?cv;int?main(){?/*Harris角點檢測*/?string?path?=?"Lena.png";?Mat?img?=?imread(path,?1);?Mat?img_gray,?harris;?cvtColor(img,?img_gray,?COLOR_BGR2GRAY);?cornerHarris(img_gray,?harris,?2,?3,?0.04);??//歸一化便于進行數(shù)值比較和結(jié)果顯示?Mat?harrisn;?normalize(harris,?harrisn,?0,?255,?NORM_MINMAX);?//將圖像數(shù)據(jù)類型轉(zhuǎn)換為CV_8U?harrisn.convertTo(harrisn,?CV_8U);?//尋找Harris角點?vector<KeyPoint>?keyPoints;?for?(int?row?=?0;?row?<?harrisn.rows;?row++)?{??for?(int?col?=?0;?col?<?harrisn.cols;?col++)??{???int?R?=?harrisn.at<uchar>(row,?col);???if?(R?>?125)???{????//將角點存入KeyPoints中????KeyPoint?keyPoint;????keyPoint.pt.x?=?row;????keyPoint.pt.y?=?col;????keyPoints.push_back(keyPoint);???}??}?}??drawKeypoints(img,?keyPoints,?img);?imshow("系數(shù)矩陣",?harrisn);?imshow("img",?img);??waitKey(0);?exit(0);?return?0;}
運行結(jié)果:
二、Shi-Tomas角點檢測
在上一節(jié)中介紹的Harris角點計算方法中,最后我們發(fā)現(xiàn)存在一個問題:角點檢測的精度跟自定義的常數(shù)k有很大關(guān)系,Shi-Tomas角點檢測就是對于Harris角點中的這一問題作出了改善,它重新定義了評價系數(shù)計算方式:
根據(jù)這個評價函數(shù),評價系數(shù)取值為λ1和λ2中的較小值,然后用R與自定義的閾值進行比較,當(dāng)其中當(dāng)較小的特征向量都滿足條件時,則該點一定為角點。
如下圖所示,當(dāng)較小的特征向量都滿足條件時,角點區(qū)域為圖中綠色區(qū)域,一定為角點,而圖中橙色區(qū)域可能位于邊緣,灰色區(qū)域一定不是角點。
Shi-Tomas角點檢測代碼如下:
#include?<iostream>#include?<openCV2/opencv.hpp>using?namespace?std;using?namespace?cv;int?main(){?/*Shi-Tomas角點檢測*/?string?path?=?"Lena.png";?Mat?img?=?imread(path,?1);?Mat?img_gray;?cvtColor(img,?img_gray,?COLOR_BGR2GRAY);??vector<Point2f>?corners;?//Shi-Tomas角點檢測?goodFeaturesToTrack(img_gray,?corners,?100,?0.01,?0.1);?//繪制角點?vector<KeyPoint>?keyPoints;?for?(int?i?=?0;?i?<?corners.size();?i++)?{??KeyPoint?keyPoint;??keyPoint.pt.x?=?corners[i].x;??keyPoint.pt.y?=?corners[i].y;??keyPoints.push_back(keyPoint);?}?drawKeypoints(img,?keyPoints,?img);?imshow("img",?img);?waitKey(0);?exit(0);?return?0;}
運行結(jié)果:
三、亞像素級別角點位置優(yōu)化
通過上面的角點檢測算法,雖然能在原圖像中檢測出角點的位置,但仔細觀察函數(shù)可以發(fā)現(xiàn)檢測出的角點坐標都為整數(shù)值,這是因為圖像是由像素構(gòu)成的,是一個離散的數(shù)據(jù)類型,如果我們想要的到更精細的角點坐標,就需要對角點位置進行優(yōu)化。
優(yōu)化的原理如下圖所示,當(dāng)一個像素點q是角點時,它在自己的領(lǐng)域附近是一個局部最大值,以q為起點,周圍一點p1或p0為終點組成一個向量,并求梯度,此時向量與梯度的積應(yīng)該是0。
當(dāng)在opencv中檢測出角點后(此時的角點位置時不精確的),我們就根據(jù)這個原理不斷調(diào)整角點q的位置,直到達到一定的精度或迭代次數(shù)。
而在實際的計算過程中,通常是通過以下公式進行計算:
就是對q與周圍點組成向量與梯度的積求和,當(dāng)這個和小于一定值時,就判定為達到一定的精度。
亞像素級別角點位置優(yōu)化代碼如下:
#include?<iostream>#include?<openCV2/opencv.hpp>using?namespace?std;using?namespace?cv;int?main(){?/*Shi-Tomas角點檢測*/?string?path?=?"Lena.png";?Mat?img?=?imread(path,?1);?Mat?img_gray;?cvtColor(img,?img_gray,?COLOR_BGR2GRAY);??vector<Point2f>?corners;?//Shi-Tomas角點檢測?goodFeaturesToTrack(img_gray,?corners,?100,?0.01,?0.1);?cout?<<?"檢測到的角點數(shù)量:"?<<?corners.size()?<<?endl;?//繪制角點?vector<KeyPoint>?keyPoints;?for?(int?i?=?0;?i?<?corners.size();?i++)?{??KeyPoint?keyPoint;??keyPoint.pt.x?=?corners[i].x;??keyPoint.pt.y?=?corners[i].y;??keyPoints.push_back(keyPoint);?}?drawKeypoints(img,?keyPoints,?img);?//亞像素角點位置優(yōu)化?vector<Point2f>?cornersSub?=?corners;?TermCriteria?criteria?=?TermCriteria(TermCriteria::EPS?+?TermCriteria::COUNT,?40,?0.01);?cornerSubPix(img_gray,?cornersSub,?Size(3,?3),?Size(-1,?-1),?criteria);?for?(int?i?=?0;?i?<?corners.size();?i++)?{??cout?<<?"第"?<<?to_string(i)?<<?"個檢測出的角點坐標:?"?<<?corners[i]?<<?"???????角點位置優(yōu)化后:?"?<<?cornersSub[i]?<<?endl;?}?imshow("img",?img);?waitKey(0);?exit(0);?return?0;}
運行結(jié)果:
可以看到優(yōu)化后的角點位置出現(xiàn)了小數(shù),說明對其位置進行了優(yōu)化。
四、FAST角點檢測
相比于其他的角點檢測方法,F(xiàn)AST角點檢測的方法精度比較低,但FAST的優(yōu)點也非常明顯,就像它的名字一樣--FAST,就是快,它計算速度快,資源消耗少,在具有有限計算資源的情況下(如基于嵌入式設(shè)備的SLAM機器人)應(yīng)用非常廣泛。
FAST角點檢測的原理如下:
在圖像中任選一點p, 假定其像素值為Ip
以3為半徑畫圓,覆蓋p點周圍的16個像素,如下圖所示
設(shè)定閾值t,如果這周圍的16個像素中有連續(xù)的n個像素的像素值都小于(Ip?t)或大于(Ip+t),那么就判斷點p為角點。在OpenCV的實現(xiàn)中n取值為12(16個像素周長的 3/4).
一種更加快的改進是:首先檢測p點周圍的四個點,即1,5,9,12四個點中是否有三個點滿足超過Ip+t,如果不滿足,則直接跳過,如果滿足,則繼續(xù)使用前面的算法,全部判斷16個點中是否有12個滿足條件
FAST角點檢測代碼如下:
#include?<iostream>#include?<openCV2/opencv.hpp>using?namespace?std;using?namespace?cv;int?main(){?/*FAST角點檢測*/?string?path?=?"Lena.png";?Mat?img?=?imread(path,?1);?Mat?img_gray;?cvtColor(img,?img_gray,?COLOR_BGR2GRAY);?vector<KeyPoint>?keyPoints;?Ptr<FastFeatureDetector>?fast?=?FastFeatureDetector::create(32);?//FAST角點檢測器,32為閾值,閾值越大,特征點越少?fast->detect(img_gray,?keyPoints,?Mat());???????//FAST角點檢測?drawKeypoints(img,?keyPoints,?img);?????????//FAST角點繪制?imshow("img",?img);?waitKey(0);?exit(0);?return?0;}
運行結(jié)果如下:
FAST的缺點也很明顯:
不具備尺度不變性
不具備旋轉(zhuǎn)不變性
容易受到噪聲影響
ORB特征點檢測中專門針對上述缺點做了改進,使得FAST在保持運算速度快的優(yōu)勢下,更加魯棒。
五、ORB特征點檢測
ORB的全稱是ORiented Brief,它是將FAST角點檢測與BRIEF特征描述結(jié)合并進行了改進:
(1)由于要解決BRIEF算法的旋轉(zhuǎn)不變性,則需要計算特征點的主方向。ORB中利用重心來計算,如下(其中(x,y)是特征鄰域內(nèi)的點):
計算圖像的矩
計算矩形區(qū)域的質(zhì)心
連接FAST角點與質(zhì)心得到方向向量
得到的θ值就是FAST特征點的主方向.
(2) BRIEF描述子,它是一種二進制編碼的描述子,擯棄了利用區(qū)域灰度直方圖描述特征點的傳統(tǒng)方法,大大的加快了特征描述符建立的速度。
以特征點為中心,取SxS的鄰域窗口。在窗口內(nèi)隨機選取一對(兩個)點,比較二者像素的大小,進行如下二進制賦值。
在窗口中隨機選取n對隨機點,將其進行旋轉(zhuǎn),后進行判別,然后重復(fù)上述步驟的二進制賦值,形成一個二進制編碼,這個編碼就是對特征點的描述,即特征描述子。(一般N=256)
ORB特征點檢測代碼如下:
#include?<iostream>#include?<openCV2/opencv.hpp>using?namespace?std;using?namespace?cv;int?main(){?/*ORB特征點檢測*/?string?path?=?"Lena.png";?Mat?img?=?imread(path,?1);?Mat?img_gray;?cvtColor(img,?img_gray,?COLOR_BGR2GRAY);?vector<KeyPoint>?keyPoints;?Ptr<ORB>?orb?=?ORB::create(500,??????//特征點數(shù)目????????1.2f,?????//金字塔層級間的縮放比例????????8,??????//金字塔圖像層數(shù)系數(shù)????????31,??????//邊緣閾值????????0,??????//原圖像在金字塔中的層級????????2,??????//生成描述子時需要用的像素點數(shù)目????????ORB::HARRIS_SCORE,??//使用Harris方法評價特征點????????31,??????//生成描述子時關(guān)鍵點周圍鄰域的尺寸????????20??????//計算FAST角點時像素值差值的閾值????????);?//ORB特征點檢測器?orb->detect(img_gray,?keyPoints,?Mat());???????//ORB特征點檢測?Mat?descriptors;?orb->compute(img_gray,?keyPoints,?descriptors);??????//ORB描述子計算?drawKeypoints(img,?keyPoints,?img);???????????//ORB特征點繪制:不含有角度和大小?//drawKeypoints(img,?keyPoints,?img,?DrawMatchesFlags::DRAW_RICH_KEYPOINTS);?//ORB特征點繪制:含有角度和大小?imshow("img",?img);?waitKey(0);?exit(0);?return?0;}
運行結(jié)果:
總結(jié)
非常常用且經(jīng)典的特征檢測算法還有SIFT和SUFT算法,這兩種算法都是申請專利的,不能商用,在opencv中實現(xiàn)時需要先配置opencv_contrib拓展包,然后包含<xfeatures2d.hpp>頭文件,然后初始化方式與ORB檢測類似Ptr
關(guān)于SIFT特征點檢測可以看一下這篇文章(https://blog.csdn.net/wuzhongqiang/article/details/123969628)
其他的檢測方法就不過多展開,大家可以在網(wǎng)上自己找一下資料進行學(xué)習(xí)。
—END—目前工坊已經(jīng)建立了3D視覺方向多個社群,包括SLAM、工業(yè)3D視覺、自動駕駛方向,細分群包括:[工業(yè)方向]三維點云、結(jié)構(gòu)光、機械臂、缺陷檢測、三維測量、TOF、相機標定、綜合群;[SLAM方向]多傳感器融合、ORB-SLAM、激光SLAM、機器人導(dǎo)航、RTK|GPS|UWB等傳感器交流群、SLAM綜合討論群;[自動駕駛方向]深度估計、Transformer、毫米波|激光雷達|視覺攝像頭傳感器討論群、多傳感器標定、自動駕駛綜合群等。[三維重建方向]NeRF、colmap、OpenMVS等。除了這些,還有求職、硬件選型、視覺產(chǎn)品落地等交流群。大家可以添加小助理微信:?dddvisiona,備注:加群+方向+學(xué)校|公司, 小助理會拉你入群。