ORB特征匹配原理及源代碼


 

  這篇往后,會暫時先更ORB、SITF、SURF三篇特征算子,在代碼部分,會在本篇介紹下OPENCV特征匹配的特征點KeyPoint、特征描述子和匹配算子Match等的構成。

1ORB簡介

        ORB算法是一種特征匹配算法,可用於目標追蹤、圖像匹配等多個方面,在實時圖像處理上,有較好的效果。目前比較流行的特征匹配算子有SIFT、SURF、ORB等,三者有不同的優缺點,SIFT是90年代提出的一種特征匹配算子,在機器學習流行前,十分火熱,SURF是SIFT的改進,相對於SIFT,運行速度更快。ORB是一種為滿足實時特征匹配提出的算子,運行速度較前兩者有很大提升。

2ORB特征匹配原理

2.1概述

   ORB特征匹配步驟可分為3步:

     (1).特征點檢測,(2)計算特征點描述子,(3)進行圖像的特征點的匹配。(特征點的匹配部分由OPENCV提供的API實現,不再介紹)

 

 

2.2特征點檢測:

  在特征點檢測部分,ORB采用FAST算法,在介紹ORB算法內FAST的實現前,先介紹下FAST算法的原理,詳細如下:

          

 

 

 

       如上圖,任選圖像中的一點P,以該點為圓形,r為半徑確定一個圓,在圓上均勻取m個像素點,設定一個閾值t,如果m個像素點中,有連續N個像素點的大小均大於或小於t,則這個點就是角點。(其中r、m、t和N參數的設置 可設為 3 、16 、未知、9會有更好的效果,t可根據自己需要進行設置)。

       如果對於像分辨率為1440*1080尺寸比較大的圖形,對每個像素點進行上述步驟處理,會需要很大的計算量,不滿足ORB實時的要求,故有一種通過排除非角點的算法,提高了FAST特征點檢測的效率:

       先進性提取像素點周圍1,5,9,13四個位置的像素點大小,后通過1,9,5,13的順序進行比較,若其中至少三個大於或小於t,則該點可能為角點,進一步按照上面不走進行判斷;否則,該點不符合角點條件。

上面是FAST算法原理,下面介紹FAST在ORB內的實現。

      ORB算法作者使用FAST-9算法進行特征點提取(FAST-9算法有更好的效果,其中9代表m值為9即周圍均勻的9個點)。在進行FAST進行角點檢測時,邊緣位置的部分易混淆,如對一個圓形物體,圓上的每一點按照FAST算法,均可視為角點。為最小化這種影響,ORB算法作何通過Harris角點檢測器把N個關鍵點進行等級排序,使用者可提取前n個自己需要的點(排序方法根據Harris角點響應的大小,響應大小計算可看Harris角點檢測一節中的介紹)。在進行特征點匹配時,檢測出的角點需要滿足尺度不變形和旋轉不變性。ORB作者通過增加圖像金字塔和計算角度的方法達到效果。詳細如下面介紹。

      對於尺度不變形,ORB算法通過對初始圖像的按1/2的比例不斷下采樣(即按1/2的比例不斷縮放),得到一系列圖像,形成圖像金字塔。對每層圖像,進行FAST角點檢測,得到一系列角點,具備尺度不變性。(尺度不變性:在一幅圖像提取角點后,對圖像進行縮放,該角點仍能被檢測出,且與第一幅檢測角點進行匹配。)

      對於旋轉不變形,ORB采用灰度質心法進行計算每個特征點的主方向。灰度質心法的定義如下:

                                                     

   其中x,y分別表示像素點周圍圓上所選取點的橫坐標和縱坐標,I(x,y)表示灰度值大小,p和q表示指數。角度計算的方法如下:

              θ=atan2(m01,m10)。

2.3         特征描述子計算

2.3.1 描述子概念

  描述子用於在提取角點后,兩幅圖之間的匹配,相當於角點的特征描述。同時,描述子應具備尺度、角度和光照等條件的不變形。

  ORB內描述子的計算采用BRIEF描述子計算算法實現,下面先介紹BRIEF算法原理。

2.3.2 BRIEF算法原理

  BRIEF算法在2010年由一篇《BRIEF:Binary Robust Independent Elementary Features》論文提出,是一種二進制編碼的描述子,因沒有采用直方圖描述點的方式,加快特征提取速度,降低匹配時間。

 

 

  BRIEF算法可分為兩步:1特征點大小的對比。2.選取多對特征點對比。下面具體介紹。

  第一步中,以特征點為中心,取SXS的鄰域窗口,在窗口上通過某種規律(下面會有介紹)選擇兩個點p(x)和p(y),比較兩個點像素值的大小,進行如下賦值:

 

 

 

 

  第二步中,在窗口中通過某種規律選取N對像素點,重復第一步進行像素值大小的比較,最后形成一個二進制編碼(編碼為比較的結果,如01010101.. N值大小一般為256)。

 

  上文中提到的某種規律,BRIEF作者嘗試了5中方法,具體如下文,其中法(2)效果較好:

       

 

 

 

2.3.3ORB內的BRIEF算法

   OBR算法對BRIEF有兩種改變,分別為steer BRIEF和rBRIEF。下面做介紹。

   相對於BRIEF,steer BRIEF具備旋轉不變形的特征。通過2.2可計算出角點的角度大小θ,將該點周圍的點旋轉θ度,得到新的點對,公式如下:(R.表示旋轉矩陣)

                 

 

 

 

         旋轉后,在新的位置上比較像素值的大小,得到描述子。

            相對於BRIEF,rBRIEF的提出,是為了彌補在通過steer BRIEF進行旋轉后的缺陷。下圖為三者的一個對比:

         

 

 

     

           其中橫坐標表示均值(即二進制形成的描述子的均值)距離0.5的距離,縱坐標表示數量。

           通過上圖可發現,BRIEF描述子主要分布在0附近,表明通過BRIEF計算出的描述子內0和1的數目大致相等,而通過steer BRIEF計算出的描述子在0.3附近較多,表明通過steer BRIEF計算出的描述子內出現0多於1、或1多於0的情況,不易於兩個特征點的描述子的區分,增大了描述符之間的相關性。

           rBRIEF算法通過改變描述子的計算方法,進一步減弱同一圖像中特征點的描述子的相關性,具體如下:

           對每個角點,考慮其31X31的鄰域,使用領域中每個點周圍的5X5的鄰域的像素值平均值作為該點的像素值,進而比較點對的大小。上面計算可得到(31-5+1)*(31-5+1)=729個子窗口,提取點對的方法有729*728=265356種,通過在這265356中方法中選取256種取法,形成描述子。下面是具體選擇步驟:

    (1)  在300k特征點的每個31X31鄰域內,M=265356種方法取點對,比較點對大小,形成一個300kXM的矩陣Q。矩陣種的每一列代表一種提取方法得到的二進制數。

    (2)  對Q矩陣的每一列求取平均值,按照平均值到0.5的距離大小,重新對Q進行列向量的排序形成矩陣T。

    (3)  將T的第一列向量放到R中。

    (4)   將T的下一列向量和R中的所有列向量計算相關性,如果相關系數小於設定的閾值,則將T中的該列向量移入R中。

    (5)    重復步驟(4)的操作,直至R中列向量數目為256.

   上面即為rBRIEF的算法原理。

 

 

3ORB在OPENCV內的API和一個demo:

 

                  //一點個人教訓:在使用orb、sift和surf時,發現第一次進行特征點檢測、描述子計算耗費時間遠大於通過已用的接口進行第二次特征點
檢測和描述子計算。(至於原因,博主暫時也未搞懂)舉個簡單的例子:


  

   cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //創建orb對象,后為得到的特征點數目
  cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2;   //只是舉例,建設圖像已傳入
  std::vector<cv::KeyPoint> KeyPointL1,KeyPointR1,KeyPointL2,KeyPointR2;
  orb->detectAndCompute(imageL1,cv::Mat(),KeyPointL1,despL1);  //第一次特征點檢測和描述子計算
  orb->detectAndCompute(imageR1,cv::Mat(),KeyPointR1,despR1);
  orb->detectAndCompute(imageL2,cv::Mat(),KeyPointL2,despL2);  //第二次特征點檢測和描述子計算
  orb->detectAndCompute(imageR2,cv::Mat(),KeyPointR2,despR2);

 


  //博主在使用中,發現第二次用時遠少於第一次用時。同樣情況出現在SIFT和SURF中。ps:使用opencv和opencv-contrib版本為4.0+(這是個大坑,記得博主使用時,因為這個坑,自己都寫出起算法進行特征匹配,還好最后脫坑)
分為三部分:

      

      cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //創建orb對象,后為得到的特征點數目 std::vector<cv::KeyPoint> keyPointL, keyPointR; //創建關鍵點對象 orb->detect(imageR, keyPointR); //關鍵點檢測 orb->detect(imageL, keyPointL); //關鍵點檢測 cv::Mat despL,despR;//創建描述子 orb->compute(imageR, cv::Mat(),keyPointR,despL);//cv::Mat()表示無roi區域 計算描述子 orb->detect(imageL, cv::Mat(), keyPointL,despR); //cv::Mat()表示無roi區域 計算描述子 
    orb->detectAndCompute(imageL,cv::Mat(),KeyPointL,despL);  //進行檢測和計算,相當於上面detect和compute兩步
    orb->detectAndCompute(imageR,cv::Mat(),KeyPointR,despR); std::vector<cv::DMatch> matches; cv::Ptr<cv::DescriptorMatcher> matcher=cv::DescriptorMatcher::create("FlannBased"); //進行匹配 matcher->match(despL, despR, matches); std::vector point_l,std::vector point_r; for(int i=0;i<matches.size();i++) //得到匹配點 { point_l.push_back(matches[i].queryIdx); point_r.push_back(matches[i].trainIdx); }

  Demo:

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
int main()
{
cv::Mat imageL = cv::imread(".I/like/Study.png"); //圖片位置
cv::Mat imageR = cv::imread("./Study/make/me/happy.png");//圖片2位置
cv::cvtColor(imageL,imageL,cv::COLOR_BGR2GRAY);
cv::cvtColor(imageR,imageR,cv::COLOR_BGR2GRAY);
cv::resize(imageL,imageL,cv::Size(120,90));
cv::resize(imageR,imageR,cv::Size(120,90));

// ORB
cv::Ptr<cv::ORB> orb = cv::ORB::create(50);

//特征點
std::vector<cv::KeyPoint> keyPointL, keyPointR;
//單獨提取特征點
orb->detect(imageL, keyPointL);
orb->detect(imageR, keyPointR);
//畫特征點
cv::Mat keyPointImageL;
cv::Mat keyPointImageR;
drawKeypoints(imageL, keyPointL, keyPointImageL, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(imageR, keyPointR, keyPointImageR, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

//顯示窗口
cv::namedWindow("KeyPoints of imageL");
cv::namedWindow("KeyPoints of imageR");

//顯示特征點
cv::imshow("KeyPoints of imageL", keyPointImageL);
cv::imshow("KeyPoints of imageR", keyPointImageR);

//特征點匹配
cv::Mat despL, despR;
//提取特征點並計算特征描述子
orb->detectAndCompute(imageL, cv::Mat(), keyPointL, despL);
orb->detectAndCompute(imageR, cv::Mat(), keyPointR, despR);

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

//如果采用flannBased方法 那么 desp通過orb的到的類型不同需要先轉換類型
if (despL.type() != CV_32F || despR.type() != CV_32F)
{
despL.convertTo(despL, CV_32F);
despR.convertTo(despR, CV_32F);
}

cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");
matcher->match(despL, despR, matches);

// //計算特征點距離的最大值
double maxDist = 0;
for (int i = 0; i < despL.rows; i++)
{
double dist = matches[i].distance;
if (dist > maxDist)
maxDist = dist;
}

//挑選好的匹配點
std::vector< cv::DMatch > good_matches;
int size = matches.size();
for (int i = 0; i < despL.rows; i++)
{
int low = 0;
for(int n=0;n<despL.rows;n++)
{
if(matches[i].distance<matches[n].distance)
{
low+=1;
}

}

if(low>=size-15)
{
good_matches.push_back(matches[i]);
}
}

cv::Mat imageOutput;
cv::drawMatches(imageL, keyPointL, imageR, keyPointR, good_matches, imageOutput);

cv::namedWindow("picture of matching",cv::WINDOW_NORMAL);
cv::imshow("picture of matching", imageOutput);
cv::waitKey(0);
return 0;
}

 

 

 

三、代碼部分:

    首先介紹匹配部分DescriptorMatcher:

 

OPENCV特征匹配部分通過DescriptorMatcher實現,DescriptorMatcher是匹配器的抽象基類,其具體類有:         class FlannBasedMatcher         class BFMatcher       源代碼暫時未找到,不再介紹其內部如何構造,在此僅介紹其使用方法,匹配器的創建有兩種方式(建議用1,方便,接口多):       一:           static Ptr<DescriptorMatcher> create(const string& descriptorMatcherType)             如: cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");       二:           DescriptorMatcher *pMatcher = newBFMatcher;      DescriptorMatcher的接口函數有以下幾種(對於一般的使用,僅crete()和match()兩個函數足可):         create():創建匹配器,支持的匹配器類型有:BruteForce 、BruteForce、BruteForce-Hamming、BruteForce-Hamming(2)和FlannBased  (博主目前使用FlannBased,效果還可,其它暫未嘗試)         match(desL,desR,match) :通過描述子進行匹配,desL和desR為兩個Mat類型的描述子,match為DMatch類型的vector,下面會介紹DMatch類型         knnMatch();找到最好的k個匹配         add();添加匹配器,用來連接匹配器集合,若匹配器集合不是空的,新加的匹配器會加到集合的后面。         getTrainDescriptors();獲得匹配器集合         isMaskSupported();匹配器是否支持掩模,使得話就返回ture         radiusMatch();找到距離不超過規定的匹配

 

介紹DMatch類型:      

 簡化版(針對於不想看下面源代碼的人):DMatch包含四個有用信息,queryIdx(要匹配的描述子的索引(序列)),trainIdx(被匹配的描述子索引(序列)),imageIdx(匹配圖像的索引)distance表示兩個描述子之間的距離,越小匹配效果越好。queryIdx
為match()函數第一個參數描述子對應圖像的關鍵點的索引,trainIdx為第二個參數描述子對應圖像的關鍵點的索引。imageIdx僅在多張圖像間進行匹配時,用於表示匹
圖像的序列。請注意,queryIdx為要匹配,trainIdx為被匹配。下面有實戰展示如何使用。


/*
* DMatch主要用來儲存匹配信息的結構體,query是要匹配的描述子,train是被匹配的描述子,在Opencv中進行匹配時 * void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<DMatch>& matches, const Mat& mask ) const * match函數的參數中位置在前面的為query descriptor,后面的是 train descriptor * 例如:query descriptor的數目為20,train descriptor數目為30,則DescriptorMatcher::match后的vector<DMatch>的size為20 * 若反過來,則vector<DMatch>的size為30 */ struct CV_EXPORTS_W_SIMPLE DMatch { //默認構造函數,FLT_MAX是無窮大 //#define FLT_MAX 3.402823466e+38F /* max value */ CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} //DMatch構造函數 CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} //DMatch構造函數 CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} //queryIdx為query描述子的索引,match函數中前面的那個描述子 CV_PROP_RW int queryIdx; // query descriptor index //trainIdx為train描述子的索引,match函數中后面的那個描述子 CV_PROP_RW int trainIdx; // train descriptor index //imgIdx為進行匹配圖像的索引 //例如已知一幅圖像的sift描述子,與其他十幅圖像的描述子進行匹配,找最相似的圖像,則imgIdx此時就有用了。 CV_PROP_RW int imgIdx; // train image index //distance為兩個描述子之間的距離 CV_PROP_RW float distance; //DMatch比較運算符重載,比較的是DMatch中的distance,小於為true,否則為false // less is better bool operator<( const DMatch &m ) const { return distance < m.distance; } };

 介紹KeyPoint類型(未找到文本樣式,盜幾張圖):

簡化版(懶人通道):KeyPoint主要使用的類型有pt(二維坐標點),size(該關鍵點領域直徑大小),angle(關鍵點的方向:0~360°),response(關鍵點的相應強度,可用於排序),octve(關鍵點的金字塔層數),class_id(當要對圖片進行分類時,用class_id對每個關鍵點進行區分,默認為-1。暫時不使用,使用時你也就已明白它的含義。),

        

 

KeyPointsFilter類型(屬於對KeyPoint處理的封裝好的類,已在算法內實現。一般僅在自己想寫特征匹配算法時,才會用到這些):

      KeyPointsFilter包含5個功能函數:分別為runByImageBorder()、runByKeyPointSzie()、runByPixelsMask()、removeDuplicated()、retainBest()。其源代碼結構如下:        

 

ORB API:

//一點個人教訓:在使用orb、sift和surf時,發現第一次進行特征點檢測、描述子計算耗費時間遠大於通過已用的接口進行第二次特征點
檢測和描述子計算。(至於原因,博主暫時也未搞懂)舉個簡單的例子:
   cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //創建orb對象,后為得到的特征點數目
cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2;   //只是舉例,建設圖像已傳入
std::vector<cv::KeyPoint> KeyPointL1,KeyPointR1,KeyPointL2,KeyPointR2;

  orb->detectAndCompute(imageL1,cv::Mat(),KeyPointL1,despL1);  //第一次特征點檢測和描述子計算
  orb->detectAndCompute(imageR1,cv::Mat(),KeyPointR1,despR1);

orb->detectAndCompute(imageL2,cv::Mat(),KeyPointL2,despL2);  //第二次特征點檢測和描述子計算
orb->detectAndCompute(imageR2,cv::Mat(),KeyPointR2,despR2);
  //博主在使用中,發現第二次用時遠少於第一次用時。同樣情況出現在SIFT和SURF中。ps:使用opencv和opencv-contrib版本為4.0+(這是個大坑,記得博主使用時,因為這個坑,自己都寫出起算法進行特征匹配,還好最后脫坑)

分為三部分: cv::Ptr
<cv::ORB> orb = cv::ORB::create(50); //創建orb對象,后為得到的特征點數目 std::vector<cv::KeyPoint> keyPointL, keyPointR; //創建關鍵點對象 orb->detect(imageR, keyPointR); //關鍵點檢測 orb->detect(imageL, keyPointL); //關鍵點檢測 cv::Mat despL,despR;//創建描述子 orb->compute(imageR, cv::Mat(),keyPointR,despL);//cv::Mat()表示無roi區域 計算描述子 orb->detect(imageL, cv::Mat(), keyPointL,despR); //cv::Mat()表示無roi區域 計算描述子
    orb->detectAndCompute(imageL,cv::Mat(),KeyPointL,despL);  //進行檢測和計算,相當於上面detect和compute兩步
    orb->detectAndCompute(imageR,cv::Mat(),KeyPointR,despR); std::vector<cv::DMatch> matches; cv::Ptr<cv::DescriptorMatcher> matcher=cv::DescriptorMatcher::create("FlannBased"); //進行匹配 matcher->match(despL, despR, matches); std::vector point_l,std::vector point_r; for(int i=0;i<matches.size();i++) //得到匹配點 { point_l.push_back(matches[i].queryIdx); point_r.push_back(matches[i].trainIdx); }

 

實戰展示:


#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
int main()
{
cv::Mat imageL = cv::imread(".I/like/Study.png"); //圖片位置
cv::Mat imageR = cv::imread("./Study/make/me/happy.png");//圖片2位置
cv::cvtColor(imageL,imageL,cv::COLOR_BGR2GRAY);
cv::cvtColor(imageR,imageR,cv::COLOR_BGR2GRAY);
cv::resize(imageL,imageL,cv::Size(120,90));
cv::resize(imageR,imageR,cv::Size(120,90));

// ORB
cv::Ptr<cv::ORB> orb = cv::ORB::create(50);

//特征點
std::vector<cv::KeyPoint> keyPointL, keyPointR;
//單獨提取特征點
orb->detect(imageL, keyPointL);
orb->detect(imageR, keyPointR);
//畫特征點
cv::Mat keyPointImageL;
cv::Mat keyPointImageR;
drawKeypoints(imageL, keyPointL, keyPointImageL, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(imageR, keyPointR, keyPointImageR, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

//顯示窗口
cv::namedWindow("KeyPoints of imageL");
cv::namedWindow("KeyPoints of imageR");

//顯示特征點
cv::imshow("KeyPoints of imageL", keyPointImageL);
cv::imshow("KeyPoints of imageR", keyPointImageR);

//特征點匹配
cv::Mat despL, despR;
//提取特征點並計算特征描述子
orb->detectAndCompute(imageL, cv::Mat(), keyPointL, despL);
orb->detectAndCompute(imageR, cv::Mat(), keyPointR, despR);

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

//如果采用flannBased方法 那么 desp通過orb的到的類型不同需要先轉換類型
if (despL.type() != CV_32F || despR.type() != CV_32F)
{
despL.convertTo(despL, CV_32F);
despR.convertTo(despR, CV_32F);
}

cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");
matcher->match(despL, despR, matches);

// //計算特征點距離的最大值
double maxDist = 0;
for (int i = 0; i < despL.rows; i++)
{
double dist = matches[i].distance;
if (dist > maxDist)
maxDist = dist;
}

//挑選好的匹配點
std::vector< cv::DMatch > good_matches;
int size = matches.size();
for (int i = 0; i < despL.rows; i++)
{
int low = 0;
for(int n=0;n<despL.rows;n++)
{
if(matches[i].distance<matches[n].distance)
{
low+=1;
}

}

if(low>=size-15)
{
good_matches.push_back(matches[i]);
}
}

cv::Mat imageOutput;
cv::drawMatches(imageL, keyPointL, imageR, keyPointR, good_matches, imageOutput);

cv::namedWindow("picture of matching",cv::WINDOW_NORMAL);
cv::imshow("picture of matching", imageOutput);
cv::waitKey(0);
return 0;
}

 

 ORB源代碼:

CV_EXPORTS_W ORB :  Feature2D  
{  
:  
      
     { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 };  
  
    CV_WRAP  ORB( nfeatures = 500,  scaleFactor = 1.2f,  nlevels = 8,  edgeThreshold = 31,  
         firstLevel = 0,  WTA_K=2,  scoreType=ORB::HARRIS_SCORE,  patchSize=31 );  
  
      
     descriptorSize() ;     
      
     descriptorType() ;  
  
      
     operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) ;  
  
      
     operator()( InputArray image, InputArray mask, vector<KeyPoint>& keypoints,      
                     OutputArray descriptors,  useProvidedKeypoints= ) ;  
  
    AlgorithmInfo* info() ;  
  
:  
  
     computeImpl(  Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) ;  
     detectImpl(  Mat& image, vector<KeyPoint>& keypoints,  Mat& mask=Mat() ) ;  
  
    CV_PROP_RW  nfeatures;  
    CV_PROP_RW  scaleFactor;  
    CV_PROP_RW  nlevels;  
    CV_PROP_RW  edgeThreshold;  
    CV_PROP_RW  firstLevel;  
    CV_PROP_RW  WTA_K;  
    CV_PROP_RW  scoreType;  
    CV_PROP_RW  patchSize;  
};  
/** Compute the ORB features and descriptors on an image 
 * @param keypoints the resulting keypoints 
 * @param do_keypoints if true, the keypoints are computed, otherwise used as an input 
 */ ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,  
                      OutputArray _descriptors,  useProvidedKeypoints)   
{  
    CV_Assert(patchSize >= 2);  
  
     do_keypoints = !useProvidedKeypoints;  
     do_descriptors = _descriptors.needed();  
  
    ( (!do_keypoints && !do_descriptors) || _image.empty() )  
        ;  
  
      
      HARRIS_BLOCK_SIZE = 9;  
     halfPatchSize = patchSize / 2;.  
     border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;  
  
    Mat image = _image.getMat(), mask = _mask.getMat();  
    ( image.type() != CV_8UC1 )  
        cvtColor(_image, image, CV_BGR2GRAY);  
  
     levelsNum = ->nlevels;  
  
    ( !do_keypoints )     
    {  
          
          
          
          
          
          
          
          
          
        levelsNum = 0;  
        (  i = 0; i < _keypoints.size(); i++ )  
            levelsNum = std::max(levelsNum, std::max(_keypoints[i].octave, 0));  
        levelsNum++;  
    }  
  
      
    vector<Mat> imagePyramid(levelsNum), maskPyramid(levelsNum);  
     ( level = 0; level < levelsNum; ++level)  
    {  
         scale = 1/getScale(level, firstLevel, scaleFactor);    
                 static inline float getScale(int level, int firstLevel, double scaleFactor) 
                   return (float)std::pow(scaleFactor, (double)(level - firstLevel)); 
        */        Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale));  
        Size wholeSize(sz.width + border*2, sz.height + border*2);  
        Mat temp(wholeSize, image.type()), masktemp;  
        imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));  
        ( !mask.empty() )  
        {  
            masktemp = Mat(wholeSize, mask.type());  
            maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));  
        }  
  
          
        ( level != firstLevel )      
        {  
            ( level < firstLevel )  
            {  
                resize(image, imagePyramid[level], sz, 0, 0, INTER_LINEAR);  
                 (!mask.empty())  
                    resize(mask, maskPyramid[level], sz, 0, 0, INTER_LINEAR);  
            }  
              
            {  
                resize(imagePyramid[level-1], imagePyramid[level], sz, 0, 0, INTER_LINEAR);  
                 (!mask.empty())  
                {  
                    resize(maskPyramid[level-1], maskPyramid[level], sz, 0, 0, INTER_LINEAR);  
                    threshold(maskPyramid[level], maskPyramid[level], 254, 0, THRESH_TOZERO);  
                }  
            }  
  
            copyMakeBorder(imagePyramid[level], temp, border, border, border, border,  
                           BORDER_REFLECT_101+BORDER_ISOLATED);  
             (!mask.empty())  
                copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,  
                               BORDER_CONSTANT+BORDER_ISOLATED);  
        }  
          
        {  
            copyMakeBorder(image, temp, border, border, border, border,  
                           BORDER_REFLECT_101);  
            ( !mask.empty() )  
                copyMakeBorder(mask, masktemp, border, border, border, border,  
                               BORDER_CONSTANT+BORDER_ISOLATED);  
        }  
    }  
  
      
    vector < vector<KeyPoint> > allKeypoints;  
    ( do_keypoints )  
    {  
          
        computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,    
                         nfeatures, firstLevel, scaleFactor,  
                         edgeThreshold, patchSize, scoreType);  
  
          
                 for (int level = 0; level < n_levels; ++level) 
            vector<KeyPoint>& keypoints = all_keypoints[level]; 
            keypoints.clear(); 
 
 
             keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint) 
  
    }  
        
    {  
          
        KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);  
  
          
        allKeypoints.resize(levelsNum);  
         (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),  
             keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)  
            allKeypoints[keypoint->octave].push_back(*keypoint);      
  
          
         ( level = 0; level < levelsNum; ++level)     
        {  
             (level == firstLevel)  
                ;  
  
            vector<KeyPoint> & keypoints = allKeypoints[level];  
             scale = 1/getScale(level, firstLevel, scaleFactor);  
             (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
                keypoint->pt *= scale;     
        }  
    }  
  
    Mat descriptors;          
    vector<Point> pattern;  
  
    ( do_descriptors )   
    {  
         nkeypoints = 0;  
         ( level = 0; level < levelsNum; ++level)  
            nkeypoints += ()allKeypoints[level].size();  
        ( nkeypoints == 0 )  
            _descriptors.release();  
          
        {  
            _descriptors.create(nkeypoints, descriptorSize(), CV_8U);  
            descriptors = _descriptors.getMat();  
        }  
  
          npoints = 512;  
        Point patternbuf[npoints];  
         Point* pattern0 = ( Point*)bit_pattern_31_;  
  
        ( patchSize != 31 )  
        {  
            pattern0 = patternbuf;  
            makeRandomPattern(patchSize, patternbuf, npoints);  
        }  
  
        CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );  
  
        ( WTA_K == 2 )    
            std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));  
          
        {  
             ntuples = descriptorSize()*4;  
            initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);  
        }  
    }  
  
    _keypoints.clear();  
     offset = 0;  
     ( level = 0; level < levelsNum; ++level)  
    {  
          
        vector<KeyPoint>& keypoints = allKeypoints[level];  
         nkeypoints = ()keypoints.size();  
  
          
         (do_descriptors)  
        {  
            Mat desc;  
             (!descriptors.empty())  
            {  
                desc = descriptors.rowRange(offset, offset + nkeypoints);  
            }  
  
            offset += nkeypoints;    
              
            Mat& workingMat = imagePyramid[level];  
              
            GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);  
            computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);  
        }  
  
          
         (level != firstLevel)    
        {  
             scale = getScale(level, firstLevel, scaleFactor);  
             (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
                keypoint->pt *= scale;   
        }  
          
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());  
    }  
}  
/** Compute the ORB keypoints on an image 
 * @param keypoints the resulting keypoints, clustered per level 
  
  computeKeyPoints( vector<Mat>& imagePyramid,  
                              vector<Mat>& maskPyramid,  
                             vector<vector<KeyPoint> >& allKeypoints,  
                              nfeatures,  firstLevel,  scaleFactor,  
                              edgeThreshold,  patchSize,  scoreType )  
{  
     nlevels = ()imagePyramid.size();    
    vector<> nfeaturesPerLevel(nlevels);  
  
      
     factor = ()(1.0 / scaleFactor);  
     ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - ()pow(()factor, ()nlevels));  
  
     sumFeatures = 0;  
    (  level = 0; level < nlevels-1; level++ )     
    {  
        nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);  
        sumFeatures += nfeaturesPerLevel[level];  
        ndesiredFeaturesPerScale *= factor;  
    }  
    nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);  
  
      
      
  
      
     halfPatchSize = patchSize / 2;             
    vector<> umax(halfPatchSize + 2);  
     v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);  
     vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);  
     (v = 0; v <= vmax; ++v)             
        umax[v] = cvRound(sqrt(()halfPatchSize * halfPatchSize - v * v));  
      
     (v = halfPatchSize, v0 = 0; v >= vmin; --v)  
    {  
         (umax[v0] == umax[v0 + 1])  
            ++v0;  
        umax[v] = v0;  
           ++v0;  
    }  
  
    allKeypoints.resize(nlevels);  
  
     ( level = 0; level < nlevels; ++level)  
    {  
         featuresNum = nfeaturesPerLevel[level];  
        allKeypoints[level].reserve(featuresNum*2);  
  
        vector<KeyPoint> & keypoints = allKeypoints[level];  
  
          
        FastFeatureDetector fd(20, );        
        fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);  
  
          
        KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);  
  
        ( scoreType == ORB::HARRIS_SCORE )  
        {  
              
            KeyPointsFilter::retainBest(keypoints, 2 * featuresNum);  
  
              
            HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K);   
        }  
  
          
        KeyPointsFilter::retainBest(keypoints, featuresNum);  
  
         sf = getScale(level, firstLevel, scaleFactor);  
  
          
         (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
             keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
        {  
            keypoint->octave = level;    
            keypoint->size = patchSize*sf;   
        }  
  
        computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax);    
    }  
}  
static computeOrientation( Mat& image, vector<KeyPoint>& keypoints,  
                                halfPatchSize,  vector<>& umax)  
{  
      
     (vector<KeyPoint>::iterator keypoint = keypoints.begin(),    
         keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
    {  
        keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);  
    }  
}  
static IC_Angle( Mat& image,   half_k, Point2f pt,  
                       vector<> & u_max)  
{  
     m_01 = 0, m_10 = 0;  
  
     uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));  
  
      
     ( u = -half_k; u <= half_k; ++u)  
        m_10 += u * center[u];  
  
      
     step = ()image.step1();  
     ( v = 1; v <= half_k; ++v)      
    {  
          
         v_sum = 0;  
         d = u_max[v];  
         ( u = -d; u <= d; ++u)  
        {  
             val_plus = center[u + v*step], val_minus = center[u - v*step];  
            v_sum += (val_plus - val_minus);   
            m_10 += u * (val_plus + val_minus);  
        }  
        m_01 += v * v_sum;  
    }  
  
     fastAtan2(()m_01, ()m_10);  
}  
static computeDescriptors( Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,  
                                vector<Point>& pattern,  dsize,  WTA_K)  
{  
      
    CV_Assert(image.type() == CV_8UC1);  
      
    descriptors = Mat::zeros(()keypoints.size(), dsize, CV_8UC1);  
  
     ( i = 0; i < keypoints.size(); i++)  
        computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr(()i), dsize, WTA_K);  
}  
static computeOrbDescriptor( KeyPoint& kpt,  
                                  Mat& img,  Point* pattern,  
                                 uchar* desc,  dsize,  WTA_K)  
{  
     angle = kpt.angle;   
      
    angle *= ()(CV_PI/180.f);  
     a = ()cos(angle), b = ()sin(angle);  
  
     uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));  
     step = ()img.step;  
  
  
  
        center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \  
               cvRound(pattern[idx].x*a - pattern[idx].y*b)]  
  
     x, y;  
     ix, iy;  
  
        (x = pattern[idx].x*a - pattern[idx].y*b, \  
        y = pattern[idx].x*b + pattern[idx].y*a, \  
        ix = cvFloor(x), iy = cvFloor(y), \  
        x -= ix, y -= iy, \  
        cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \  
                center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))  
  
  
    ( WTA_K == 2 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 16)  
        {  
             t0, t1, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
            val = t0 < t1;  
            t0 = GET_VALUE(2); t1 = GET_VALUE(3);  
            val |= (t0 < t1) << 1;  
            t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
            val |= (t0 < t1) << 2;  
            t0 = GET_VALUE(6); t1 = GET_VALUE(7);  
            val |= (t0 < t1) << 3;  
            t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
            val |= (t0 < t1) << 4;  
            t0 = GET_VALUE(10); t1 = GET_VALUE(11);  
            val |= (t0 < t1) << 5;  
            t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
            val |= (t0 < t1) << 6;  
            t0 = GET_VALUE(14); t1 = GET_VALUE(15);  
            val |= (t0 < t1) << 7;  
  
            desc[i] = (uchar)val;  
        }  
    }  
     ( WTA_K == 3 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 12)  
        {  
             t0, t1, t2, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);  
            val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);  
  
            t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;  
  
            t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;  
  
            t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;  
  
            desc[i] = (uchar)val;  
        }  
    }  
     ( WTA_K == 4 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 16)  
        {  
             t0, t1, t2, t3, u, v, k, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
            t2 = GET_VALUE(2); t3 = GET_VALUE(3);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val = k;  
  
            t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
            t2 = GET_VALUE(6); t3 = GET_VALUE(7);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 2;  
  
            t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
            t2 = GET_VALUE(10); t3 = GET_VALUE(11);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 4;  
  
            t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
            t2 = GET_VALUE(14); t3 = GET_VALUE(15);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 6;  
  
            desc[i] = (uchar)val;  
        }  
    }  
      
        CV_Error( CV_StsBadSize,    
}  

 

 

 

 

 

如有錯誤,歡迎指正和批評。有部分參考,因忘記記錄,未標注下面,抱歉。

參考:

https://blog.csdn.net/Small_Munich/article/details/80866162

https://www.cnblogs.com/TransTown/p/7396996.html

 http://docs.opencv.org/3.3.0/d2/d29/classcv_1_1KeyPoint.html

https://blog.csdn.net/Darlingqiang/article/details/79404869

https://blog.csdn.net/frozenspring/article/details/78146076

https://blog.csdn.net/zcg1942/article/details/83824367

https://www.cnblogs.com/wyuzl/p/7856863.html


免責聲明!

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



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