P6: Feature Matching

Goal:學習實作局部特徵描述符的特徵匹配

作業環境  

Theory explanation

(1) template matching

是一種影像處理的技術。在一張影像中尋找特定模板位置,目是找到與給定模板最相似的區域,並確定其在原始影像中的位置。將模板圖像滑動或匹配到原始影像中的各個位置,然後計算模板和該位置下原始影像區域之間的相似度或差異度,可以選擇相似度或差異度最高(或最低)的位置作為最佳匹配點。相似度度量通常使用像素之間的差異或相關性,常見的度量方法包括歐式距離、相關係數、相關性匹配等。模板匹配應用廣泛,例如:物體檢測、特徵追蹤、圖像配準和文字識別。需要注意的是,模板匹配對於光線、尺度、旋轉等變化較大的情況可能不太適用,因為它僅僅比對像素的相似度或差異度。

尋找特定模板位置

template

(2) knn(K-Nearest Neighbors) matching

一種在影像處理和模式識別中常用的匹配算法。基於鄰近搜尋的方法,用於在特徵空間中找到與給定特徵最相似的K個鄰居,選取距離最小或相似度最高的特徵點作為匹配結果。KNN matching的特點是簡單且直觀,並且不需要事先訓練模型。它在處理影像匹配和物體識別等問題時非常有用。應用在特徵匹配、物體識別、近似搜索。KNN matching在處理大數據集時可能效率較低,因為它需要計算待匹配點與所有樣本之間的距離。KNN matching還需要選擇合適的K值過小的K值可能會導致噪聲影響,而過大的K值可能會導致匹配不精確。因此KNN matching,需要根據具體情況進行參數調節和性能優化。

相似性一般用空間內兩個點的距離來度量。距離越大,表示兩個越不相似。計算距離的方式有很多種,如右圖。Euclidean Distance是 Minkowski Distance在p=2時的特例。

(3) radius matching

一種影像處理和特徵匹配的方法,它用於找到與給定特徵點在特徵空間中距離在一定範圍內的所有鄰居。根據給定的半徑範圍,將距離在該範圍內的特徵點視為鄰居。選取距離在半徑範圍內的特徵點作為匹配結果。需要指定一個半徑值,該半徑值決定了搜索鄰居的範圍適當調整半徑值可以獲得精確的匹配結果。較小的半徑值可能會導致缺少匹配點,而較大的半徑值可能會導致匹配點過多或不精確。常應用在特徵匹配、特徵追蹤、特徵匹配篩選。

(4) cross check

在特徵匹配中用於確定匹配的一致性和減少錯誤匹配。在傳統的特徵匹配算法中,會計算兩張影像(或影像中的特徵點)之間的特徵描述符距離或相似度,然後根據一個閾值來判斷是否為匹配點。但單純使用這種方法可能會導致一些錯誤的匹配,因為有些特徵點的描述符可能與其他特徵點非常相似

Cross check :

Cross check可以過濾掉那些單向匹配的錯誤,因為如果 A 是 B 的最近鄰,而 C 不是 A 的最近鄰,那麼這樣的匹配就可能是錯誤的。通過對特徵點的交叉檢查,可以增加匹配的一致性,減少錯誤匹配的機會。Cross check 簡單且易於實現,可以有效地降低不正確匹配的數量,通常與其他特徵匹配算法結合使用,如最近鄰算法(Nearest Neighbor)或基於距離閾值的匹配。Cross check 需要考慮匹配的一致性,但可能無法處理重復或遮擋的情況。在某些應用中,可能需要進一步的檢查和過濾,以確保得到高質量的匹配結果。

(5) ratio test

在特徵匹配中過濾掉不可靠的匹配點,從而提高匹配的準確性。這種方法可能會遇到一個問題,即某些特徵點的最近鄰可能與其距離非常接近,這導致了多個近似匹配。

Ratio test :

Ratio test的原理是基於觀察:對於正確的匹配點,其最近鄰的距離應該明顯小於次近鄰的距離,因此 R 的值應該接近於 1。而對於錯誤的近似匹配點,它們的最近鄰和次近鄰的距離可能非常接近,導致 R 的值接近於 1,這樣的匹配點就可以通過Ratio test被濾除。通常,我們將閾值設定為一個小於 1 的值,例如 0.8 或 0.9。這樣的設定可以過濾掉那些不可靠的近似匹配,保留更可靠的匹配點。

Matching local templates

影像匹配和模板匹配,使用FAST detector來檢測關鍵點,並使用模板匹配方法在兩張影像中找到最佳匹配的關鍵點。 

patches.cpp

#include <iostream>

#include <vector>

#include <opencv2/core.hpp>

#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

#include <opencv2/features2d.hpp>

#include <opencv2/objdetect.hpp>

 

int main()

{

  // image matching

 

  // 1. Read input images

  cv::Mat image1= cv::imread("C:/Users/User/Desktop/CV_testimage/image1.jpg", cv::IMREAD_GRAYSCALE);

  cv::Mat image2= cv::imread("C:/Users/User/Desktop/CV_testimage/image2.jpg", cv::IMREAD_GRAYSCALE);

 

  // 2. Define keypoints vector

  std::vector<cv::KeyPoint> keypoints1;

  std::vector<cv::KeyPoint> keypoints2;

 

  // 3. Define feature detector

  cv::Ptr<cv::FeatureDetector> ptrDetector;       // generic detector

  ptrDetector= cv::FastFeatureDetector::create(80);   // 選用 FAST detector

 

  // 4. Keypoint detection

  //檢測關鍵點

  ptrDetector->detect(image1,keypoints1);

  ptrDetector->detect(image2,keypoints2);

 

  std::cout << "Number of keypoints (image 1): " << keypoints1.size() << std::endl;

  std::cout << "Number of keypoints (image 2): " << keypoints2.size() << std::endl;

 

  // 5. Define a square neighborhood

  //定義特定大小的矩形,表示關鍵點周圍的]圖像塊

  const int nsize(11); // size of the neighborhood

  cv::Rect neighborhood(0, 0, nsize, nsize); // 11x11

  cv::Mat patch1;

  cv::Mat patch2;

 

  // 6. For all keypoints in first image

  // find best match in second image

  //在第二章圖中找出與第一幅圖像中的每個關鍵點最匹配的

  cv::Mat result;

  std::vector<cv::DMatch> matches;

 

  //for all keypoints in image 1

  //針對imaage1的全部關鍵點

  for (int i=0; i<keypoints1.size(); i++) {

 

       // define image1 patch 定義image1圖像塊

       neighborhood.x = keypoints1[i].pt.x-nsize/2;

       neighborhood.y = keypoints1[i].pt.y-nsize/2;

 

       // if neighborhood of points outside image, then continue with next point

       //如果鄰域超出圖像範圍,就繼續處理下一個點

       if (neighborhood.x<0 || neighborhood.y<0 ||

           neighborhood.x+nsize >= image1.cols || neighborhood.y+nsize >= image1.rows)

           continue;

 

       //patch in image 1

       patch1 = image1(neighborhood);

 

       // reset best correlation value;

       //存放最匹配的值

       cv::DMatch bestMatch;

 

       //for all keypoints in image 2

       //針對imaage2的全部關鍵點

  for (int j=0; j<keypoints2.size(); j++) {

 

           // define image2 patch 定義image2圖像塊

           neighborhood.x = keypoints2[j].pt.x-nsize/2;

           neighborhood.y = keypoints2[j].pt.y-nsize/2;

 

           // if neighborhood of points outside image, then continue with next point

           if (neighborhood.x<0 || neighborhood.y<0 ||

                neighborhood.x + nsize >= image2.cols || neighborhood.y + nsize >= image2.rows)

                continue;

 

           // patch in image 2

           patch2 = image2(neighborhood);

 

           // match the two patches匹配兩個圖像塊

           cv::matchTemplate(patch1,patch2,result, cv::TM_SQDIFF);

 

           // check if it is a best match

           //檢查是否為最佳匹配

           if (result.at<float>(0,0) < bestMatch.distance) {

 

                bestMatch.distance= result.at<float>(0,0);

                bestMatch.queryIdx= i;

                bestMatch.trainIdx= j;

           }

       }

 

       // add the best match

       //添加最佳匹配

       matches.push_back(bestMatch);

  }

 

  std::cout << "Number of matches: " << matches.size() << std::endl;

 

  // extract the 50 best matches 提取50個最佳匹配項

std::nth_element(matches.begin(),matches.begin()+50,matches.end());

  matches.erase(matches.begin()+50,matches.end());

 

  std::cout << "Number of matches (after): " << matches.size() << std::endl;

 

  // Draw the matching results

  //畫出匹配結果

  cv::Mat matchImage;

  cv::drawMatches(image1,keypoints1, // first image

                    image2,keypoints2, // second image

                    matches, // vector of matches

                    matchImage,  // produced image

              cv::Scalar(0,0,255),  // line color 紅色

                 cv::Scalar(0,0,255)); // point color 紅色

 

// Display the image of matches

  cv::namedWindow("Matches");

  cv::imshow("Matches",matchImage);

 

  // Match template

 

  // define a template

  cv::Mat target(image1,cv::Rect(80,105,30,30));

// Display the template

  cv::namedWindow("Template");

  cv::imshow("Template",target);

 

  // define search region

  cv::Mat roi(image2,

       // here top half of the image

       cv::Rect(0,0,image2.cols,image2.rows/2));

          

  // perform template matching

  cv::matchTemplate(

       roi, // search region

       target, // template

       result, // result

       cv::TM_SQDIFF); // similarity measure

 

  // find most similar location

  double minVal, maxVal;

  cv::Point minPt, maxPt;

  cv::minMaxLoc(result, &minVal, &maxVal, &minPt, &maxPt);

 

  // draw rectangle at most similar location

  // at minPt in this case

  cv::rectangle(roi, cv::Rect(minPt.x, minPt.y, target.cols , target.rows), 255);

 

// Display the template

  cv::namedWindow("Best");

  cv::imshow("Best",image2);

 

  cv::waitKey();

  return 0;

}

 


cv::Ptr<cv::FeatureDetector> ptrDetector;可以指向任何特徵檢測器,所以只需要在調用函數時更換檢測器,就可以運用在各種興趣點檢測。

使用FAST detector:cv::FastFeatureDetector::create(80);

檢測關鍵點

cv::matchTemplate()來比較兩個圖像塊。

[1]TemplateMatchModes:描述了可用比較方法的公式(I 表示圖像,T 模板,R 表示結果,M 表示可選掩碼)。 求和是在模板和/或圖像塊上完成的:x′=0...w−1,y′=0...h−1。函數完成比較後,可以使用 minMaxLoc 函數找到最佳匹配作為全局最小值(使用 TM_SQDIFF 時)或最大值(使用 TM_CCORRTM_CCOEFF 時)。 在彩色圖像的情況下,分子中的模板求和和分母中的每個求和在所有通道上完成,並且每個通道使用單獨的平均值。 也就是說,該函數可以採用顏色模板和彩色圖像。 結果仍然是單通道圖像,更易於分析。


drawMatches():從兩個圖像中繪製找到的關鍵點匹配項。

原圖:

image1(左)、image2(右)

輸出匹配的數量。

從matches中提取前50個最佳匹配。

匹配結果

Template

cv::Mat target(image1,cv::Rect(80,105,30,30));

找到最相似的位置,並在該位置繪製矩形

原圖:

image11(左)、image22(右)

輸出匹配的數量。

從matches中提取前50個最佳匹配。

匹配結果

Template

cv::Mat target(image1,cv::Rect(80,105,30,30));

找到最相似的位置,並在該位置繪製矩形

Describing and matching local intensity patterns

使用SURFSIFT兩種特徵檢測器,進行影像匹配和特徵檢測,並進行特徵點檢測、描述子提取、顯示匹配和匹配結果。

matcher.cpp

#include <iostream>

#include <vector>

#include <opencv2/core.hpp>

#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

#include <opencv2/features2d.hpp>

#include <opencv2/objdetect.hpp>

#include <opencv2/xfeatures2d.hpp>

 

int main()

{

  // image matching

 

  // 1. Read input images

  cv::Mat image1= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image1.jpg",cv::IMREAD_GRAYSCALE);

  cv::Mat image2= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image2.jpg",cv::IMREAD_GRAYSCALE);

  //cv::Mat image1 = cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p51.jpg", cv::IMREAD_GRAYSCALE);

  //cv::Mat image2 = cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p52.jpg", cv::IMREAD_GRAYSCALE);

 

  // 2. Define keypoints vector

  std::vector<cv::KeyPoint> keypoints1;

  std::vector<cv::KeyPoint> keypoints2;

 

  // 3. Define feature detector

  // Construct the SURF feature detector object

  cv::Ptr<cv::Feature2D> ptrFeature2D = cv::xfeatures2d::SURF::create(2000.0);

  // to test with SIFT instead of SURF

// cv::Ptr<cv::Feature2D> ptrFeature2D = cv::xfeatures2d::SIFT::create(74);

 

  // 4. Keypoint detection

  // Detect the SURF features

  ptrFeature2D->detect(image1,keypoints1);

  ptrFeature2D->detect(image2,keypoints2);

 

  // Draw feature points

  cv::Mat featureImage;

cv::drawKeypoints(image1,keypoints1,featureImage,cv::Scalar(255,255,0),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

// Display the corners

  cv::namedWindow("SURF");

  cv::imshow("SURF",featureImage);

 

  std::cout << "Number of SURF keypoints (image 1): " << keypoints1.size() << std::endl;

  std::cout << "Number of SURF keypoints (image 2): " << keypoints2.size() << std::endl;

 

  // SURF includes both the detector and descriptor extractor

 

  // 5. Extract the descriptor

cv::Mat descriptors1;

cv::Mat descriptors2;

  ptrFeature2D->compute(image1,keypoints1,descriptors1);

  ptrFeature2D->compute(image2,keypoints2,descriptors2);

 

// Construction of the matcher

  cv::BFMatcher matcher(cv::NORM_L2);

  // to test with crosscheck (symmetry) test

  // note: must not be used in conjunction with ratio test

// cv::BFMatcher matcher(cv::NORM_L2, true); // with crosscheck

  // Match the two image descriptors

std::vector<cv::DMatch> matches;

    matcher.match(descriptors1,descriptors2, matches);

 

// draw matches

cv::Mat imageMatches;

    cv::drawMatches(

    image1, keypoints1, // 1st image and its keypoints

    image2, keypoints2, // 2nd image and its keypoints

    matches,        // the matches

    imageMatches,   // the image produced

    cv::Scalar(0, 255, 255),  // color of lines

    cv::Scalar(0, 255, 255),  // color of points

    std::vector< char >(),  // masks if any

    cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

// Display the image of matches

  cv::namedWindow("SURF Matches");

  cv::imshow("SURF Matches",imageMatches);

 

  std::cout << "Number of matches: " << matches.size() << std::endl;

 

  // perform the ratio test

 

  // find the best two matches of each keypoint

  std::vector<std::vector<cv::DMatch> > matches2;

  matcher.knnMatch(descriptors1, descriptors2,

               matches2,

               2); // find the k (2) best matches

  matches.clear();

 

  // perform ratio test

  double ratioMax= 0.6;

std::vector<std::vector<cv::DMatch> >::iterator it;

  for (it= matches2.begin(); it!= matches2.end(); ++it) {

       //   first best match/second best match

       if ((*it)[0].distance/(*it)[1].distance < ratioMax) {

           // it is an acceptable match

           matches.push_back((*it)[0]);

 

       }

  }

  // matches is the new match set

 

    cv::drawMatches(

     image1,keypoints1, // 1st image and its keypoints

     image2,keypoints2, // 2nd image and its keypoints

  matches,       // the matches

     imageMatches,  // the image produced

  cv::Scalar(255,0,0),  // color of lines

  cv::Scalar(255,0,0),  // color of points

  std::vector< char >(), // masks if any

  cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

  std::cout << "Number of matches (after ratio test): " << matches.size() << std::endl;

 

// Display the image of matches

  cv::namedWindow("SURF Matches (ratio test at 0.6)");

  cv::imshow("SURF Matches (ratio test at 0.6)",imageMatches);

 

  // radius match

  float maxDist = 0.3;

  matches2.clear();

  matcher.radiusMatch(descriptors1, descriptors2, matches2,

                   maxDist); // maximum acceptable distance

                              // between the 2 descriptors

  cv::drawMatches(

       image1, keypoints1, // 1st image and its keypoints

       image2, keypoints2, // 2nd image and its keypoints

       matches2,      // the matches

       imageMatches,  // the image produced

       cv::Scalar(0, 255, 0),  // color of lines

       cv::Scalar(0, 255, 0),  // color of points

       std::vector<std::vector< char >>(), // masks if any

       cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

  int nmatches = 0;

  for (int i = 0; i< matches2.size(); i++) nmatches += matches2[i].size();

  std::cout << "Number of matches (with max radius): " << nmatches << std::endl;

 

  // Display the image of matches

  cv::namedWindow("SURF Matches (with max radius)");

  cv::imshow("SURF Matches (with max radius)", imageMatches);

 

  // scale-invariance test

 

  // Read input images

  image1= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image1.jpg",cv::IMREAD_GRAYSCALE);

  image2= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image2.jpg",cv::IMREAD_GRAYSCALE);

 

  std::cout << "Number of SIFT keypoints (image 1): " << keypoints1.size() << std::endl;

  std::cout << "Number of SIFT keypoints (image 2): " << keypoints2.size() << std::endl;

 

  // Extract the keypoints and descriptors

  ptrFeature2D = cv::SIFT::create();

  ptrFeature2D->detectAndCompute(image1, cv::noArray(), keypoints1, descriptors1);

  ptrFeature2D->detectAndCompute(image2, cv::noArray(), keypoints2, descriptors2);

 

// Match the two image descriptors

    matcher.match(descriptors1,descriptors2, matches);

 

  // extract the 50 best matches

std::nth_element(matches.begin(),matches.begin()+50,matches.end());

  matches.erase(matches.begin()+50,matches.end());

 

   // draw matches

  cv::drawMatches(

       image1, keypoints1, // 1st image and its keypoints

       image2, keypoints2, // 2nd image and its keypoints

       matches,        // the matches

       imageMatches,  // the image produced

       cv::Scalar(0, 0, 255),  // color of lines

       cv::Scalar(0, 0, 255), // color of points

       std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS| cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

// Display the image of matches

  cv::namedWindow("Multi-scale SIFT Matches");

  cv::imshow("Multi-scale SIFT Matches",imageMatches);

 

  std::cout << "Number of matches: " << matches.size() << std::endl;

 

cv::waitKey();

return 0;

}

 


使用SURF

使用SIFT

matcher.match()匹配兩張圖像的描述符

match()找到每個描述符的最佳匹配

在此方法的第一個變體中,traindescriptors 作為輸入參數傳遞。 在該方法的第二個變體中,使用了由 DescriptorMatcher::add 設置的 traindescriptors 集合。 可以傳遞可選的掩碼(或多個掩碼)以指定可以匹配哪些查詢和訓練描述符。 即,僅當 mask.at\<uchar\>(i,j) 非零時,queryDescriptors[i] 才能與 trainDescriptors[j] 匹配。

knnMatch():為每個描述符找到 k 個最佳匹配項。

DescriptorMatcher::match 方法的這些擴展變體為每個查詢描述符找到幾個最佳匹配。 匹配項以距離遞增的順序返回。 有關查詢和訓練描述符的詳細信息,請參閱 DescriptorMatcher::match。

radiusMatch():對於每個查詢描述符,查找不超過指定距離的訓練描述符。

對於每個查詢描述符,該方法找到查詢描述符和訓練描述符之間的距離等於或小於 maxDistance 的訓練描述符。 找到的匹配項以距離遞增的順序返回。

原圖:

image1(左)、image2(右)

SURF feature points

SURF matches 

SURF matches +ratio test(ratio=0.6)

SURF matches + radius match (max radius)

Multi-scale SIFT Matches

原圖:

image11(左)、image22(右)

SURF feature points

SURF matches 

SURF matches +ratio test(ratio=0.6)

SURF matches + radius match (max radius)

Multi-scale SIFT Matches

Describing keypoints with binary features

binaryDescriptor.cpp

#include <iostream>

#include <vector>

#include <opencv2/core.hpp>

#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

#include <opencv2/features2d.hpp>

#include <opencv2/objdetect.hpp>

#include <opencv2/xfeatures2d.hpp>

 

int main()

{

  // image matching

 

  // 1. Read input images

  //cv::Mat image1= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image1.jpg", cv::IMREAD_GRAYSCALE);

  //cv::Mat image2= cv::imread("C:/Users/User/Desktop/CV_testimage/P6/image2.jpg", cv::IMREAD_GRAYSCALE);

  cv::Mat image1 = cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p51.jpg", cv::IMREAD_GRAYSCALE);

  cv::Mat image2 = cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p52.jpg", cv::IMREAD_GRAYSCALE);

  // 2. Define keypoint vectors and descriptors

  std::vector<cv::KeyPoint> keypoints1;

  std::vector<cv::KeyPoint> keypoints2;

  cv::Mat descriptors1;

  cv::Mat descriptors2;

 

  // 3. Define feature detector/descriptor

  // Construct the ORB feature object

  cv::Ptr<cv::Feature2D> feature =

       cv::ORB::create(60);

  //   cv::BRISK::create(80);

 

  // 4. Keypoint detection and description

  // Detect the ORB features

  feature->detectAndCompute(image1, cv::noArray(), keypoints1, descriptors1);

  feature->detectAndCompute(image2, cv::noArray(), keypoints2, descriptors2);

 

  // Draw feature points

  cv::Mat featureImage;

cv::drawKeypoints(image1,keypoints1,featureImage,cv::Scalar(0,0,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

// Display the corners

  cv::namedWindow("ORB");

  cv::imshow("ORB",featureImage);

 

  std::cout << "Number of ORB keypoints (image 1): " << keypoints1.size() << std::endl;

  std::cout << "Number of ORB keypoints (image 2): " << keypoints2.size() << std::endl;

 

  // to describe with FREAK (use with BRISK)

  // feature = cv::xfeatures2d::FREAK::create();

  // feature->compute(image1, keypoints1, descriptors1);

  // feature->compute(image1, keypoints2, descriptors2);

 

   // Construction of the matcher

   cv::BFMatcher matcher(

    cv::NORM_HAMMING); // always use hamming norm

                          // for binary descriptors

   // Match the two image descriptors

   std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

 

   // draw matches

   cv::Mat imageMatches;

   cv::drawMatches(

     image1,keypoints1, // 1st image and its keypoints

     image2,keypoints2, // 2nd image and its keypoints

  matches,       // the matches

     imageMatches,  // the image produced

  cv::Scalar(0,0,255),  // color of lines

  cv::Scalar(0,0,255),  // color of points

  std::vector< char >(), // masks if any

  cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

 

// Display the image of matches

   cv::namedWindow("ORB Matches");

   cv::imshow("ORB Matches", imageMatches);

//   cv::namedWindow("FREAK Matches");

//   cv::imshow("FREAK Matches", imageMatches);

 

  std::cout << "Number of matches: " << matches.size() << std::endl;

 

   cv::waitKey();

   return 0;

}

 


原圖:

image1(左)、image2(右)

原圖:

image11(左)、image22(右)

ORB

ORB 描述子通過簡單比較強度值,提取出每個關鍵點的表徵,然後在關鍵點周圍的鄰域內隨機選取一對像素點,創建一個二值描述子。比較這兩個像素點的強度值,如果第一個點的強度值較大,就把對應描述子的bit設為 1,否則就設為 0。對一批隨機像素點對進行上述處理,就產生了一個由若干bit組成的描述子,通常採用 128 到 512 位(成對地測試)。接下來判斷用哪些像素點對構建描述子。雖然像素點對是隨機選取的,但只要它們被選中,就要進行同樣的二值測試,並構建全部關鍵點的描述子,以確保結果的一致性。

 Detect the ORB features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

 Detect the ORB features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

BRISK

BRISK 描述子的情況也非常類似,也是成對地比較強度值。但有兩點不同:第一,它不是從 31×31 的鄰域中隨機選取像素,而是從一系列等間距的同心圓(由 60 個點組成)的採樣模式中選取;第二,這些採樣點的強度值都經過高斯平滑處理,處理中使用的 σ 值與該像素到圓心的距離成正比。 BRISK 據此選取了 512 對點。

 Detect the BRISK features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

 Detect the BRISK features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

FREAK(Fast Retina Keypoint)

是一種二值描述子,但沒有對應的檢測器。可以應用於所有已檢測到的關鍵點,例如 SIFT、SURF 或 ORB。與 BRISK 一樣,FREAK 描述子也基於用同心圓定義的採樣模式。為了獲得它的強度值,每個像素都用高斯內核進行濾波,當與中心點的距離增加時,內核的尺寸也隨之增大。FREAK 還引入了階梯式比較描述子的概念。具體做法是,先執行表示較粗略信息的前 128位(用較大的高斯內核在外圍進行測試)。只有對比的描述子通過了第一步測試,後面的測試才能進行。

 Detect the BRISK features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

 Detect the BRISK features

cv::BFMatcher matcher(cv::NORM_HAMMING);

std::vector<cv::DMatch> matches;

   matcher.match(descriptors1,descriptors2, matches);

Reference