OpenCV的feature2d module中提供了从局部图像特征(Local image feature)的检测、特征向量(feature vector)的提取,到特征匹配的实现。其中的局部图像特征包括了常用的几种局部图像特征检测与描述算子,如FAST、SURF、SIFT、以及ORB。对于高维特征向量之间的匹配,OpenCV主要有两种方式:1)BruteForce穷举法;2)FLANN近似K近邻算法(包含了多种高维特征向量匹配的算法,例如随机森林等)。
feature2d module: http://docs.opencv.org/modules/features2d/doc/features2d.html
OpenCV FLANN: http://docs.opencv.org/modules/flann/doc/flann.html
FLANN: http://www.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN
下面的这段代码实现了基于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
相关内容:www.icvpr.com
------------------------------------------
< 转载请注明:http://blog.csdn.net/icvpr >