1.概述

前面介绍模板匹配的时候已经提到模板匹配时一种基于灰度的匹配方法,而基于特征的匹配方法有FAST、SIFT、SURF等,上面两篇文章已经介绍过使用Surf算法进行特征点检测以及使用暴力匹配(BruteForceMatcher)和最近邻匹配(FLANN)两种匹配方法。接下来将更深一步介绍利用Surf检测到的特征点以及匹配对进行图像匹配.
利用Surf算法进行图像匹配其一般流程为:检测物体特征点->计算特征点描述子->使用BurteForceMatcher或FLANN进行特征点匹配->匹配到的特征点进行透视变换findHomography()->透视矩阵变换perspectiveTransform()->绘制匹配物体轮廓

2. OpenCV API

透视变换
findHomography()
这个函数的作用是在图像原平面和目标图像平面之间寻找并返回一个透视变换矩阵H,如下:

所以反向投影误差

最小化。如果参数metchod被设置为默认值0,改函数使用所有的点以简单的最小二乘法计算一个初始的单应估计。可以简单理解为透视变换矩阵就是把一幅图像从一个空间变换到另一个空间所需要进行旋转平移而进行加权的矩阵。
但是并不是所有的点匹配对(srcPoints_i, dstPoints_i)都适合使用刚性的透视变换(有些异常值),这样变换得到的透视矩阵误差较大。在这种情况下可以使用两种鲁棒的方法RANSAC和LMeDS,尝试使用很多对应匹配点的随机子集,使用该子集和最简单的最小二乘法估计单应性矩阵,然后计算得到的透视变换矩阵的质量/好坏(quality/goodness),然后使用最佳子集来计算单应性矩阵的初始估计矩阵和内在值/异常值(inliers/outliers)的掩码。
不管方法是否具有鲁棒性,使用Levenberg-Marquardt方法进一步精确计算单应性矩阵(如果方法具有鲁棒性仅使用内在值(inline))来减少再投影误差。
RANSAC几乎可以处理任意比例的异常值,但是它需要一个阈值来区分内在值还是异常值。LMeDS不需要任何阈值,但是只有在内在值比例大于50%的情况下才能准确计算。如果没有太多异常值,噪声有比较小的情况下使用默认方法即可
函数用来查找初始的内在和外在矩阵,透视变换矩阵确定了一个比例.无论何时如果不能估计H矩阵则函数将返回一个空矩阵

Mat cv::findHomography  (   InputArray  srcPoints,InputArray  dstPoints,int     method = 0,double  ransacReprojThreshold = 3,OutputArray     mask = noArray(),const int   maxIters = 2000,const double    confidence = 0.995 )

srcPoints:原平面对应点,可以是CV_32FC2或vector类型的矩阵。
dstPoints:目标平面对应点,可以是CV_32FC2或vector类型的矩阵
method:用于计算矩阵的方法,可选方法上面已经介绍过有默认值0,CV_RANSAC和CV_LMEDS
ransacReprojThreshold:有默认值3,区分内在值还是异常值的阈值点,只在RANSAC方法有用,当||dstPoints-convertPointsHomogeneous(H*srcPoints)||>ransacReprojThreshold,这个点就会被认为是异常值(outlier).如果srcPoints和dstPoints是以像素为单位,则参数的取值范围一般在1-10之间。
mask:可选参数,有默认值noArray(),通过鲁棒性方法(RANSAC或LMEDS)设置输出掩码。
函数另一种定义形式如下:

Mat cv::findHomography  (   InputArray  srcPoints,InputArray  dstPoints,OutputArray     mask,int     method = 0,double  ransacReprojThreshold = 3 )

求得的透视矩阵是一个3x3的变换矩阵。

perspectiveTransform()
函数能够进行向量透视矩阵变换。

void cv::perspectiveTransform   (   InputArray  src,OutputArray     dst,InputArray  m )

src:双通道或三通道浮点型原图像或数组,每个元素都是二维或三维可被转换的向量。
dst:目标数组或图像,与原图像有相同的尺寸和类型
m:变换矩阵,为3x3或4x4的浮点型矩阵

3.示例代码

#include <iostream>
#include <stdio.h>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\nonfree\features2d.hpp>
#include <opencv2\calib3d\calib3d.hpp>using namespace std;
using namespace cv;int main()
{Mat image_object = imread("match_src.jpg", IMREAD_GRAYSCALE);Mat image_scene = imread("match_dst.jpg", IMREAD_GRAYSCALE);//判断图像是否加载成功if (image_object.empty() || image_scene.empty()){cout << "图像加载失败";return -1;}elsecout << "图像加载成功..." << endl << endl;//检测特征点const int minHessian = 700;SurfFeatureDetector detector(minHessian);vector<KeyPoint>keypoints_object, keypoints_scene;detector.detect(image_object, keypoints_object);detector.detect(image_scene, keypoints_scene);//计算特征点描述子SurfDescriptorExtractor extractor;Mat descriptors_object, descriptors_scene;extractor.compute(image_object, keypoints_object, descriptors_object);extractor.compute(image_scene, keypoints_scene, descriptors_scene);//使用FLANN进行特征点匹配FlannBasedMatcher matcher;vector<DMatch>matches;matcher.match(descriptors_object, descriptors_scene, matches);//计算匹配点之间最大和最小距离double max_dist = 0;double min_dist = 100;for (int i = 0; i < descriptors_object.rows; i++){double dist = matches[i].distance;if (dist < min_dist){min_dist = dist;}else if (dist > max_dist){max_dist = dist;}}printf("Max dist: %f \n", max_dist);printf("Min dist: %f \n", min_dist);//绘制“好”的匹配点vector<DMatch>good_matches;for (int i = 0; i < descriptors_object.rows; i++){if (matches[i].distance<2*min_dist){good_matches.push_back(matches[i]);}}Mat image_matches;drawMatches(image_object, keypoints_object, image_scene, keypoints_scene, good_matches, image_matches,Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//定位“好”的匹配点vector<Point2f> obj;vector<Point2f> scene;for (int i = 0; i < good_matches.size(); i++){//DMathch类型中queryIdx是指match中第一个数组的索引,keyPoint类型中pt指的是当前点坐标obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);}Mat H = findHomography(obj, scene, CV_RANSAC);vector<Point2f> obj_corners(4), scene_corners(4);obj_corners[0] = cvPoint(0, 0);obj_corners[1] = cvPoint(image_object.cols, 0);obj_corners[2] = cvPoint(image_object.cols, image_object.rows);obj_corners[3] = cvPoint(0, image_object.rows);perspectiveTransform(obj_corners, scene_corners, H);//绘制角点之间的直线line(image_matches, scene_corners[0] + Point2f(image_object.cols, 0),scene_corners[1] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);line(image_matches, scene_corners[1] + Point2f(image_object.cols, 0),scene_corners[2] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);line(image_matches, scene_corners[2] + Point2f(image_object.cols, 0),scene_corners[3] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);line(image_matches, scene_corners[3] + Point2f(image_object.cols, 0),scene_corners[0] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);//输出图像namedWindow("匹配图像", WINDOW_AUTOSIZE);imshow("匹配图像", image_matches);waitKey(0);return 0;
}

程序说明
在定位匹配点中用到了DMatch的queryIdx、trainIdx成员变量和keyPoint的成员变量pt,做个说明:
DMatch有三个构造函数,其中一组如下:

cv::DMatch::DMatch  (   int     _queryIdx,  //在对描述子匹配时,第一组特征点的索引int     _trainIdx,  //在对描述子匹配时,第二组特征点的索引int     _imgIdx,    //多个图像中图像的索引float   _distance   //两个特征向量间的欧氏距离,越小表明匹配度越高)

对于keyPoint类有两种构造形式如下:

cv::KeyPoint::KeyPoint  (   Point2f     _pt,float   _size,float   _angle = -1,float   _response = 0,int     _octave = 0,int     _class_id = -1 )
cv::KeyPoint::KeyPoint  (   float   x,float   y,float   _size,float   _angle = -1,float   _response = 0,int     _octave = 0,int     _class_id = -1 )

两种形式在本质上是一样的,只是第一种形式中的特征点坐标pt在第二种形式中以x和y的形式给出。
pt关键点坐标
size是关键点邻域直径
angle特征点方向,范围为[0,360),负值表示不使用
response关键点检测器对于关键点的响应程度
octave关键点位于图像金字塔的层
class_id用于聚类的id

运行结果

opencv之SURF图像匹配相关推荐

  1. OpenCV仿射变换 SURF特征点描述合辑

    原文:http://blog.csdn.net/poem_qianmo/article/details/33320997?utm_source=tuicool&utm_medium=refer ...

  2. pythonopencv算法_python opencv之SURF算法示例

    本文介绍了python opencv之SURF算法示例,分享给大家,具体如下: 目标: SURF算法基础 opencv总SURF算法的使用 原理: 上节课使用了SIFT算法,当时这种算法效率不高,需要 ...

  3. python opencv入门 SURF算法(34)

    内容来自OpenCV-Python Tutorials 自己翻译整理 目标: SURF算法基础 opencv总SURF算法的使用 原理: 上节课使用了SIFT算法,当时这种算法效率不高,需要更快速的算 ...

  4. opencv之SURF算法原理及关键点检测

    1.概述 在基础篇里面讲模板匹配的时候已经介绍过,图像匹配主要有基于灰度和基于特征两种方法.基于特征匹配的方法有很多种如:FAST.HARRIS.SIFT.SURF.SUSAN等.其中SIFT算法由D ...

  5. 【opencv】(7) 图像匹配、直方图、图像均衡化

    各位同学好,今天和大家分享一下opencv中图像匹配方法,和图像均衡化方法 (1)模板匹配: cv2.matchTemplate():(2)图像直方图: cv2.calcHist():(3)图像均衡化 ...

  6. OpenCV | OpenCV:sift,SURF 特征提取

    一.环境准备 目前 Opencv 有2.x 和 3.x 版本,两个版本之间的差异主要是一些功能函数被放置到了不同的功能模块,因此大多数情况两个版本的代码并不能通用.建议安装 Anaconda,自行下载 ...

  7. OpenCV:SURF算法浅析

    引子: 课题需要SURF特征提取算法,在运动中提取摄像头图像中的特征点,并进行跟踪匹配,以此估计运动状态.开始找到了SIFT算法,SIFT特征提取具有极强的适应能力,但运算量稍大,后来就有了SURF特 ...

  8. surf和sift算法被申请专利后部分opencv版本无法使用后的安装pycharm+opencv使用surf和sift算法教程

    安装pycharm+opencv教程使用surf和sift算法 surf和sift算法在pycharm中的问题 安装过程 使用的软件版本 安装步骤 一.pycharm的安装 二.尝试打开pycharm ...

  9. 【OpenCV】SURF图像拼接和Stitcher拼接

    介绍两种图像拼接的方法,一种是SURF算法的图像拼接,另一种是Stitcher硬拼接 首先先从简单的讲起 一.Stitcher直接拼接 可以实现多张图片一起拼接,只要两行代码就可以实现拼接: 1.首先 ...

最新文章

  1. 前沿研究:21世纪工程领域的重大挑战 | 中国工程院院刊
  2. 组成关系映射(注解)
  3. 机器学习笔记(七)贝叶斯分类器
  4. 简单多进程任务处理程序
  5. python链接hbase模块_HBase实战(1):使用Python连接Hbase数据库
  6. 《dp补卡——多重背包》
  7. 第一百零一期:如何处理ASP .NET Core中的cookie?
  8. [MFC]关于Visual studio 2012的AfxGetMainWnd
  9. yii CComponent组件 实例说明1
  10. JFinal实现原理
  11. 计算机操作系统的功能有哪些,操作系统的基本功能是什么
  12. 流程图怎么画?绘制一个流程图的简单操作方法
  13. 通过Grafana进行监控报警
  14. linear regreesion 线性回归
  15. 98家央企及下属上市企业全名单(2023版)
  16. uni-app离线打包遇到的坑
  17. Duplicate entry ‘XXX‘ for key ‘XXX.PRIMARY‘解决方案。
  18. The Rust Programming Language - 第13章 Rust语言中的函数式语言功能:迭代器与闭包 - 13.1 可以捕获其环境的匿名函数
  19. GTSAM中ISAM2和IncrementalFixedLagSmoother说明
  20. 线性代数的本质--笔记整理

热门文章

  1. dac0832控制电机驱动流程图_dac0832锯齿波流程图
  2. jquery获取父元素及祖先元素
  3. 故宫景点功课16:内廷西路(上)
  4. Java中集合ArrayList、Map的使用
  5. C#三大特性的理解和使用
  6. 常用的请求报头和响应报头
  7. 第二单元电梯作业总结
  8. 信号与系统 卷积积分结合律证明
  9. 一分钟了解阿里云产品:云监控
  10. 灵活用工平台的税务如何解决?