1. 程式人生 > >OpenCV_區域性影象特徵的提取與匹配_原始碼

OpenCV_區域性影象特徵的提取與匹配_原始碼

OpenCV的feature2d module中提供了從區域性影象特徵(Local image feature)的檢測、特徵向量(feature vector)的提取,到特徵匹配的實現。其中的區域性影象特徵包括了常用的幾種區域性影象特徵檢測與描述運算元,如FAST、SURF、SIFT、以及ORB。對於高維特徵向量之間的匹配,OpenCV主要有兩種方式:1)BruteForce窮舉法;2)FLANN近似K近鄰演算法(包含了多種高維特徵向量匹配的演算法,例如隨機森林等)。

下面的這段程式碼實現了基於OpenCV的區域性影象特徵檢測、特徵向量提取、以及高維特徵向量的匹配功能。

版本:OpenCV2.4.2 

LocalFeature.h

//	區域性影象特徵提取與匹配
//      Author:  www.icvpr.com
//	Blog  :  http://blog.csdn.net/icvpr
	
#ifndef _FEATURE_H_ 
#define _FEATURE_H_

#include <iostream>
#include <vector>
#include <string>

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

class Feature
{
public:
	Feature();
	~Feature();

	Feature(const string& detectType, const string& extractType, const string& matchType);

public:
	
	void detectKeypoints(const Mat& image, vector<KeyPoint>& keypoints);   // 檢測特徵點
	void extractDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptor);   // 提取特徵向量
	void bestMatch(const Mat& queryDescriptor, Mat& trainDescriptor, vector<DMatch>& matches);  // 最近鄰匹配
	void knnMatch(const Mat& queryDescriptor, Mat& trainDescriptor, vector<vector<DMatch>>& matches, int k);   // K近鄰匹配

	void saveKeypoints(const Mat& image, const vector<KeyPoint>& keypoints, const string& saveFileName = "");  // 儲存特徵點
	void saveMatches(const Mat& queryImage,
			 const vector<KeyPoint>& queryKeypoints,
			 const Mat& trainImage,
			 const vector<KeyPoint>& trainKeypoints,
			 const vector<DMatch>& matches,
			 const string& saveFileName = "");   // 儲存匹配結果到圖片中

private:
	Ptr<FeatureDetector> m_detector;
	Ptr<DescriptorExtractor> m_extractor;
	Ptr<DescriptorMatcher> m_matcher;

	string m_detectType;
	string m_extractType;
	string m_matchType;

};


#endif


LocalFeature.cpp

//  區域性影象特徵提取與匹配  
//  Author:  www.icvpr.com
//  Blog  :  http://blog.csdn.net/icvpr

#include "LocalFeature.h"

Feature::Feature()
{
	m_detectType = "SIFT";
	m_extractType = "SIFT";
	m_matchType = "FruteForce";
	initModule_nonfree(); 
}

Feature::~Feature()
{

}


Feature::Feature(const string& detectType, const string& extractType, const string& matchType)
{
	assert(!detectType.empty());
	assert(!extractType.empty());
	assert(!matchType.empty());

	m_detectType = detectType;
	m_extractType = extractType;
	m_matchType = matchType;
	initModule_nonfree(); 
}


void Feature::detectKeypoints(const Mat& image, std::vector<KeyPoint>& keypoints) 
{
	assert(image.type() == CV_8UC1);
	assert(!m_detectType.empty());

	keypoints.clear();
	m_detector = FeatureDetector::create(m_detectType);
	m_detector->detect(image, keypoints);

}



void Feature::extractDescriptors(const Mat& image, std::vector<KeyPoint>& keypoints, Mat& descriptor)
{
	assert(image.type() == CV_8UC1);
	assert(!m_extractType.empty());

	m_extractor = DescriptorExtractor::create(m_extractType);
	m_extractor->compute(image, keypoints, descriptor);

}


void Feature::bestMatch(const Mat& queryDescriptor, Mat& trainDescriptor, std::vector<DMatch>& matches) 
{
	assert(!queryDescriptor.empty());
	assert(!trainDescriptor.empty());
	assert(!m_matchType.empty());

	matches.clear();

	m_matcher = DescriptorMatcher::create(m_matchType);
	m_matcher->add(std::vector<Mat>(1, trainDescriptor));
	m_matcher->train();
	m_matcher->match(queryDescriptor, matches);

}


void Feature::knnMatch(const Mat& queryDescriptor, Mat& trainDescriptor, std::vector<std::vector<DMatch>>& matches, int k)
{
	assert(k > 0);
	assert(!queryDescriptor.empty());
	assert(!trainDescriptor.empty());
	assert(!m_matchType.empty());

	matches.clear();

	m_matcher = DescriptorMatcher::create(m_matchType);
	m_matcher->add(std::vector<Mat>(1, trainDescriptor));
	m_matcher->train();
	m_matcher->knnMatch(queryDescriptor, matches, k);

}



void Feature::saveKeypoints(const Mat& image, const vector<KeyPoint>& keypoints, const string& saveFileName)
{
	assert(!saveFileName.empty());

	Mat outImage;
	cv::drawKeypoints(image, keypoints, outImage, Scalar(255,255,0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS );

	//
	string saveKeypointsImgName = saveFileName + "_" + m_detectType + ".jpg";
	imwrite(saveKeypointsImgName, outImage);

}



void Feature::saveMatches(const Mat& queryImage,
							const vector<KeyPoint>& queryKeypoints,
							const Mat& trainImage,
							const vector<KeyPoint>& trainKeypoints,
							const vector<DMatch>& matches,
							const string& saveFileName)
{
	assert(!saveFileName.empty());

	Mat outImage;
	cv::drawMatches(queryImage, queryKeypoints, trainImage, trainKeypoints, matches, outImage, 
				Scalar(255, 0, 0), Scalar(0, 255, 255), vector<char>(),  DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

	//
	string saveMatchImgName = saveFileName + "_" + m_detectType + "_" + m_extractType + "_" + m_matchType + ".jpg";
	imwrite(saveMatchImgName, outImage);
}



測試程式碼, main.cpp

//  區域性影象特徵提取與匹配  
//  Author:  www.icvpr.com  
//  Blog  : http://blog.csdn.net/icvpr    
  
#include   
#include   
#include <opencv2/opencv.hpp>  
  
using namespace cv;  
using namespace std;  
  
#include "LocalFeature.h"  
  
int main(int argc, char** argv)  
{  
	if (argc != 6)  
	{  
		cout << "wrong usage!" << endl;  
		cout << "usage: .exe FAST SIFT BruteForce queryImage trainImage" << endl;  
		return -1;  
	}  
  
	string detectorType = argv[1];  
	string extractorType = argv[2];  
	string matchType = argv[3];  
	string queryImagePath = argv[4];  
	string trainImagePath = argv[5];  
	  
	Mat queryImage = imread(queryImagePath, CV_LOAD_IMAGE_GRAYSCALE);  
	if (queryImage.empty())  
	{  
		cout<<"read failed"<< endl;  
		return -1;  
	}  
	  
	Mat trainImage = imread(trainImagePath, CV_LOAD_IMAGE_GRAYSCALE);  
	if (trainImage.empty())  
	{  
		cout<<"read failed"<< endl;  
		return -1;  
	}  
	  
	Feature feature(detectorType, extractorType, matchType);  
	  
	vector queryKeypoints, trainKeypoints;  
	feature.detectKeypoints(queryImage, queryKeypoints);  
	feature.detectKeypoints(trainImage, trainKeypoints);  
	  
	Mat queryDescriptor, trainDescriptor;  
	  
	feature.extractDescriptors(queryImage, queryKeypoints, queryDescriptor);  
	feature.extractDescriptors(trainImage, trainKeypoints, trainDescriptor);  
	  
	vector matches;  
	feature.bestMatch(queryDescriptor, trainDescriptor, matches);  
	  
	vector<vector> knnmatches;  
	feature.knnMatch(queryDescriptor, trainDescriptor, knnmatches, 2);  
	  
	Mat outImage;  
	feature.saveMatches(queryImage, queryKeypoints, trainImage, trainKeypoints, matches, "../");  
	  
	return 0;  
}  


下面是對不同的區域性影象特徵檢測運算元的實驗對比結果:

(說明:這裡只是簡單地對各個區域性影象特徵檢測運算元進行了對比,實際應用中需要考慮不同檢測運算元的特點,以及所應用的場景來選擇。)

1. FAST+SIFT+FLANN (即區域性影象特徵檢測運算元+特徵向量描述運算元+高維特徵向量匹配方法)

2. HARRIS+SIFT+FLANN

3. SURF+SIFT+FLANN

4. MSER+SIFT+FLANN

5. STAR+SIFT+FLANN

6.SIFT+SIFT+FLANN

7. ORB+ORB+FLANN

------------------------------------------