opencv2.4.9影象特徵點的提取和匹配
opencv影象特徵點的提取和匹配(二)
在上面一節大概分析了一下在opencv中如何實現特徵的提取,這一節分析一下opencv中如何生成特徵點的描述子並對描述子進行匹配。opencv提取的特徵點都儲存在一個向量(vector)中,元素的型別是Point類。所有實現特徵點描述子提取的類均派生於DescriptorExtractor類。特徵描述子的匹配是由DescriptorMatcher類實現,匹配的結果儲存在一個向量(vector)中,向量元素的型別為DMatch;DMatch中儲存了特徵描述子在各自特徵描述子集合中索引值和得到匹配的兩個描述子之間的歐氏距離。
首先來看用於生成特徵描述子的DescriptorExtractor類,具體原始碼如下:
class CV_EXPORTS DescriptorExtractor { public: virtual ~DescriptorExtractor(); void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const; void compute( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, vector<Mat>& descriptors ) const; virtual void read( const FileNode& ); virtual void write( FileStorage& ) const; virtual int descriptorSize() const = 0; virtual int descriptorType() const = 0; <pre name="code" class="cpp">static Ptr<DescriptorExtractor> create( const string& descriptorExtractorType );
protected: ...};
在這個介面中,關鍵點的特徵描述子被表達成密集的、固定維數的向量。特徵描述子的集合被表達成一個Mat,其中每一行是一個關鍵特徵點的描述子,Mat矩陣的行數代表提取的特徵點的個數,列數代表特徵點描述子的維數。
可以通過名字來建立特定的特徵描述子,由靜態成員函式create實現,程式碼如下:
static Ptr<DescriptorExtractor> create( const string& descriptorExtractorType );
現在只支援以下幾種的特徵描述子的提取方法:
"SIFT"—SiftDescriptorExtractor
"SURF"—SurfDescriptorExtractor
"ORB"—OrbDescriptorExtractor
"BRIEF"—BriefDescriptorExtractor
並且支援組合型別:提取特徵描述子的介面卡("Opponent" - OpponentColorDescriptorExtractor)+描述子的提取型別(OpponentSIFT)。
DescriptorExtractor類派生類多個子類用以獲取不同型別特徵描述子,如:SiftDescriptorExtractor(原始碼直接定義的是SIFT,這兩者等價,具體見opencv影象特徵點的提取和匹配(一))、SurfDescriptorExtractor(等價於SURF類)、OrbDescriptorExtractor(等價於ORB)、BriefDescriptorExtractor、CalonderDescriptorExtractor、OpponentColorDescriptorExtractor(在對立顏色空間中計算特徵描述子)。
特徵描述的生成是由DescriptorExtractor類的成員函式compute來實現。
特徵描述子的匹配:
首先得了解DMatch結構體,這個結構體封裝了匹配的特徵描述子的一些特性:特徵描述子的索引、訓練影象的索引、特徵描述子之間的距離等。具體程式碼如下:
struct DMatch
{
DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1),
distance(std::numeric_limits<float>::max()) {}
DMatch( int _queryIdx, int _trainIdx, float _distance ) :
queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1),
distance(_distance) {}
DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) :
queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx),
distance(_distance) {}
int queryIdx; // query descriptor index
int trainIdx; // train descriptor index
int imgIdx; // train image index
float distance;
// less is better
bool operator<( const DMatch &m ) const;
};
DecriptorMatcher類是用來特徵關鍵點描述子匹配的基類,主要用來匹配兩個影象之間的特徵描述子,或者一個影象和一個影象集的特徵描述子。主要包括兩種匹配方法(均為DecriptorMatcher類的子類):BFMatcher和FlannBasedMatcher。
BFMatcher建構函式如下:
BFMatcher::BFMatcher(int normType=NORM_L2, bool crossCheck=false )
normType可以取的引數如下:
NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2.
對於SIFT運算元和SURF運算元來說,一般推薦NROM_L1和NORM_L2;NORM_HAMMING一般用於ORB、BRISK和BRIEF;NORM_HAMMING2用於ORB且ORB的建構函式的引數WTA = 3或者4時。
crossCheck引數:為false時尋找k個最鄰近的匹配點;為true時尋找最好的匹配點對。
FlannBasedMatcher類:採用最鄰近演算法尋找最好的匹配點。
用BFMatcher進行匹配的程式如下:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void readme();
int main(int argc,char* argv[])
{
Mat img1 = imread("box.png",CV_LOAD_IMAGE_GRAYSCALE);
Mat img2 = imread("box_in_scene.png",CV_LOAD_IMAGE_GRAYSCALE);
if(!img1.data || !img2.data)
{
cout<<"Error reading images!!"<<endl;
return -1;
}
SurfFeatureDetector detector;
vector<KeyPoint> keypoints1,keypoints2;
detector.detect(img1,keypoints1,Mat());
detector.detect(img2,keypoints2,Mat());
SurfDescriptorExtractor extractor;
Mat descriptor1,descriptor2;
extractor.compute(img1,keypoints1,descriptor1);
extractor.compute(img2,keypoints2,descriptor2);
//FlannBasedMatcher matcher;
BFMatcher matcher(NORM_L2);
vector<DMatch> matches;
matcher.match(descriptor1,descriptor2,matches,Mat());
Mat imgmatches;
drawMatches(img1,keypoints1,img2,keypoints2,matches,imgmatches,Scalar::all(-1),Scalar::all(-1));
imshow("Matches",imgmatches);
waitKey(0);
return 0;
}
void readme()
{ cout<<" Usage: ./SURF_descriptor <img1> <img2>"<<endl;}
特徵檢測與匹配結果:
FlannBasedMatcher匹配;並尋找匹配精度小於最小距離兩倍的匹配點集。程式如下:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc,char* argv[])
{
Mat img1 = imread("box.png",CV_LOAD_IMAGE_GRAYSCALE);
Mat img2 = imread("box_in_scene.png",CV_LOAD_IMAGE_GRAYSCALE);
if(!img1.data || !img2.data)
{
cout<<"Error reading images!!"<<endl;
return -1;
}
SurfFeatureDetector detector;
vector<KeyPoint> keypoints1,keypoints2;
detector.detect(img1,keypoints1,Mat());
detector.detect(img2,keypoints2,Mat());
SurfDescriptorExtractor extractor;
Mat descriptor1,descriptor2;
extractor.compute(img1,keypoints1,descriptor1);
extractor.compute(img2,keypoints2,descriptor2);
FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(descriptor1,descriptor2,matches,Mat());
double dist_max = 0;
double dist_min = 100;
for(int i = 0; i < descriptor1.rows; i++)
{
double dist = matches[i].distance;
if(dist < dist_min)
dist_min = dist;
if(dist > dist_max)
dist_max = dist;
}
printf("Max distance: %f \n",dist_max);
printf("Min distance: %f \n",dist_min);
vector<DMatch> goodmatches;
for(int i = 0;i < matches.size(); i++)
{
if(matches[i].distance < 2*dist_min)
goodmatches.push_back(matches[i]);
}
Mat imgout;
drawMatches(img1,
keypoints1,
img2,
keypoints2,
goodmatches,
imgout,
Scalar::all(-1),
Scalar::all(-1),
vector<char>(),
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow("Matches",imgout);
for(int i = 0; i < goodmatches.size(); i++)
{
printf("Good Matches[%d] keypoint 1: %d -- keypoint 2: %d",i,goodmatches[i].queryIdx,goodmatches[i].trainIdx);
}
waitKey(0);
return 0;
}
特徵檢測和匹配結果:
以上程式均是在opencv2.4.9+vs2010+win7條件下執行的,有錯誤希望指標,相互學習,共同進步!!