当前位置: 代码迷 >> 综合 >> ORB、SIFT、SURF特征提取与匹配
  详细解决方案

ORB、SIFT、SURF特征提取与匹配

热度:18   发布时间:2023-12-23 14:35:04.0
  • 1、ORB特征提取与匹配

ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST(Features from  Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust IndependentElementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。

对于FAST角点特征的提取主要分为:粗提取,机器学习的方法筛选最优特征点,非极大值抑制去除局部较密集特征点,特征点的尺度不变形,特征点的旋转不变性5个步骤。

BRIEF 是 Binary Robust Independent Elementary Features 的简称,它的作用是根据一组关键点创建二元特征向量。它是在一个特征点的邻域内,选择n对像素点pi、qi(i=1,2,…,n)。然后比较每个点对的灰度值的大小。如果I(pi)> I(qi),则生成二进制串中的1,否则为0。所有的点对都进行比较,则生成长度为n的二进制串。一般n取128、256或512,opencv默认为256。

具体的详细细节参见:

https://blog.csdn.net/qq_20791919/article/details/80176643

https://www.cnblogs.com/alexme/p/11345701.html

在ORB-SLAM算法中对ORB特征提取与匹配进行了改进,使得图像中提取的特征点分布更加均匀。主要思想是:设定提取的ORB特征点数量为1000个。将图像进行网格划分,设置FAST角点的最大阈值为12,最小为5。然后使用最大阈值对每一个网格图像都进行FAST特征点提取,如果没有提取到特征点,则减小阈值再次提取,如若到了最小阈值还没有提取到特征点,那么跳过该网格。在匹配过程中,与传统的暴力匹配不同,搜索对应网格区域内的ORB特征点作为匹配点,提高匹配的正确性。

在ubuntu系统上使用OpenCV3.3.1,对OpenCV中的ORB特征提取与匹配算法进行测试,,测试代码(SLAM14讲代码略做修改)为:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>using namespace std;
using namespace cv;int main ( int argc, char** argv )
{if ( argc != 3 ){cout<<"usage: feature_extraction img1 img2"<<endl;return 1;}//-- 读取图像Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR );Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR );//-- 初始化,提取1000个ORB特征点std::vector<KeyPoint> keypoints_1, keypoints_2;Mat descriptors_1, descriptors_2;Ptr<FeatureDetector> detector = ORB::create(1000);Ptr<DescriptorExtractor> descriptor = ORB::create(1000);// Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);// Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);Ptr<DescriptorMatcher> matcher  = DescriptorMatcher::create ( "BruteForce-Hamming" );//-- 第一步:检测 Oriented FAST 角点位置detector->detect ( img_1,keypoints_1 );detector->detect ( img_2,keypoints_2 );//-- 第二步:根据角点位置计算 BRIEF 描述子descriptor->compute ( img_1, keypoints_1, descriptors_1 );descriptor->compute ( img_2, keypoints_2, descriptors_2 );Mat outimg1, outimg2;drawKeypoints( img_1, keypoints_1, outimg1, Scalar(0, 255, 0), DrawMatchesFlags::DEFAULT );drawKeypoints( img_2, keypoints_2, outimg2, Scalar(0, 255, 0), DrawMatchesFlags::DEFAULT );imwrite("./result/orb_feature.png", outimg1);//-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离vector<DMatch> matches;//BFMatcher matcher ( NORM_HAMMING );matcher->match ( descriptors_1, descriptors_2, matches );//-- 第四步:匹配点对筛选double min_dist=10000, max_dist=0;//找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离for ( int i = 0; i < descriptors_1.rows; i++ ){double dist = matches[i].distance;if ( dist < min_dist ) min_dist = dist;if ( dist > max_dist ) max_dist = dist;}// 仅供娱乐的写法min_dist = min_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;max_dist = max_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;printf ( "-- Max dist : %f \n", max_dist );printf ( "-- Min dist : %f \n", min_dist );//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.std::vector< DMatch > good_matches;for ( int i = 0; i < descriptors_1.rows; i++ ){if ( matches[i].distance <= max ( 2*min_dist, 30.0 ) ){good_matches.push_back ( matches[i] );}}//-- 第五步:绘制匹配结果Mat img_match;Mat img_goodmatch;drawMatches ( outimg1, keypoints_1, outimg2, keypoints_2, matches, img_match, Scalar(0, 255, 255), Scalar(0, 255, 0));drawMatches ( outimg1, keypoints_1, outimg2, keypoints_2, good_matches, img_goodmatch, Scalar(0, 255, 255), Scalar(0, 255, 0));imwrite("./result/orb_match1.png", img_match);imwrite("./result/orb_match2.png", img_goodmatch);return 0;
}

CMakeLists可以简单的写为:

cmake_minimum_required( VERSION 2.8 )
project( orb )set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )find_package( OpenCV REQUIRED )include_directories( ${OpenCV_INCLUDE_DIRS} )add_executable( feature_extraction feature_extraction.cpp  )
target_link_libraries( feature_extraction ${OpenCV_LIBS} )

使用时需要读入图片,并对保存结果的路径进行修改。若在Windows上使用,需要对输入的参数进行修改。以上代码的测试结果为:             

   

可以看出:特征点的分布较为集中,特和那个匹配筛选前误匹配较多,筛选后结果相对较好。对于均匀分布ORB特征提取与匹配的测试代码为:https://github.com/zwl2017/ORB_Feature

测试结果为:

    

2、SIFT和SURF特征提取与匹配

尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。Lowe将SIFT算法分解为如下四步:

尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。

关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。

方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。

关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化

其具体的详细细节可以参考:https://blog.csdn.net/zddblog/article/details/7521424

可以通过C++代码自己实现SIFT算法:https://blog.csdn.net/maweifei/article/details/58227605

由于OpenCV3中的SIFT和SURF算法被移植到contrib模块中,因此为了简单起见,使用OpenCV2进行实验的测试,SIFT代码如下:

///SIFT特征点匹配  
#include "opencv2/opencv.hpp"  
#include "opencv2/nonfree/nonfree.hpp"//SIFT相关  
#include "opencv2/legacy/legacy.hpp"//匹配器相关  
#include <iostream>  using namespace cv;
using namespace std;int main()
{//1.SURF特征点提取——detect()方法    Mat srcImg1 = imread("nn_left.jpg", CV_LOAD_IMAGE_COLOR);Mat srcImg2 = imread("nn_right.jpg", CV_LOAD_IMAGE_COLOR);double t = getTickCount();//当前滴答数Mat dstImg1, dstImg2;//定义SIFT特征检测类对象    SiftFeatureDetector siftDetector;//SiftFeatureDetector是SIFT类的别名    //定义KeyPoint变量    vector<KeyPoint> keyPoints1;vector<KeyPoint> keyPoints2;//特征点检测    siftDetector.detect(srcImg1, keyPoints1);siftDetector.detect(srcImg2, keyPoints2);//绘制特征点(关键点)    drawKeypoints(srcImg1, keyPoints1, dstImg1);drawKeypoints(srcImg2, keyPoints2, dstImg2);//显示结果resize(dstImg1, dstImg1, Size(dstImg1.cols*0.5, dstImg1.rows*0.5));imshow("dstImg1", dstImg1);imshow("dstImg2", dstImg2);//2.特征点描述符(特征向量)提取——compute()方法    SiftFeatureDetector descriptor;//siftDescriptorExtractor是SIFT类的别名     Mat description1;Mat description2;descriptor.compute(srcImg1, keyPoints1, description1);descriptor.compute(srcImg2, keyPoints2, description2);//3.使用Flann匹配器进行匹配——FlannBasedMatcher类的match()方法    FlannBasedMatcher matcher;//实例化Flann匹配器  vector<DMatch> matches;matcher.match(description1, description2, matches);//4.对匹配结果进行筛选(依据DMatch结构体中的float类型变量distance进行筛选)    float minDistance = 100;float maxDistance = 0;for (int i = 0; i < matches.size(); i++){if (matches[i].distance < minDistance)minDistance = matches[i].distance;if (matches[i].distance > maxDistance)maxDistance = matches[i].distance;}cout << "minDistance: " << minDistance << endl;cout << "maxDistance: " << maxDistance << endl;vector<DMatch> goodMatches;for (int i = 0; i < matches.size(); i++){if (matches[i].distance < 2 * minDistance){goodMatches.push_back(matches[i]);}}//5.绘制匹配结果——drawMatches()    Mat dstImg3;Mat dstImg4;drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, goodMatches, dstImg3);resize(dstImg3, dstImg4, Size(dstImg3.cols*0.5, dstImg3.rows*0.5));t = ((double)getTickCount() - t) / getTickFrequency();cout << "算法用时:" << t << "秒" << endl;imshow("dstImg4", dstImg4);waitKey(0);return 0;
}

SURF的代码与SIFT类似,仅仅是实例化的类不一样,代码如下:

///SURF特征点匹配  
#include "opencv2/opencv.hpp"  
#include "opencv2/nonfree/nonfree.hpp"//SURF相关  
#include "opencv2/legacy/legacy.hpp"//匹配器相关  
#include <iostream>  using namespace cv;
using namespace std;int main()
{//1.SURF特征点提取——detect()方法    Mat srcImg1 = imread("nn_left.jpg", CV_LOAD_IMAGE_COLOR);Mat srcImg2 = imread("nn_right.jpg", CV_LOAD_IMAGE_COLOR);double t = getTickCount();//当前滴答数 Mat dstImg1, dstImg2;//定义SURF特征检测类对象    SurfFeatureDetector surfDetector;//SurfFeatureDetector是SURF类的别名    //定义KeyPoint变量    vector<KeyPoint> keyPoints1;vector<KeyPoint> keyPoints2;//特征点检测    surfDetector.detect(srcImg1, keyPoints1);surfDetector.detect(srcImg2, keyPoints2);//绘制特征点(关键点)    drawKeypoints(srcImg1, keyPoints1, dstImg1);drawKeypoints(srcImg2, keyPoints2, dstImg2);//显示结果    resize(dstImg1, dstImg1, Size(dstImg1.cols*0.5, dstImg1.rows*0.5));imshow("dstImg1", dstImg1);imshow("dstImg2", dstImg2);//2.特征点描述符(特征向量)提取——compute()方法    SurfDescriptorExtractor descriptor;//SurfDescriptorExtractor是SURF类的别名     Mat description1;Mat description2;descriptor.compute(srcImg1, keyPoints1, description1);descriptor.compute(srcImg2, keyPoints2, description2);//3.使用Flann匹配器进行匹配——FlannBasedMatcher类的match()方法    FlannBasedMatcher matcher;//实例化Flann匹配器  vector<DMatch> matches;matcher.match(description1, description2, matches);//4.对匹配结果进行筛选(依据DMatch结构体中的float类型变量distance进行筛选)    float minDistance = 100;float maxDistance = 0;for (int i = 0; i < matches.size(); i++){if (matches[i].distance < minDistance)minDistance = matches[i].distance;if (matches[i].distance > maxDistance)maxDistance = matches[i].distance;}cout << "minDistance: " << minDistance << endl;cout << "maxDistance: " << maxDistance << endl;vector<DMatch> goodMatches;for (int i = 0; i < matches.size(); i++){if (matches[i].distance < 2 * minDistance){goodMatches.push_back(matches[i]);}}//5.绘制匹配结果——drawMatches()    Mat dstImg3;drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, goodMatches, dstImg3);resize(dstImg3, dstImg3, Size(dstImg3.cols*0.5, dstImg3.rows*0.5));t = ((double)getTickCount() - t) / getTickFrequency();cout << "算法用时:" << t << "秒" << endl;imshow("dstImg3", dstImg3);waitKey(0);return 0;
}

 

  相关解决方案