特征點匹配+特征檢測方法匯總


特征提取與匹配---SURF;SIFT;ORB;FAST;Harris角點

匹配方法

匹配函數

 

1. OpenCV提供了兩種Matching方式

• Brute-force matcher (cv::BFMatcher) //暴力方法找到點集1中每個descriptor在點集2中距離最近的descriptor;找尋到的距離最小就認為匹配
//浮點描述子-歐氏距離;二進制描述符-漢明距離。
//詳細描述:
在第一幅圖像中選取一個關鍵點然后依次與第二幅圖像的每個關鍵點進行(描述符)距離測試,最后返回距離最近的關鍵點 • Flann-based matcher (cv::FlannBasedMatcher) //速最近鄰搜索算法尋找(用快速的第三方庫近似最近鄰搜索算法)
//是一個對大數據集和高維特
征進行最近鄰搜索的算法的集合,在面對大數據集時它的效果要好於BFMatcher。

//使用FLANN匹配需要傳入兩個字典參數:
一個參數是IndexParams,對於SIFT和SURF,可以傳入參數index_params=dict(algorithm=FLANN_INDEX_KDTREE, trees=5)。

對於ORB,可以傳入參數index_params=dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)。
第二個參數是SearchParams,可以傳入參數search_params=dict(checks=100),它來指定遞歸遍歷的次數,值越高結果越准確,但是消耗的時間也越多。

 

 

cv::BFMatcher(int normType=NORM_L2, bool crossCheck=false),如下所示: 1. normType:它是用來指定要使用的距離測試類型,默認值為cv2.Norm_L2,這很適合SIFT和SURF等(c2.NORM_L1也可)。對於使用二進制描述符的ORB、BRIEF和BRISK算法等,要使用cv2.NORM_HAMMING, 這樣就會返回兩個測試對象之間的漢明距離。如果ORB算法的參數設置為WTA_K==3或4,normType就應該設置成cv2.NORM_HAMMING2。 2. crossCheck:默認值為False。如果設置為True,匹配條件就會更加嚴格,只有到A中的第i個特征點與B中的第j個特征點距離最近,並且B中的第j個特征點到A中的第i個特征點也是最近時才會返回最佳匹配(i,j),
即這兩個特征點要互相匹配才行。 BFMatcher對象有兩個方法BFMatcher.match()和BFMatcher.knnMatch()。第一個方法會返回最佳匹配。第二個方法為每個關鍵點返回k個最佳匹配,其中k是由用戶設定的。
cv2.drawMatches()來繪制匹配的點,它會將兩幅圖像先水平排列,然后在最佳匹配的點之間繪制直線。
如果前面使用的是BFMatcher.knnMatch(),現在可以使用函數cv2.drawMatchsKnn為每個關鍵點和它的個最佳匹配點繪制匹配線,如果要選擇性繪制就要給函數傳入一個掩模。

一般,點集1稱為 train set (訓練集)的對應模板圖像,點集2稱為 query set(查詢集)的對應查找模板圖的目標圖像

為了提高檢測速度,你可以調用matching函數前,先訓練一個matcher。訓練過程可以首先使用cv::FlannBasedMatcher來優化,為descriptor建立索引樹,這種操作將在匹配大量數據時發揮巨大作用。

而Brute-force matcher在這個過程並不進行操作,它只是將train descriptors保存在內存中。


2. matching過程---使用cv::DescriptorMatcher的如下功能來進行匹配:

  • 簡單查找最優匹配:
void match( const Mat& queryDescriptors, std::vector<DMatch>& matches, const std::vector<Mat>& masks = std::vector<Mat>() );
  • 為每個descriptor查找K-nearest-matches:
void knnMatch( const Mat& queryDescriptors, std::vector<std::vector<DMatch> >& matches, int k, const std::vector<Mat>& masks = std::vector<Mat>(), bool compactResult = false );
  • 查找那些descriptors間距離小於特定距離的匹配:
void radiusMatch( const Mat& queryDescriptors, std::vector<std::vector<DMatch> >& matches, float maxDistance, const std::vector<Mat>& masks = std::vector<Mat>(), bool compactResult = false );

 

3. matching結果包含許多錯誤匹配,錯誤的匹配分為兩種:

  • False-positive matches: 將非對應特征點檢測為匹配(我們可以對他做文章,盡量消除它)
  • False-negative matches: 未將匹配的特征點檢測出來(無法處理,因為matching算法拒絕)
為了消除False-positive matches采用如下兩種方式:
  • Cross-match filter:在OpenCV中 cv::BFMatcher class已經支持交叉驗證,建立 cv::BFMatcher將第二參數聲明為true---cv::BFMatcher(cv::NORM_HAMMING,true)
  • Ratio test:使用KNN-matching算法,令K=2。則每個match得到兩個最接近的descriptor,然后計算最接近距離和次接近距離之間的比值,當比值大於既定值時,才作為最終match。
  • RANSAC:隨機樣本一致性方法因為我們是使用一幅圖像(一個平面物體),可以將它定義為剛性的,在pattern image和query image的特征點之間使用cv::findHomography找到單應性變換(homography transformation),再使用RANSAC找到最佳匹配,從而找到最佳單應性矩陣。(由於cv::findHomography這個函數使用的特征點同時包含正確和錯誤匹配點,因此計算的單應性矩陣依賴於二次投影的准確性,下面有詳細解釋)
 

 

代碼說明---OpenCV3中特征點的提取和匹配

OpenCV中封裝了常用的特征點算法(如SIFT,SURF,ORB等),提供了統一的接口,便於調用。 下面代碼是OpenCV中使用其feature 2D 模塊的示例代碼

    Mat img1 = imread("F:\\image\\1.png"); Mat img2 = imread("F:\\image\\2.png"); // 1. 初始化
    vector<KeyPoint> keypoints1, keypoints2; Mat descriptors1, descriptors2; Ptr<ORB> orb = ORB::create(); // 2. 提取特征點
    orb->detect(img1, keypoints1); orb->detect(img2, keypoints2); // 3. 計算特征描述符
    orb->compute(img1, keypoints1, descriptors1); orb->compute(img2, keypoints2, descriptors2); // 4. 對兩幅圖像的BRIEF描述符進行匹配,使用BFMatch,Hamming距離作為參考
    vector<DMatch> matches; BFMatcher bfMatcher(NORM_HAMMING); bfMatcher.match(descriptors1, descriptors2, matches);

 

  1. 獲取檢測器的實例
    在OpenCV3中重新的封裝了特征提取的接口,可統一的使用Ptr<FeatureDetector> detector = FeatureDetector::create()來得到特征提取器的一個實例,所有的參數都提供了默認值,也可以根據具體的需要傳入相應的參數。
  2. 在得到特征檢測器的實例后,可調用的detect方法檢測圖像中的特征點的具體位置,檢測的結果保存在vector<KeyPoint>向量中。
  3. 有了特征點的位置后,調用compute方法來計算特征點的描述子,描述子通常是一個向量,保存在Mat中。
  4. 得到了描述子后,可調用匹配算法進行特征點的匹配。上面代碼中,使用了opencv中封裝后的暴力匹配算法BFMatcher,該算法在向量空間中,將特征點的描述子一一比較,選擇距離(上面代碼中使用的是Hamming距離)較小的一對作為匹配點。

上面代碼匹配后的結果如下:

特征點的匹配后的優化

特征的匹配是針對特征描述子進行的,上面提到特征描述子通常是一個向量,兩個特征描述子的之間的距離可以反應出其相似的程度,也就是這兩個特征點是不是同一個。

根據描述子的不同,可以選擇不同的距離度量。如果是浮點類型的描述子,可以使用其歐式距離;對於二進制的描述子(BRIEF)可以使用其漢明距離(兩個不同二進制之間的漢明距離指的是兩個二進制串不同位的個數)。

有了計算描述子相似度的方法,那么在特征點的集合中如何尋找和其最相似的特征點,這就是特征點的匹配了。最簡單直觀的方法就是上面使用的:暴力匹配方法(Brute-Froce Matcher),計算某一個特征點描述子與其他所有特征點描述子之間的距離,然后將得到的距離進行排序,取距離最近的一個作為匹配點。這種方法簡單粗暴,其結果也是顯而易見的,通過上面的匹配結果,也可以看出有大量的錯誤匹配,這就需要使用一些機制來過濾掉錯誤的匹配。

  • 漢明距離小於最小距離的兩倍
    選擇已經匹配的點對的漢明距離不大於最小距離的兩倍作為判斷依據,如果不大於該值則認為是一個正確的匹配,過濾掉;大於該值則認為是一個錯誤的匹配。其實現代碼也很簡單,如下:

    // 匹配對篩選
    double min_dist = 1000, max_dist = 0; // 找出所有匹配之間的最大值和最小值
    for (int i = 0; i < descriptors1.rows; i++) { double dist = matches[i].distance; if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; } // 當描述子之間的匹配不大於2倍的最小距離時,即認為該匹配是一個錯誤的匹配。 // 但有時描述子之間的最小距離非常小,可以設置一個經驗值作為下限
    vector<DMatch> good_matches; for (int i = 0; i < descriptors1.rows; i++) { if (matches[i].distance <= max(2 * min_dist, 30.0)) good_matches.push_back(matches[i]); }

     

     

    結果如下:

對比只是用暴力匹配的方法,進行過濾后的匹配效果好了很多。

  • 交叉匹配
    針對暴力匹配,可以使用交叉匹配的方法來過濾錯誤的匹配。交叉過濾的思想很簡單,再進行一次匹配,反過來使用被匹配到的點進行匹配,如果匹配到的仍然是第一次匹配的點的話,就認為這是一個正確的匹配。舉例來說就是,假如第一次特征點A使用暴力匹配的方法,匹配到的特征點是特征點B;反過來,使用特征點B進行匹配,如果匹配到的仍然是特征點A,則就認為這是一個正確的匹配,否則就是一個錯誤的匹配。OpenCV中BFMatcher已經封裝了該方法,創建BFMatcher的實例時,第二個參數傳入true即可,BFMatcher bfMatcher(NORM_HAMMING,true)

  • KNN匹配
    K近鄰匹配,在匹配的時候選擇K個和特征點最相似的點,如果這K個點之間的區別足夠大,則選擇最相似的那個點作為匹配點,通常選擇K = 2,也就是最近鄰匹配。對每個匹配返回兩個最近鄰的匹配,如果第一匹配和第二匹配距離比率足夠大(向量距離足夠遠),則認為這是一個正確的匹配,比率的閾值通常在2左右
    OpenCV中的匹配器中封裝了該方法,上面的代碼可以調用bfMatcher->knnMatch(descriptors1, descriptors2, knnMatches, 2);具體實現的代碼如下:

    const float minRatio = 1.f / 1.5f; const int k = 2; vector<vector<DMatch>> knnMatches; matcher->knnMatch(leftPattern->descriptors, rightPattern->descriptors, knnMatches, k); for (size_t i = 0; i < knnMatches.size(); i++) { const DMatch& bestMatch = knnMatches[i][0]; const DMatch& betterMatch = knnMatches[i][1]; float  distanceRatio = bestMatch.distance / betterMatch.distance; if (distanceRatio < minRatio) matches.push_back(bestMatch); }

    將不滿足的最近鄰的匹配之間距離比率大於設定的閾值(1/1.5)匹配剔除。

  • RANSAC

隨機采樣一致性(RANSAC)可過濾掉錯誤的匹配,該方法利用匹配點計算兩個圖像之間單應矩陣,並分解得到位姿R,t,通過三角測量來得到兩個關聯特征對應的3D點,將3D點按照當前估計的位姿進行投影,也就是重投影,然后利用重投影誤差(觀測到得投影位置(像素坐標)與3D點進行重投影的位置之差)來判定某一個匹配是不是正確的匹配。

OpenCV中封裝了求解單應矩陣的方法findHomography,可以為該方法設定一個重投影誤差的閾值,可以得到一個向量mask來指定那些是符合該重投影誤差的匹配點對(Inliers),以此來剔除錯誤的匹配,代碼如下:

const int minNumbermatchesAllowed = 8; if (matches.size() < minNumbermatchesAllowed) return; //Prepare data for findHomography
vector<Point2f> srcPoints(matches.size()); vector<Point2f> dstPoints(matches.size()); for (size_t i = 0; i < matches.size(); i++) { srcPoints[i] = rightPattern->keypoints[matches[i].trainIdx].pt; dstPoints[i] = leftPattern->keypoints[matches[i].queryIdx].pt; } //find homography matrix and get inliers mask
vector<uchar> inliersMask(srcPoints.size()); homography = findHomography(srcPoints, dstPoints, CV_FM_RANSAC, reprojectionThreshold, inliersMask); vector<DMatch> inliers; for (size_t i = 0; i < inliersMask.size(); i++){ if (inliersMask[i]) inliers.push_back(matches[i]); } matches.swap(inliers);

備注:OpenCV的特征點匹配及一些剔除錯誤匹配的文章,OpenCV2:特征匹配及其優化,使用的是OpenCV2,在OpenCV3中更新了特征點檢測和匹配的接口,不過大體還是差不多的。

 

主要包括以下幾個內容:

  • DescriptorMatcher

DescriptorMatcher是匹配特征向量的抽象類,在OpenCV2中的特征匹配方法都繼承自該類(例如:BFmatcher,FlannBasedMatcher)。

該類主要包含了兩組匹配方法:圖像對之間的匹配以及圖像和一個圖像集之間的匹配。

用於圖像對之間匹配的方法的聲明

復制代碼
 // Find one best match for each query descriptor (if mask is empty).  CV_WRAP void match( const Mat& queryDescriptors, const Mat& trainDescriptors, CV_OUT vector<DMatch>& matches, const Mat& mask=Mat() ) const; // Find k best matches for each query descriptor (in increasing order of distances). // compactResult is used when mask is not empty. If compactResult is false matches vector will have the same size as queryDescriptors rows.
// If compactResult is true matches vector will not contain matches for fully masked out query descriptors. CV_WRAP void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, CV_OUT vector<vector<DMatch> >& matches, int k, const Mat& mask=Mat(), bool compactResult=false ) const; // Find best matches for each query descriptor which have distance less than maxDistance (in increasing order of distances). void radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<vector<DMatch> >& matches, float maxDistance, const Mat& mask=Mat(), bool compactResult=false ) const;
復制代碼

方法重載,用於圖像和圖像集匹配的方法聲明

    CV_WRAP void match( const Mat& queryDescriptors, CV_OUT vector<DMatch>& matches, const vector<Mat>& masks=vector<Mat>() ); CV_WRAP void knnMatch( const Mat& queryDescriptors, CV_OUT vector<vector<DMatch> >& matches, int k, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false ); void radiusMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
  • DMatcher

DMatcher 是用來保存匹配結果的,主要有以下幾個屬性

    CV_PROP_RW int queryIdx; // query descriptor index CV_PROP_RW int trainIdx; // train descriptor index  CV_PROP_RW int imgIdx; // train image index CV_PROP_RW float distance;

在圖像匹配時有兩種圖像的集合,查找集(Query Set)和訓練集(Train Set),對於每個Query descriptor,DMatch中保存了和其最好匹配的Train descriptor。另外,每個train image會生成多個train descriptor。

如果是圖像對之間的匹配的話,由於所有的train descriptor都是由一個train image生成的,所以在匹配結果DMatch中所有的imgIdx是一樣的,都為0.

  • KNN匹配
  • 計算兩視圖的基礎矩陣F,並細化匹配結果
  • 計算兩視圖的單應矩陣H,並細化匹配結果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM