精华内容
下载资源
问答
  • 特征点匹配

    千次阅读 2017-01-09 10:45:40
    Mastering OpenCV with Practical Computer Vision Projects 中的第三章里面讲到了几种特征点匹配的优化方式 1. OpenCV提供了两种Matching方式: • Brute-force matcher (cv::BFMatcher)  • Flann-...

    Mastering OpenCV with Practical Computer Vision Projects 中的第三章里面讲到了几种特征点匹配的优化方式




    1. OpenCV提供了两种Matching方式

    • Brute-force matcher (cv::BFMatcher) 

    • Flann-based matcher (cv::FlannBasedMatcher)

    Brute-force matcher就是用暴力方法找到点集一中每个descriptor在点集二中距离最近的descriptor;

    Flann-based matcher 使用快速近似最近邻搜索算法寻找(用快速的第三方库近似最近邻搜索算法)

    一般把点集一称为 train set (训练集)对应模板图像,点集二称为 query set(查询集)对应查找模板图的目标图像。

    为了提高检测速度,你可以调用matching函数前,先训练一个matcher。训练过程可以首先使用cv::FlannBasedMatcher来优化,为descriptor建立索引树,这种操作将在匹配大量数据时发挥巨大作用(比如在上百幅图像的数据集中查找匹配图像)。而Brute-force matcher在这个过程并不进行操作,它只是将train descriptors保存在内存中。



    2. 在matching过程可以使用cv::DescriptorMatcher的如下功能来进行匹配:

    • 简单查找最优匹配:void match(const Mat& queryDescriptors, vector<DMatch>& matches,const vector<Mat>& masks=vector<Mat>() );
    • 为每个descriptor查找K-nearest-matches:void knnMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,const vector<Mat>&masks=vector<Mat>(),bool compactResult=false );
    • 查找那些descriptors间距离小于特定距离的匹配:void radiusMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, maxDistance, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );

    3. matching结果包含许多错误匹配,错误的匹配分为两种:

    • False-positive matches: 将非对应特征点检测为匹配(我们可以对他做文章,尽量消除它)
    • False-negative matches: 未将匹配的特征点检测出来(无法处理,因为matching算法拒绝)
    为了消除False-positive matches采用如下两种方式:
    • Cross-match filter:
    在OpenCV中 cv::BFMatcher class已经支持交叉验证,建立 cv::BFMatcher将第二参数声明为true
    cv::Ptr<cv::DescriptorMatcher> matcher(new cv::BFMatcher(cv::NORM_HAMMING,true));
    经过Cross-match filter的结果:

    • Ratio test
    使用KNN-matching算法,令K=2。则每个match得到两个最接近的descriptor,然后计算最接近距离和次接近距离之间的比值,当比值大于既定值时,才作为最终match。

    1. void PatternDetector::getMatches(const cv::Mat& queryDescriptors,  std::vector<cv::DMatch>& matches)  
    2. {  
    3.     matches.clear();  
    4.     if (enableRatioTest)  
    5.     {  
    6.         // To avoid NaNs when best match has   
    7.         // zero distance we will use inverse ratio.   
    8.         const float minRatio = 1.f / 1.5f;  
    9.         // KNN match will return 2 nearest   
    10.         // matches for each query descriptor  
    11.         m_matcher->knnMatch(queryDescriptors, m_knnMatches, 2);  
    12.         for (size_t i=0; i<m_knnMatches.size(); i++)  
    13.         {  
    14.             const cv::DMatch& bestMatch = m_knnMatches[i][0];  
    15.             const cv::DMatch& betterMatch = m_knnMatches[i][1];  
    16.             float distanceRatio = bestMatch.distance /   
    17.                 betterMatch.distance;  
    18.             // Pass only matches where distance ratio between   
    19.             // nearest matches is greater than 1.5   
    20.             // (distinct criteria)  
    21.             if (distanceRatio < minRatio)  
    22.             {  
    23.                 matches.push_back(bestMatch);  
    24.             }  
    25.         }  
    26.     }  
    27.     else  
    28.     {  
    29.         // Perform regular match  
    30.         m_matcher->match(queryDescriptors, matches);  
    31.     }  
    32. }   



    4. Homography estimation

    为了进一步提升匹配精度,可以采用随机样本一致性(RANSAC)方法。
    因为我们是使用一幅图像(一个平面物体),我们可以将它定义为刚性的,可以在pattern image和query image的特征点之间找到单应性变换(homography transformation)。使用cv::findHomography找到这个单应性变换,使用RANSAC找到最佳单应性矩阵。(由于这个函数使用的特征点同时包含正确和错误匹配点,因此计算的单应性矩阵依赖于二次投影的准确性)
    1. bool PatternDetector::refineMatchesWithHomography  
    2. (  
    3. const std::vector<cv::KeyPoint>& queryKeypoints,  
    4. const std::vector<cv::KeyPoint>& trainKeypoints,   
    5. float reprojectionThreshold,  
    6. std::vector<cv::DMatch>& matches,  
    7. cv::Mat& homography  
    8. )  
    9. {  
    10. const int minNumberMatchesAllowed = 8;  
    11. if (matches.size() < minNumberMatchesAllowed)  
    12. return false;  
    13. // Prepare data for cv::findHomography  
    14. std::vector<cv::Point2f> srcPoints(matches.size());  
    15. std::vector<cv::Point2f> dstPoints(matches.size());  
    16. for (size_t i = 0; i < matches.size(); i++)  
    17. {  
    18. srcPoints[i] = trainKeypoints[matches[i].trainIdx].pt;  
    19. dstPoints[i] = queryKeypoints[matches[i].queryIdx].pt;  
    20. }  
    21. // Find homography matrix and get inliers mask  
    22. std::vector<unsigned char> inliersMask(srcPoints.size());  
    23. homography = cv::findHomography(srcPoints,   
    24. dstPoints,   
    25. CV_FM_RANSAC,   
    26. reprojectionThreshold,   
    27. inliersMask);  
    28. std::vector<cv::DMatch> inliers;  
    29. for (size_t i=0; i<inliersMask.size(); i++)  
    30. {  
    31. if (inliersMask[i])  
    32. inliers.push_back(matches[i]);  
    33. }  
    34. matches.swap(inliers);  //释放空内存
    35. return matches.size() > minNumberMatchesAllowed;  
    36. }   

    经过单应性变换的过滤结果

    运用H矩阵进行误匹配点去除:
    #include "opencv2/core/core.hpp"
    #include "opencv2/features2d/features2d.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/calib3d/calib3d.hpp"
    #include "opencv2/nonfree/nonfree.hpp"
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    int main(  )
    {
    	//【0】改变console字体颜色
    	system("color 1F"); 
    
    
    	//【1】载入原始图片
    	Mat srcImage1 = imread( "1.jpg", 1 );
    	Mat srcImage2 = imread( "2.jpg", 1 );
    	Mat copysrcImage1=srcImage1.clone();
    	Mat copysrcImage2=srcImage2.clone();
    
    	if( !srcImage1.data || !srcImage2.data )
    	{ printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }  
    
    	//【2】使用SURF算子检测关键点
    	int minHessian = 400;//SURF算法中的hessian阈值
    	SurfFeatureDetector detector( minHessian );//定义一个SurfFeatureDetector(SURF) 特征检测类对象  
    	vector<KeyPoint> keypoints_object, keypoints_scene;//vector模板类,存放任意类型的动态数组
    
    	//【3】调用detect函数检测出SURF特征关键点,保存在vector容器中
    	detector.detect( srcImage1, keypoints_object );
    	detector.detect( srcImage2, keypoints_scene );
    
    	//【4】计算描述符(特征向量)
    	SurfDescriptorExtractor extractor;
    	Mat descriptors_object, descriptors_scene;
    	extractor.compute( srcImage1, keypoints_object, descriptors_object );
    	extractor.compute( srcImage2, keypoints_scene, descriptors_scene );
    
    	//【5】使用FLANN匹配算子进行匹配
    	FlannBasedMatcher matcher;
    	vector< DMatch > matches;
    	matcher.match( descriptors_object, descriptors_scene, matches );
    	double max_dist = 0; double min_dist = 100;//最小距离和最大距离
    
    	//【6】计算出关键点之间距离的最大值和最小值
    	for( int i = 0; i < descriptors_object.rows; i++ )
    	{ 
    		double dist = matches[i].distance;
    		if( dist < min_dist ) min_dist = dist;
    		if( dist > max_dist ) max_dist = dist;
    	}
    
    	printf(">Max dist 最大距离 : %f \n", max_dist );
    	printf(">Min dist 最小距离 : %f \n", min_dist );
    
    	//【7】存下匹配距离小于3*min_dist的点对
    	std::vector< DMatch > good_matches;
    	for( int i = 0; i < descriptors_object.rows; i++ )
    	{ 
    		if( matches[i].distance < 3*min_dist )
    		{ 
    			good_matches.push_back( matches[i]);
    		}
    	}
    
    	//绘制出匹配到的关键点
    	Mat img_matches;
    	drawMatches( srcImage1, keypoints_object, srcImage2, keypoints_scene,
    		good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
    		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
    
    	//定义两个局部变量
    	vector<Point2f> obj;
    	vector<Point2f> scene;
    
    	//从匹配成功的匹配对中获取关键点
    	for( unsigned int i = 0; i < good_matches.size(); i++ )
    	{
    		obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt );
    		scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt );
    	}
    	vector<unsigned char> listpoints;
    
    	//Mat H = findHomography( obj, scene, CV_RANSAC );//计算透视变换 
    	Mat H = findHomography( obj, scene, CV_RANSAC,3, listpoints);//计算透视变换 
    
    
    	std::vector< DMatch > goodgood_matches;
    	for (int i=0;i<listpoints.size();i++)
    	{
    		if ((int)listpoints[i])
    		{	
    		
    			goodgood_matches.push_back(good_matches[i]);
    		
    			
    			cout<<(int)listpoints[i]<<endl;
    		}
    		
    	}
    	cout<<"listpoints大小:"<<listpoints.size()<<endl;
    	cout<<"goodgood_matches大小:"<<goodgood_matches.size()<<endl;
    	cout<<"good_matches大小:"<<good_matches.size()<<endl;
    	Mat Homgimg_matches;
    	drawMatches( copysrcImage1, keypoints_object, copysrcImage2, keypoints_scene,
    		goodgood_matches, Homgimg_matches, Scalar::all(-1), Scalar::all(-1),
    		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
    
    	imshow("去除误匹配点后;",Homgimg_matches);
    
    
    	//从待测图片中获取角点
    	vector<Point2f> obj_corners(4);
    	obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( srcImage1.cols, 0 );
    	obj_corners[2] = cvPoint( srcImage1.cols, srcImage1.rows ); obj_corners[3] = cvPoint( 0, srcImage1.rows );
    	vector<Point2f> scene_corners(4);
    
    	//进行透视变换
    	perspectiveTransform( obj_corners, scene_corners, H);
    
    	//绘制出角点之间的直线
    	line( img_matches, scene_corners[0] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[1] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4 );
    	line( img_matches, scene_corners[1] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[2] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );
    	line( img_matches, scene_corners[2] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[3] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );
    	line( img_matches, scene_corners[3] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[0] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );
    
    	//显示最终结果
    	imshow( "效果图", img_matches );
    
    	waitKey(0);
    	return 0;
    }



    展开全文
  • 特征点匹配Demo

    2018-06-07 16:28:23
    特征点匹配Demo,可以实现特征点的匹配,使用SURF算法
  • SIFT特征点匹配

    2015-05-03 00:18:23
    SIFT特征点匹配,计算SIFT特征点,匹配特征点,为后续图像配准做准备工作
  • 特征点匹配源码

    2014-09-06 09:23:13
    该资源包括用opencv实现的特征点匹配,通过提取特征点匹配特征点实现图像间的匹配,附有图片,运行的时候改下图片地址就行,希望对你有帮助
  • SIFT特征点匹配代码

    2018-07-13 22:18:40
    SIFT特征点匹配的代码和实验报告,需要2008的VS和openCV库。如果VS版本高的话需要下载一个2008版本的guill.gll文件。修改代码中视频路径即可实现特征点匹配。有实验报告和用例。
  • 图像拼接 之 特征点匹配

    千次阅读 2019-01-29 22:28:55
    特征点匹配

    介绍

    在人们实际生活和工作中时常需要获得宽视角、高分辨率的全景图像,但是由于摄像设备的机械限制,一般只能得到局部的成像。全景相机、广角镜头等可以直接获得全景图像的硬件设备往往代价高昂,难以广泛推行,因此催生出了使用计算机视觉的方法来将多个局部的图像拼接生成全景图像,也即是图像拼接技术。
    图像拼接是将多张小尺寸的相同场景的图像拼接成为一张大尺寸图像的技术。OpenCV 提供了 Stitcher 类对传统的图像拼接算法进行了实现,其算法流程如下图所示:

    OpenCV图像拼接流程
    来自 Stitching Pipeline

    Stitcher 提供的图像拼接算法流程可以划分为 RegistrationComposition 两个阶段,其中 Registration 阶段中又包含了特征点提取、特征点匹配、相机参数估计等步骤。本文内容主要针对于图像拼接中的特征点匹配步骤相关算法进行介绍。

    特征点对搜索

    输入原始图像后,可以通过 SIFT、SURF 或者 ORB 等特征点提取算法计算出图像的特征点。通过这些特征点,可以对图像之间的特征点进行匹配。
    传统算法中使用 KNN 算法计算两个图像之间的匹配特征点对。每个提取出的特征点均包含特征点描述向量,从而可以计算出两个特征点之间的距离。对于特征匹配的 K 个最近邻计算,如果使用简单遍历,在提取出的特征点数量较高的情况下需要耗费大量计算时间。对此可以使用 K-D 树来优化计算过程。计算得到的最近邻通过一定的筛选方法获取到最可靠的匹配特征点。

    建立 K-D 树

    K-D 树是对于 k 维数据的二叉树结构,应用于多维键值搜索,其中每个节点均是 k 维数据。
    一种典型的创建 K-D 树的方法如下:

    • 对于 k 维的数据,轮流选择其中一个维度计算轴垂直分割面,例如 32 维的 ORB 特征描述符则对于 0 - 31 维执行轮转;
    • 计算选定轴的中位数并将所有数据划分为两部分放入左右的子树。
      通过轮流选择维度,可以使得计算生成的 K-D 树平衡,每个叶节点与根节点的距离相近。

    搜索 K-D 树

    建立完成 K-D 树后,可以通过以下流程对目标数据进行搜索:

    • 从根节点开始向下递归,根据当前维度选择进入左右子树,直到到达叶节点,将其作为最佳点;
    • 回溯递归过程,如果当前节点距离优于最佳点,则使用其替换当前最佳点。并检查另一侧的子树中是否存在更优的点,如果存在则从该节点向下递归;
    • 回溯到根节点后,当前最佳点则为最终选择点。

    特征点对的筛选

    KNN 算法会为每个特征点计算出对应的最近距离匹配点,但是由于两个图像并不一定重合,因此部分特征点可能找不到对应的匹配点,需要对计算出的匹配结果进行筛选以删除不可靠的匹配点对。

    其中一种筛选方法如下:

    • 设置 KNN 算法中 K 值为2,计算出当前特征点的最近邻以及次近邻;
    • 计算特征点与两个邻点的距离之比是否小于阈值,即在这里插入图片描述
    • 选择小于阈值的特征点与最近邻作为匹配点对,筛除不匹配的点对。

    计算单应性矩阵

    计算获取到特征点匹配对后,可以基于其计算两个图像之间的射影变换关系,进而得到两个图像之间的重叠部分并执行后续的图像融合等操作。计算两个图像的射影变换,也即是计算两个图像之间的单应性矩阵 H。

    基于特征点对计算单应性矩阵

    图像中的某一像素点可以通过单应性矩阵变换到另一图像的对应点,如公式1所示:
    公式1
    公式1(1)
    其中 pa 为图像a中的像素点,pb 为图像b中的像素点,Hab 为 a、b 图像之间的单应性矩阵。
    将公式1展开并改写成矩阵形式可以得到公式2:
    公式2 (2)
    不难发现左侧矩阵前两行线性组合可以得到第三行,因此可以将公式2改写为公式3:
    公式3 (3)
    由于单应性矩阵包含8个自由度,因此选择4个特征点匹配对即可以求解单应性矩阵,即公式4:
    公式4 (4)

    对于公式4进行奇异值分解可以获取单应性矩阵,其中 V 的最后一列即为单应性矩阵的值。

    RANSAC 优化单应性矩阵

    上述的单应性矩阵计算方法仅仅依赖于计算得到的特征点对的其中4个,这在实际使用过程中是巨大的浪费并且结果准确性依赖于特征点对的选取。对于这一问题,可以使用 RANSAC 算法进行优化。
    首先明确内点的概念:内点是重映射误差在给定阈值之内匹配点对,与外点相对应。其中重映射误差的计算公式如公式5所示:
    公式5 (5)
    RANSAC 算法的核心是从样本集中抽取多少次才能保证至少有一次不是外点的概率为q。根据概率q可以计算出迭代次数,相关公式如公式6所示:
    公式6 (6)
    其中 ε 表示样本集中外点的比例。

    RANSAC 算法过程如下:

    1. 给定概率值q,重映射误差η,并计算最大迭代次数;
    2. 进入迭代,选择4个特征点对,按上述单应性矩阵的方法计算出当前 H;
    3. 计算所有特征点对的重映射误差,计算内点的数量;
    4. 通过公式6对最大迭代次数进行更新;
    5. 结束迭代后选择最大的内点数量,并根据这些内点计算出相应的单应性矩阵作为最终的输出。

    在计算得到两个图像单应性矩阵后并不确定两个图像之间是否存在可靠的匹配关系,此时可以通过计算置信度来反应这一匹配关系。置信度 c 计算方法如公式7所示:
    公式7 (7)
    一般置信度阈值设为 1 左右,当两图像之间置信度大于1,则可认为两个图像可以进行拼接。

    总结

    本文内容主要介绍了图像拼接中特征点匹配相关的基础算法以及相关流程,其中还有一些优化的方法并未包含,例如将图像坐标归一化以消除图像尺度等造成的干扰、应用 LM 算法优化单应性矩阵等。
    虽说对于上述的流程已经做了简单的 C++ 实现,但是计算性能极其低劣,同时代码也十分丑陋,还是不贴上来损害读者的眼睛了。

    参考资料

    1. Opencv2.4.9源码分析——Stitching(二)
    2. k-d树
    展开全文
  • ORB特征点匹配

    热门讨论 2012-03-27 17:23:02
    ORB特征用于图像特征点的匹配,对学习图像特征点匹配的同学很有帮助。
  • SURF特征点匹配.rar

    2019-05-09 09:27:57
    实现两张图像的SURF特征点匹配
  • 通过一个示例,进行FLANN进行特征点匹配,适合人群为刚学习OpenCV的学生及工作人员,也可供相关图像处理工程师进行参考
  • matlab图像特征点匹配

    2015-06-21 18:56:59
    matlab数字图像处理部分 特征点匹配代码,多种匹配方式可选,详见代码段
  • 奇异值控制的图像特征点匹配方法
  • 基于特征点匹配的多视图像校正
  • 本文提出了一种鲁棒的特征点匹配方法,结合了尺度不变特征变换(SIFT)和支持向量机(SVM)来匹配动态航空图像的配准特征。 在这种方法中,每个特征点都由128维SIFT描述符描述为训练向量。 然后通过支持向量机建立...
  • 使用python-opencv实现用ORB算法实现特征点匹配,代码量较低,内容较少。
  • 一种新颖的遥感影像特征点匹配方法
  • 为了解决上述问题,提出一种基于特征点匹配的自适应目标跟踪算法。算法初始化时在选定的目标区域内提取特征点,跟踪过程中通过对前后两帧的特征点进行匹配,计算出目标的位置、尺度和旋转变化,进而实现对目标的跟踪...
  • 特征点匹配及消除误匹配点

    千次阅读 2018-06-06 20:57:32
    Mastering OpenCV with Practical Computer Vision Projects 中的第三章里面讲到了几种特征点匹配的优化方式1. OpenCV提供了两种Matching方式:• Brute-force matcher (cv::BFMatcher) • Flann-based matcher ...

    Mastering OpenCV with Practical Computer Vision Projects 中的第三章里面讲到了几种特征点匹配的优化方式




    1. OpenCV提供了两种Matching方式

    • Brute-force matcher (cv::BFMatcher) 

    • Flann-based matcher (cv::FlannBasedMatcher)

    Brute-force matcher就是用暴力方法找到点集一中每个descriptor在点集二中距离最近的descriptor;

    Flann-based matcher 使用快速近似最近邻搜索算法寻找(用快速的第三方库近似最近邻搜索算法)

    一般把点集一称为 train set (训练集)对应模板图像,点集二称为 query set(查询集)对应查找模板图的目标图像。

    为了提高检测速度,你可以调用matching函数前,先训练一个matcher。训练过程可以首先使用cv::FlannBasedMatcher来优化,为descriptor建立索引树,这种操作将在匹配大量数据时发挥巨大作用(比如在上百幅图像的数据集中查找匹配图像)。而Brute-force matcher在这个过程并不进行操作,它只是将train descriptors保存在内存中。



    2. 在matching过程可以使用cv::DescriptorMatcher的如下功能来进行匹配:

    • 简单查找最优匹配:void match(const Mat& queryDescriptors, vector<DMatch>& matches,const vector<Mat>& masks=vector<Mat>() );
    • 为每个descriptor查找K-nearest-matches:void knnMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,const vector<Mat>&masks=vector<Mat>(),bool compactResult=false );
    • 查找那些descriptors间距离小于特定距离的匹配:void radiusMatch(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, maxDistance, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );

    3. matching结果包含许多错误匹配,错误的匹配分为两种:

    • False-positive matches: 将非对应特征点检测为匹配(我们可以对他做文章,尽量消除它)
    • False-negative matches: 未将匹配的特征点检测出来(无法处理,因为matching算法拒绝)
    为了消除False-positive matches采用如下两种方式:
    • Cross-match filter:
    在OpenCV中 cv::BFMatcher class已经支持交叉验证,建立 cv::BFMatcher将第二参数声明为true
    cv::Ptr<cv::DescriptorMatcher> matcher(new cv::BFMatcher(cv::NORM_HAMMING,true));
    经过Cross-match filter的结果:

    • Ratio test
    使用KNN-matching算法,令K=2。则每个match得到两个最接近的descriptor,然后计算最接近距离和次接近距离之间的比值,当比值大于既定值时,才作为最终match。

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void PatternDetector::getMatches(const cv::Mat& queryDescriptors,  std::vector<cv::DMatch>& matches)  
    2. {  
    3.     matches.clear();  
    4.     if (enableRatioTest)  
    5.     {  
    6.         // To avoid NaNs when best match has   
    7.         // zero distance we will use inverse ratio.   
    8.         const float minRatio = 1.f / 1.5f;  
    9.         // KNN match will return 2 nearest   
    10.         // matches for each query descriptor  
    11.         m_matcher->knnMatch(queryDescriptors, m_knnMatches, 2);  
    12.         for (size_t i=0; i<m_knnMatches.size(); i++)  
    13.         {  
    14.             const cv::DMatch& bestMatch = m_knnMatches[i][0];  
    15.             const cv::DMatch& betterMatch = m_knnMatches[i][1];  
    16.             float distanceRatio = bestMatch.distance /   
    17.                 betterMatch.distance;  
    18.             // Pass only matches where distance ratio between   
    19.             // nearest matches is greater than 1.5   
    20.             // (distinct criteria)  
    21.             if (distanceRatio < minRatio)  
    22.             {  
    23.                 matches.push_back(bestMatch);  
    24.             }  
    25.         }  
    26.     }  
    27.     else  
    28.     {  
    29.         // Perform regular match  
    30.         m_matcher->match(queryDescriptors, matches);  
    31.     }  
    32. }   



    4. Homography estimation

    为了进一步提升匹配精度,可以采用随机样本一致性(RANSAC)方法。
    因为我们是使用一幅图像(一个平面物体),我们可以将它定义为刚性的,可以在pattern image和query image的特征点之间找到单应性变换(homography transformation)。使用cv::findHomography找到这个单应性变换,使用RANSAC找到最佳单应性矩阵。(由于这个函数使用的特征点同时包含正确和错误匹配点,因此计算的单应性矩阵依赖于二次投影的准确性)
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. bool PatternDetector::refineMatchesWithHomography  
    2. (  
    3. const std::vector<cv::KeyPoint>& queryKeypoints,  
    4. const std::vector<cv::KeyPoint>& trainKeypoints,   
    5. float reprojectionThreshold,  
    6. std::vector<cv::DMatch>& matches,  
    7. cv::Mat& homography  
    8. )  
    9. {  
    10. const int minNumberMatchesAllowed = 8;  
    11. if (matches.size() < minNumberMatchesAllowed)  
    12. return false;  
    13. // Prepare data for cv::findHomography  
    14. std::vector<cv::Point2f> srcPoints(matches.size());  
    15. std::vector<cv::Point2f> dstPoints(matches.size());  
    16. for (size_t i = 0; i < matches.size(); i++)  
    17. {  
    18. srcPoints[i] = trainKeypoints[matches[i].trainIdx].pt;  
    19. dstPoints[i] = queryKeypoints[matches[i].queryIdx].pt;  
    20. }  
    21. // Find homography matrix and get inliers mask  
    22. std::vector<unsigned char> inliersMask(srcPoints.size());  
    23. homography = cv::findHomography(srcPoints,   
    24. dstPoints,   
    25. CV_FM_RANSAC,   
    26. reprojectionThreshold,   
    27. inliersMask);  
    28. std::vector<cv::DMatch> inliers;  
    29. for (size_t i=0; i<inliersMask.size(); i++)  
    30. {  
    31. if (inliersMask[i])  
    32. inliers.push_back(matches[i]);  
    33. }  
    34. matches.swap(inliers);  //释放空内存
    35. return matches.size() > minNumberMatchesAllowed;  
    36. }   

    经过单应性变换的过滤结果

    运用H矩阵进行误匹配点去除:
    1.   
    1. #include "opencv2/core/core.hpp"  
    2. #include "opencv2/features2d/features2d.hpp"  
    3. #include "opencv2/highgui/highgui.hpp"  
    4. #include "opencv2/calib3d/calib3d.hpp"  
    5. #include "opencv2/nonfree/nonfree.hpp"  
    6. #include <iostream>  
    7. using namespace cv;  
    8. using namespace std;  
    9.   
    10. int main(  )  
    11. {  
    12.     //【0】改变console字体颜色  
    13.     system("color 1F");   
    14.   
    15.   
    16.     //【1】载入原始图片  
    17.     Mat srcImage1 = imread( "1.jpg", 1 );  
    18.     Mat srcImage2 = imread( "2.jpg", 1 );  
    19.     Mat copysrcImage1=srcImage1.clone();  
    20.     Mat copysrcImage2=srcImage2.clone();  
    21.   
    22.     if( !srcImage1.data || !srcImage2.data )  
    23.     { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }    
    24.   
    25.     //【2】使用SURF算子检测关键点  
    26.     int minHessian = 400;//SURF算法中的hessian阈值  
    27.     SurfFeatureDetector detector( minHessian );//定义一个SurfFeatureDetector(SURF) 特征检测类对象    
    28.     vector<KeyPoint> keypoints_object, keypoints_scene;//vector模板类,存放任意类型的动态数组  
    29.   
    30.     //【3】调用detect函数检测出SURF特征关键点,保存在vector容器中  
    31.     detector.detect( srcImage1, keypoints_object );  
    32.     detector.detect( srcImage2, keypoints_scene );  
    33.   
    34.     //【4】计算描述符(特征向量)  
    35.     SurfDescriptorExtractor extractor;  
    36.     Mat descriptors_object, descriptors_scene;  
    37.     extractor.compute( srcImage1, keypoints_object, descriptors_object );  
    38.     extractor.compute( srcImage2, keypoints_scene, descriptors_scene );  
    39.   
    40.     //【5】使用FLANN匹配算子进行匹配  
    41.     FlannBasedMatcher matcher;  
    42.     vector< DMatch > matches;  
    43.     matcher.match( descriptors_object, descriptors_scene, matches );  
    44.     double max_dist = 0; double min_dist = 100;//最小距离和最大距离  
    45.   
    46.     //【6】计算出关键点之间距离的最大值和最小值  
    47.     forint i = 0; i < descriptors_object.rows; i++ )  
    48.     {   
    49.         double dist = matches[i].distance;  
    50.         if( dist < min_dist ) min_dist = dist;  
    51.         if( dist > max_dist ) max_dist = dist;  
    52.     }  
    53.   
    54.     printf(">Max dist 最大距离 : %f \n", max_dist );  
    55.     printf(">Min dist 最小距离 : %f \n", min_dist );  
    56.   
    57.     //【7】存下匹配距离小于3*min_dist的点对  
    58.     std::vector< DMatch > good_matches;  
    59.     forint i = 0; i < descriptors_object.rows; i++ )  
    60.     {   
    61.         if( matches[i].distance < 3*min_dist )  
    62.         {   
    63.             good_matches.push_back( matches[i]);  
    64.         }  
    65.     }  
    66.   
    67.     //绘制出匹配到的关键点  
    68.     Mat img_matches;  
    69.     drawMatches( srcImage1, keypoints_object, srcImage2, keypoints_scene,  
    70.         good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),  
    71.         vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );  
    72.   
    73.     //定义两个局部变量  
    74.     vector<Point2f> obj;  
    75.     vector<Point2f> scene;  
    76.   
    77.     //从匹配成功的匹配对中获取关键点  
    78.     for( unsigned int i = 0; i < good_matches.size(); i++ )  
    79.     {  
    80.         obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt );  
    81.         scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt );  
    82.     }  
    83.     vector<unsigned char> listpoints;  
    84.   
    85.     //Mat H = findHomography( obj, scene, CV_RANSAC );//计算透视变换   
    86.     Mat H = findHomography( obj, scene, CV_RANSAC,3, listpoints);//计算透视变换   
    87.   
    88.   
    89.     std::vector< DMatch > goodgood_matches;  
    90.     for (int i=0;i<listpoints.size();i++)  
    91.     {  
    92.         if ((int)listpoints[i])  
    93.         {     
    94.           
    95.             goodgood_matches.push_back(good_matches[i]);  
    96.           
    97.               
    98.             cout<<(int)listpoints[i]<<endl;  
    99.         }  
    100.           
    101.     }  
    102.     cout<<"listpoints大小:"<<listpoints.size()<<endl;  
    103.     cout<<"goodgood_matches大小:"<<goodgood_matches.size()<<endl;  
    104.     cout<<"good_matches大小:"<<good_matches.size()<<endl;  
    105.     Mat Homgimg_matches;  
    106.     drawMatches( copysrcImage1, keypoints_object, copysrcImage2, keypoints_scene,  
    107.         goodgood_matches, Homgimg_matches, Scalar::all(-1), Scalar::all(-1),  
    108.         vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );  
    109.   
    110.     imshow("去除误匹配点后;",Homgimg_matches);  
    111.   
    112.   
    113.     //从待测图片中获取角点  
    114.     vector<Point2f> obj_corners(4);  
    115.     obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( srcImage1.cols, 0 );  
    116.     obj_corners[2] = cvPoint( srcImage1.cols, srcImage1.rows ); obj_corners[3] = cvPoint( 0, srcImage1.rows );  
    117.     vector<Point2f> scene_corners(4);  
    118.   
    119.     //进行透视变换  
    120.     perspectiveTransform( obj_corners, scene_corners, H);  
    121.   
    122.     //绘制出角点之间的直线  
    123.     line( img_matches, scene_corners[0] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[1] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4 );  
    124.     line( img_matches, scene_corners[1] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[2] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );  
    125.     line( img_matches, scene_corners[2] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[3] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );  
    126.     line( img_matches, scene_corners[3] + Point2f( static_cast<float>(srcImage1.cols), 0), scene_corners[0] + Point2f( static_cast<float>(srcImage1.cols), 0), Scalar( 255, 0, 123), 4 );  
    127.   
    128.     //显示最终结果  
    129.     imshow( "效果图", img_matches );  
    130.   
    131.     waitKey(0);  
    132.     return 0;  
    133. }  


    展开全文
  • 本文提出一种用于英文签字识别的特征点匹配算法。该算法通过比较各特征点的局部特性及特征点之间的相对关系,计算各特征点间的相似性从而得到特征点匹配关系。特征点集通过沿笔划骨架的变尺度量测获得。本文提出的...
  • 模板匹配和特征点匹配学习笔记

    千次阅读 2020-09-24 21:55:34
    ​​​​​​刚好最近在做项目,老师让查模板匹配与特征点匹配的相关知识,搜了很多博客,整理成word文档,顺便也来发个博客。 模板匹配 模板匹配是一种最原始、最基本的识别方法。简单来说,模板匹配就是用一幅...

    ​​​​​​刚好最近在做项目,老师让查模板匹配与特征点匹配的相关知识,搜了很多博客,整理成word文档,顺便也来发个博客。

     

    1. 模板匹配

    模板匹配是一种最原始、最基本的识别方法。简单来说,模板匹配就是用一幅已知的模板图片在目标图片上依次滑动,每次滑动都计算模板与模板下方子图的相似度。如果是单个目标的匹配,只需要取相似度最大值所在的位置就可以得到匹配位置。如果要匹配多个目标,只需要设定阈值,只要相似度大于阈值则认为是匹配的目标。

    模板匹配的主要应用场景:字符匹配,车牌识别,图像拼接

    基于区域(灰度值的模板匹配

    最基础的一类模板匹配算法,这类算法是通过比较模板与图像之间的所有灰度值的相似度实现的。相似度的度量是通过比较两幅图灰度值的相关函数、协方差函数、差平方和、差绝对值和等测量极值,来判断两幅图的相似度。计算量大,一般用于简单的图像。OpenCV通过函数matchTemplate实现了模板匹配算法。minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置。

    在OpenCV中提供了6种匹配度量方法。
    (1)差值平方和匹配(CV_TM_SQDIFF)

    原理: 计算模板与某个子图的对应像素的差值平方和。

    越相似该值越小

     

    (2)标准化差值平方和匹配(CV_TM_SQDIFF_NORMED)

    原理:标准化的差值平方和

    越相似该值越小


    (3)相关匹配(CV_TM_CCORR)

    原理:模板与子图对应位置相乘

    越相似值越大


    (4)标准相关匹配(CV_TM_CCORR_NORMED)

    原理:标准化的相关匹配,去除了亮度线性变化对相似度计算的影响。可以保证图像和模板同时变量或变暗k倍时结果不变。

    越相似值越大


    (5)系数匹配法(CV_TM_CCOEFF)

    原理:把图像和模板都减去了各自的平均值,使得这两幅图像都没有直流分量。

    越相似值越大


    (6)标准相关系数匹配(CV_TMCCOEFF_NORMED)

    原理:把图像和模板都减去了各自的平均值,再各自除以各自的方差,保证图像和模板分别改变光照不影响计算结果,计算出的相关系数限制在-1到1之间,1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示没有线性关系。

    越相似值越大

     

    通常来讲,随着从简单测量方法(平方差)到更复杂的测量方法(相关系数法),可以获得越来越准确的匹配,但同时也会以越来越大的计算量为代价。

     

     

    优点:简单直接。

    缺点:不具有旋转不变性和尺度不变性。即当原图像发生旋转或缩放时,不能实现匹配,可以用特征点匹配的方法。

    算法优化:

    速度优化:

    为了提高搜索的效率,模板匹配的运行时间的复杂度取决于需要检查的平移数量,序贯相似性检测法(SSDA)在计算匹配度的同时,不断累积模板和像元的灰度差,当累积值大于某一指定阈值时,则说明该点为非匹配点,进行下一个位置的计算,这样大大减少了计算复杂度。为进一步提高速度,可以先进行粗配准,即:隔行、隔离的选取子图,用上述算法进行粗糙的定位,然后再对定位到的子图,用同样的方法求其8个邻域子图的最大R值作为最终配准图像。这样可以有效的减少子图个数,减少计算量,提高计算速度。

    精度优化:

    Hadamard变换算法(SATD):将模板与原图做差后得到矩阵Q,再对矩阵Q求其hadamard变化(左右同时乘以H,即HQH),对变换得到的矩阵求其元素的绝对值之和即SATD值,作为相似度的判别依据。对所有子图都进行如上变换后,找到SATD值最小的子图,便是最佳匹配。

    基于变换域的匹配

    基于傅里叶变换,基于小波变换,对噪声不明显,检测结果不受光照强度影响,可以较好的处理图像之间的旋转和尺度变化。

    基于矩阵分解的方法

    方法描述:将图像做矩阵分解,比如SVD奇异值分解和NMF非负矩阵分解等,然后再做相似度的计算。

    基于SVD分解的方法优点:奇异值的稳定性,比例不变性,旋转不变性和压缩性。即奇异值分解是基于整体的表示,不但具有正交变换、旋转、位移、镜像映射等代数和几何上的不变性,而且具有良好的稳定性和抗噪性,广泛应用于模式识别与图像分析中。对图像进行奇异值分解的目的是得到唯一、稳定的特征描述,降低特征空间的维度,提高抗干扰能力。

    基于SVD分解的方法缺点是:奇异值分解得到的奇异矢量中有负数存在,不能很好的解释其物理意义。

    基于NMF分解的方法:将非负矩阵分解为可以体现图像主要信息的基矩阵与系数矩阵,并且可以对基矩阵赋予很好的解释。源图像表示为基矩阵的加权组合,所以,NMF在人脸识别场合发挥着巨大的作用。

    基于矩阵特征值计算的方法还有很多,比如Trace变换,不变矩计算等。

    FAST-Match

            FAST-Match是在2D仿射变换下用于近似模板匹配的快速算法,其最小化绝对差分和(SAD)误差测量。 通过图像平滑度的密度对其进行采样。 对于每个可能的变换,使用次线性算法来近似SAD误差, 同时使用分支定界法进一步加速算法。 由于已知图像是分段平滑的,因此结果是具有近似保证的实际仿射模板匹配算法。

    1. 图像特征匹配算法

    特征点(角点)匹配是指寻找两幅图像之间的特征像素点的对应关系,从而确定图像的位置关系。

    特征点匹配可分为四个步骤:

    1. 提取检测子:在两张待匹配的图像中寻找那些最容易识别的像素点(角点),比如纹理丰富的物体边缘点,较暗区域中的亮点,较亮区域的暗点。
    2. 提取描述子:对于检测出的角点,用一些数学上的特征来描述,如梯度直方图,局部随机二值特征。检测子和描述子的常用提取方法有:sift,harris,surf,fast,agast,brisk,freak,brisk,brief/orb等。
    3. 匹配:通过各个角点的描述子来判断它们在两张图像中的对应关系,常用方法如 BruteForce、flann等。
    4. 消噪:去除错误匹配的外点,保留正确的匹配点。常用方法有KDTREE,BBF,Ransac,GTM等。

    在现实生活中,我们从不同的距离,不同的方向、角度,不同的光照条件下观察一个物体时,物体的大小,形状,明暗都会有所不同,但我们仍可判断是同一物体,理想的特征描述子也应该是对光照不敏感,具备尺度一致性(大小),旋转一致性(角度)等。

    ORB算法(特征点检测+特征点匹配)

    特征点检测

    对于 ORB 这类基于特征点匹配的算法来说,其对于检测那些有固定特征的且不易受背景环境影响的对象如人脸识别类的应用来说具有非常高的识别度和准确率,但对于更加一般的物体识别,如行人检测,则由于姿态和背景变化多样,因此并不能很好的寻找特定的特征点来完成。

    ORB算法是基于FAST特征点检测和BRIEF描述子改进的,运算速度快,具有一定的旋转不变性。(FAST算子检测+检测的特征点的方向信息)+(BRIEF描述子+旋转不变性)

    FAST角点:某像素点与其周围像素点邻域内有足够多的像素点相差较大,则该像素可能是角点。我们通常将这个邻域选为圆形,要判断是否为角点,将该点的灰度值和它邻域内16个像素点的灰度值进行比较,若圆圈上存在n个连续的像素点的灰度值大于P点的灰度值+t(t是阈值),则P点为角点,很多文献都推荐FAST-9,使用的比较多。

    FAST角点检测本身的缺点:

    1. 没有角点响应函数,并且具有很大的边缘响应
    2. FAST不产生多尺度特征。

    针对这些缺陷,ORB的解决方法是:

    (1)若想得到N个关键点, 先设置较低的阈值提取多于N个点, 再用Harris角点检测对其进行排序, 找到角点响应值较高的前N个FAST特征点,这也使特征点更能适应环境光照的变化。

    (2)对图像构造金字塔,然后在金字塔的每一层提取FAST特征的方法,改善ORB的尺度一致性问题。

    针对FAST检测算子没有方向性,定义一种邻域矩,并利用矩计算得到特征点邻域的灰度质心,以特征点中心到灰度质心的方向作为特征点的主方向。有了特征点的方向,也就实现了旋转不变性

    利用BRIEF描述子进行特征描述,在特征点附近随机选取若干点对,将这些点对的灰度值的大小,组合成一个二进制串,并将这个二进制串作为该特征点的特征描述子。

    通过比较特征点领域范围内像素点对的灰度值,赋予0或者1,BRIEF描述子比较的是随机点对的像素值,容易受噪声影响,在31*31像素区域内随机选取5*5的子窗口,比较窗口的灰度积分来替换点对的像素值。

    尺度不变性和旋转不变性就是在描述同一个特征之前,将两张图像都变换到同一个方向和同一个尺度上,然后在这个统一标准上描述这个特征。

    特征点的匹配

    根据特征向量间的汉明距离,在得到两幅图像分别的ORB描述子后,特征点的匹配方法通常有两种,一种是将汉明距离最小的两个特征点归为一对;另一种是计算最小汉明距离与次小汉明距离之比,这个比值越小越表明两个特征点相似。

    汉明距离:两个字母中不同位值的数目称为汉明距离。

    这两种方法都需要设置阈值来判定为合适的对应特征点,当阈值越小,越有可能得到置信度高的特征点,但是可能导致舍弃一些正确的特征匹配点。当阈值越大,得到的特征匹配点越多,但是其中可能掺杂一些错误的匹配点。

    Opencv中有两种匹配模式:暴力匹配,快速最近邻匹配。

    1.暴力匹配:测量描述子之间的距离(通常采用汉明距离和欧氏距离),汉明距离是采用BRIEF二进制描述子进行匹配,浮点型描述子用欧式距离匹配。

    2.暴力匹配产生的特征点较多,采用FLANN方法可以提高质量,降低计算量。

    使用特征提取过程得到的特征描述符(descriptor)数据类型有的是float类型的,比如说SurfDescriptorExtractor,SiftDescriptorExtractor,有的是uchar类型的,比如说有ORB,BriefDescriptorExtractor。对应float类型的匹配方式有:FlannBasedMatcher,BruteForce<L2<float>>,BruteForce<SL2<float>>,BruteForce<L1<float>>。对应uchar类型的匹配方式有:BruteForce<Hammin>,BruteForce<HammingLUT>。所以ORB和BRIEF特征描述子只能使用BruteForce匹配法。

    查阅论文的收获

    1. 通过透视变换实现图像矫正,避免因拍摄角度改变造成的识别准确率下降。
    2. 图像预处理时滤波方式采用高斯滤波降低噪声(有的是在灰度化之前,有的是在二值化之后)。
    3. 图像二值化时使用OTSU自动阈值分割法获得最佳阈值。
    4. 光源选择时,采用环形LED光源和前后照射方式。
    5. 用垂直投影法实现对字符分割。

    //ORB特征点检测算法
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <opencv2/features2d/features2d.hpp>
    #include <Windows.h>
    using namespace cv;
    using namespace std;

    Mat image1, image2;//中值滤波后的图像
    Mat extracte_orb(Mat input, vector<KeyPoint>& keypoint, Mat& descriptor) 
    {
        Ptr<ORB> f2d = ORB::create(300);
        f2d->detect(input, keypoint);
        Mat image_with_kp;
        f2d->compute(input, keypoint, descriptor);
        drawKeypoints(input, keypoint, image_with_kp, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
        
        return image_with_kp;
    }

    void match_two_image(Mat image1, Mat image2, vector<KeyPoint> keypoint1, vector<KeyPoint> keypoint2, Mat descriptor1, Mat descriptor2) 
    {
        //BFMatcher matcher(NORM_HAMMING);//汉明距离作为相似度度量
        FlannBasedMatcher matcher;//采用FLANN算法匹配描述符向量
        vector<DMatch> matches;
        matcher.match(descriptor1, descriptor2, matches);
        Mat good_matches_image;
        drawMatches(image1, keypoint1, image2, keypoint2,
            matches, good_matches_image, Scalar::all(-1), Scalar::all(-1),
            vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
        sort(matches.begin(), matches.end());     //筛选匹配点,根据match里面特征对的距离从小到大排序
        int Prc = min(100, (int)(matches.size() * 0.15));
        cout << "初始匹配点数为" << Prc << endl;
        imshow("匹配效果图", good_matches_image);
        {
            vector <KeyPoint> RAN_KP1, RAN_KP2;
            vector<Point2f> keypoints1, keypoints2;
            for (int i = 0; i < matches.size(); i++) 
            {
                keypoints1.push_back(keypoint1[matches[i].queryIdx].pt);
                keypoints2.push_back(keypoint2[matches[i].trainIdx].pt);
                RAN_KP1.push_back(keypoint1[matches[i].queryIdx]);
                RAN_KP2.push_back(keypoint2[matches[i].trainIdx]);
            }

            vector<uchar> RansacStatus;
            findFundamentalMat(keypoints1, keypoints2, RansacStatus, FM_RANSAC);
            vector <KeyPoint> ransac_keypoints1, ransac_keypoints2;
            vector <DMatch> ransac_matches;
            int index = 0;
            for (size_t i = 0; i < matches.size(); i++)
            {
                if (RansacStatus[i] != 0)
                {
                    ransac_keypoints1.push_back(RAN_KP1[i]);
                    ransac_keypoints2.push_back(RAN_KP2[i]);
                    matches[i].queryIdx = index;
                    matches[i].trainIdx = index;
                    ransac_matches.push_back(matches[i]);
                    index++;
                }
            }

            sort(ransac_matches.begin(), ransac_matches.end());     //筛选匹配点,根据match里面特征对的距离从小到大排序
            int Pairs = min(100, (int)(ransac_matches.size()));
            cout << "ransac匹配点数为" << Pairs << endl;
            Mat after_ransac_sift_match;
            drawMatches(image1, ransac_keypoints1, image2, ransac_keypoints2,
                ransac_matches, after_ransac_sift_match, Scalar::all(-1), Scalar::all(-1),
                vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
            imshow("ransac后匹配效果", after_ransac_sift_match);
        
        }
    }
    //计算信噪比
    double PSNR(const Mat &inImg, const Mat &referenceImg)
    {
        if (inImg.rows != referenceImg.rows || inImg.cols != referenceImg.cols || inImg.channels() != referenceImg.channels())
            return -1000000.0;

        //转换类型
        Mat dImg, dRImg;
        int imgType;
        if (3 == inImg.channels())
            imgType = CV_64FC3;
        else
            imgType = CV_64FC1;

        inImg.convertTo(dImg, imgType);
        referenceImg.convertTo(dRImg, imgType);

        //计算均值
        double *dataImg = dImg.ptr<double>(0);
        double *dataRImg = dRImg.ptr<double>(0);
        for (int idr = 0; idr<inImg.rows; idr++)
        {
            for (int idc = 0; idc<inImg.cols*inImg.channels(); idc++, dataImg++, dataRImg++)
            {
                double difValue = *dataImg - *dataRImg;
                *dataImg = difValue *difValue;
            }
        }
        //计算插值均值
        Scalar meanValue = cv::mean(dImg);
        double mValue;
        if (3 == inImg.channels())
            mValue = (meanValue[0] + meanValue[2] + meanValue[2]) / 3.0;
        else
            mValue = meanValue[0];

        //计算PSNR峰值信噪比
        double val = 255.0;
        double reValue = 10.0*log10((val*val) / mValue);
        return reValue;
    }

    int main(int argc, char* argv)
    {
        Mat grayImage1,grayImage2,img1,img2;

        Mat srcImage1 = imread("quexian1.jpg");
        Mat srcImage2 = imread("good.jpg");
        //转为灰度图
        cvtColor(srcImage1, grayImage1, COLOR_BGR2GRAY);
        cvtColor(srcImage2, grayImage2, COLOR_BGR2GRAY);
        //进行直方图均衡化
        equalizeHist(grayImage1, img1);
        equalizeHist(grayImage2, img2);
        imshow("经直方图均衡化后的图1", img1);
        imshow("经直方图均衡化后的图2", img2);
        //中值滤波
        medianBlur(img1, image1, 3);
        medianBlur(img2, image2, 3);
        
        vector<KeyPoint> keypoint1, keypoint2;
        Mat descriptor1, descriptor2;
        double start = GetTickCount();//记录起始时间
        Mat orb_image1=extracte_orb(image1, keypoint1, descriptor1);
        imshow("orb识别效果图1" , orb_image1);
        cout << "orb识别特征点数1: " << keypoint1.size() << endl;
        Mat orb_image2=extracte_orb(image2, keypoint2, descriptor2);
        imshow("orb识别效果图2", orb_image2);
        cout << "orb识别特征点数2: " << keypoint2.size() << endl;
        match_two_image(image1, image2, keypoint1, keypoint2, descriptor1, descriptor2);
        double end = GetTickCount();
        cout << "orb耗时" << (end - start) << "ms" << endl;
        
        waitKey(0);
        return 0;
    }

     

    展开全文
  • 双目视觉利用两幅图像进行特征点匹配,生成三维点云,完成三维重建
  • sift特征点提取代码和对两幅图像提取特征点后进行特征点匹配,可用于图像拼接等。c和matlab代码都有
  • 针对此提出一种基于多视角特征点匹配的方法进行室外目标定位.该方法首先获取图像的SURF特征进行匹配,然后结合FLANN和KNN算法滤除错误匹配点,有效地提升了匹配质量,节省了运算时间.通过对不同视角下景物模板的匹配...
  • 提出一种改进的SIFT特征点匹配算法.以提高图像特征点匹配算法效率为目的,研究了SIFT特征点描述子基于欧氏最小距离测度的匹配算法.由于SIFT特征点检测算法检测到的特征点数量较大,且每个特征点描述子都是128维的...
  • 图结构在航空遥感图像特征点匹配中的应用
  • 基于特征点匹配的视频稳像,opencv。有C++和python代码。 这个文件夹里面的代码有更新,详细见https://blog.csdn.net/luohenyj/article/details/88355444
  • Sift特征点匹配过程

    千次阅读 2017-07-06 17:09:28
    现在来看看特征点匹配特征点匹配的一个应用就是物体的识别,比如说我有2张图片A和B,图片的内容相同,只是图片的大小尺寸不同。假设A图片尺寸比较大,且我们已经采用sift算法对图片A和B都进行了检测,获得了它们的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,065
精华内容 2,426
关键字:

特征点匹配