图像配准需要将一张测试图片按照第二张基准图片的尺寸、角度等形态信息进行透视(仿射)变换匹配,本例通过Surf特征的定位和匹配实现图像配准。

配准流程:

  • 1. 提取两幅图像的Surf特征
  • 2. 对Surf特征进行匹配,找到最匹配的特征点对
  • 3. 提取最优配对点的坐标,生成透视变换矩阵
  • 4. 对测试图像经过透视变换,生成配准图像

以下是Opencv代码实现:

#include "highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream> using namespace cv;
using namespace std; int main(int argc,char *argv[])
{
Mat image01=imread(argv[1]);
Mat image02=imread(argv[2]);
imshow("原始测试图像",image01);
imshow("基准图像",image02); //灰度图转换
Mat image1,image2;
cvtColor(image01,image1,CV_RGB2GRAY);
cvtColor(image02,image2,CV_RGB2GRAY); //提取特征点
SurfFeatureDetector surfDetector(800); // 海塞矩阵阈值
vector<KeyPoint> keyPoint1,keyPoint2;
surfDetector.detect(image1,keyPoint1);
surfDetector.detect(image2,keyPoint2); //特征点描述,为下边的特征点匹配做准备
SurfDescriptorExtractor SurfDescriptor;
Mat imageDesc1,imageDesc2;
SurfDescriptor.compute(image1,keyPoint1,imageDesc1);
SurfDescriptor.compute(image2,keyPoint2,imageDesc2); //获得匹配特征点,并提取最优配对
FlannBasedMatcher matcher;
vector<DMatch> matchePoints;
matcher.match(imageDesc1,imageDesc2,matchePoints,Mat());
sort(matchePoints.begin(),matchePoints.end()); //特征点排序 //获取排在前N个的最优匹配特征点
vector<Point2f> imagePoints1,imagePoints2;
for(int i=0;i<10;i++)
{
imagePoints1.push_back(keyPoint1[matchePoints[i].queryIdx].pt);
imagePoints2.push_back(keyPoint2[matchePoints[i].trainIdx].pt);
} //获取图像1到图像2的投影映射矩阵 尺寸为3*3
Mat homo=findHomography(imagePoints1,imagePoints2,CV_RANSAC);
////也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差
//Mat homo=getPerspectiveTransform(imagePoints1,imagePoints2);
cout<<"变换矩阵为:\n"<<homo<<endl<<endl; //输出映射矩阵
//图像配准
Mat imageTransform1,imageTransform2;
warpPerspective(image01,imageTransform1,homo,Size(image02.cols,image02.rows));
imshow("经过透视矩阵变换后",imageTransform1); waitKey();
return 0;
}

测试图像:

配准基准(目标)图像:

配准效果:

可以看到,使用Surf特征自动配准的效果还算可以,也不需要手动选取特征点,但是有点小遗憾是配准后原图的部分图像缺失了,如原始图像的左上角和左下角区域。

怎么解释这个现象呢?其实是因为原图像缺失部分经过透视矩阵变换后在新图像中位置坐标变成负的了,导致这部分直接被截取掉了。

拿原始图像中的坐标为左上角的点(x0,y0)=(1,1)为例,本例中生成的透视变换矩阵H为:

经过矩阵H变换后目标图像的对应坐标为X(x,y):

所以变换后的目标点X(x,y)=(135/-4,-54/-4)= (-34,13) ,即x的坐标等于-34,小于零,所以直接被截取干掉了。

解决办法之一可以通过修正变换矩阵,对其加一个水平或垂直方向上的平移分量,保证变换后的目标坐标有一个偏移,偏移后的坐标大于0即可。其中H矩阵的第一行数据影响水平(x)方向的偏移,第二行数据影响垂直(y)方向的偏移。所以水平偏移需要修正H矩阵的第一行数据,垂直偏移需要修正H矩阵的第二行数据。

H矩阵数值的调整可以通过用一个3*3的矩阵相乘实现,水平方向的修正矩阵:

Mat adjustMat=(Mat_<double>(3,3)<<1.0,0,adjustValue,0,1.0,0,0,0,1.0);


垂直方向上的修正矩阵:

Mat adjustMat=(Mat_<double>(3,3)<<1.0,0,0,0,1.0,adjustValue,0,0,1.0);


水平和垂直两个方向上的修正矩阵:

Mat adjustMat=(Mat_<double>(3,3)<<1.0,0,adjustValue1,0,1.0,adjustValue2,0,0,1.0);

上边的修正(平移)系数adjustValue数值越大,在目标图像上坐标的偏移也就越大,大于零的adjustValue对应正方向上的平移,小于零的adjustValue对应负方向的平移。

以下是经过adjustMat矩阵修正过的H矩阵数据变化对比:

经过水平和垂直方向的平移修正之后的透视效果,两个角落区域的内容都可以显示出来了:

以下是对透视变换矩阵平移修正的完整代码,有兴趣可以参考试一试。

#include "highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream> using namespace cv;
using namespace std; int main(int argc,char *argv[])
{
Mat image01=imread(argv[1]);
Mat image02=imread(argv[2]);
imshow("原始测试图像",image01);
imshow("基准图像",image02); //灰度图转换
Mat image1,image2;
cvtColor(image01,image1,CV_RGB2GRAY);
cvtColor(image02,image2,CV_RGB2GRAY); //提取特征点
SurfFeatureDetector surfDetector(800); // 海塞矩阵阈值
vector<KeyPoint> keyPoint1,keyPoint2;
surfDetector.detect(image1,keyPoint1);
surfDetector.detect(image2,keyPoint2); //特征点描述,为下边的特征点匹配做准备
SurfDescriptorExtractor SurfDescriptor;
Mat imageDesc1,imageDesc2;
SurfDescriptor.compute(image1,keyPoint1,imageDesc1);
SurfDescriptor.compute(image2,keyPoint2,imageDesc2); //获得匹配特征点,并提取最优配对
FlannBasedMatcher matcher;
vector<DMatch> matchePoints;
matcher.match(imageDesc1,imageDesc2,matchePoints,Mat());
sort(matchePoints.begin(),matchePoints.end()); //特征点排序 //获取排在前N个的最优匹配特征点
vector<Point2f> imagePoints1,imagePoints2; for(int i=0;i<10;i++)
{
imagePoints1.push_back(keyPoint1[matchePoints[i].queryIdx].pt);
imagePoints2.push_back(keyPoint2[matchePoints[i].trainIdx].pt);
} //获取图像1到图像2的投影映射矩阵 尺寸为3*3
Mat homo=findHomography(imagePoints1,imagePoints2,CV_RANSAC);
////也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差
//Mat homo=getPerspectiveTransform(imagePoints1,imagePoints2);
cout<<"变换矩阵为:\n"<<homo<<endl<<endl; //输出映射矩阵
double adjustValue=image1.cols;
Mat adjustMat=(Mat_<double>(3,3)<<1.0,0,35,0,1.0,65,0,0,1.0);
cout<<"调整矩阵为:\n"<<adjustMat<<endl<<endl;
cout<<"调整后变换矩阵为:\n"<<adjustMat*homo<<endl; //图像配准
Mat imageTransform1,imageTransform2;
warpPerspective(image01,imageTransform1,homo,Size(image02.cols,image02.rows));
warpPerspective(image01,imageTransform2,adjustMat*homo,Size(image02.cols*1.3,image02.rows*1.8));
imshow("直接经过透视矩阵变换",imageTransform1);
imshow("经过平移修正后的透视矩阵变换",imageTransform2); waitKey();
return 0;
}

顺便贴一下特征点的匹配效果:

Opencv中使用Surf特征实现图像配准及对透视变换矩阵H的平移修正的更多相关文章

  1. Opencv Sift和Surf特征实现图像无缝拼接生成全景图像

    Sift和Surf算法实现两幅图像拼接的过程是一样的,主要分为4大部分: 1. 特征点提取和描述 2. 特征点配对,找到两幅图像中匹配点的位置 3. 通过配对点,生成变换矩阵,并对图像1应用变换矩阵生 ...

  2. 基于SURF特征的图像与视频拼接技术的研究和实现(一)

    基于SURF特征的图像与视频拼接技术的研究和实现(一)      一直有计划研究实时图像拼接,但是直到最近拜读西电2013年张亚娟的<基于SURF特征的图像与视频拼接技术的研究和实现>,条 ...

  3. OpenCV中IplImage图像格式与BYTE图像数据的转换

    最近在将Karlsruhe Institute of Technology的Andreas Geiger发表在ACCV2010上的Efficent Large-Scale Stereo Matchin ...

  4. OpenCV中的SURF算法介绍

    SURF:speed up robust feature,翻译为快速鲁棒特征.首先就其中涉及到的特征点和描述符做一些简单的介绍: 特征点和描述符 特征点分为两类:狭义特征点和广义特征点.狭义特征点的位 ...

  5. 【OpenCV新手教程之十八】OpenCV仿射变换 &amp; SURF特征点描写叙述合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)  ...

  6. OpenCV中基于Haar特征和级联分类器的人脸检测

    使用机器学习的方法进行人脸检测的第一步需要训练人脸分类器,这是一个耗时耗力的过程,需要收集大量的正负样本,并且样本质量的好坏对结果影响巨大,如果样本没有处理好,再优秀的机器学习分类算法都是零. 今年3 ...

  7. OpenCV中基于HOG特征的行人检测

    目前基于机器学习方法的行人检测的主流特征描述子之一是HOG(Histogram of Oriented Gradient, 方向梯度直方图).HOG特征是用于目标检测的特征描述子,它通过计算和统计图像 ...

  8. [OpenCV-Python] OpenCV 中图像特征提取与描述 部分 V (一)

    部分 V图像特征提取与描述 OpenCV-Python 中文教程(搬运)目录 29 理解图像特征 目标本节我会试着帮你理解什么是图像特征,为什么图像特征很重要,为什么角点很重要等.29.1 解释 我相 ...

  9. OpenCV教程(47) sift特征和surf特征

         在前面三篇教程中的几种角检测方法,比如harris角检测,都是旋转无关的,即使我们转动图像,依然能检测出角的位置,但是图像缩放后,harris角检测可能会失效,比如下面的图像,图像放大之前可 ...

随机推荐

  1. ontouch、dispatchtouchevent、interceptouchevent-相关事件

    这几天一直在研究onTouch的相关方法,今天我们就来看看onTouchEvent.dispatchTouchEvent.onIntercepTouchEvent这三个方法在控件之间的传递顺序 pub ...

  2. vanzo-代码共享平台地址

    网页编辑.烧录代码 1.登录服务器 192.168.1.52 2.选择modules 3.选择builder 4.在 Project Name:填入要拉的项目名 选择版本:user,eng,userd ...

  3. amazeui学习笔记二(进阶开发2)--Web组件简介Web Component

    amazeui学习笔记二(进阶开发2)--Web组件简介Web Component 一.总结 1.amaze ui:amaze ui是一个web 组件, 由模板(hbs).样式(LESS).交互(JS ...

  4. 微信支付v2开发(9) 标记客户投诉处理状态

    本文介绍微信支付中如何标记客户投诉的处理状态. 一.API Api 的 url 为: https://api.weixin.qq.com/payfeedback/update?access_token ...

  5. Want to archive tables? Use Percona Toolkit’s pt-archiver--转载

    原文地址:https://www.percona.com/blog/2013/08/12/want-to-archive-tables-use-pt-archiver/ Percona Toolkit ...

  6. PythonOOP面向对象编程3

    override 函数重写 重写是在自定义的类内添加相应的方法,让自定义的类生成的对象(实例)像内建对象一样进行内建的函数操作 对象转字符串函数重写 repr(obj) 返回一个能代表此对象的表达式字 ...

  7. set_fix_multiple_port_nets

    set_fix_multiple_port_nets   -all    -buffer_constants 加上这个命令之后 综合之后的网表就不会出现assign语句 否则会出现

  8. 【Educational Codeforces Round 31 A】Book Reading

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 水模拟 [代码] #include <bits/stdc++.h> using namespace std; const ...

  9. 【Eclipse提高开发速度-插件篇】Checkstyle的使用

    1.CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发者遵守某些编码规范的工具. CheckStyle提供了大部分功能都是对于代码规范的检查 CheckStyle检验 ...

  10. mootools常用特性和示例(基础篇1)

    网上关于mootools这个库的信息很少. 公司一些老的项目用到了mootools库,因为要维护,所以接触到了mootools. mootools(文档)官网:http://www.chinamoot ...