Opencv中使用Surf特征实现图像配准及对透视变换矩阵H的平移修正
图像配准需要将一张测试图片按照第二张基准图片的尺寸、角度等形态信息进行透视(仿射)变换匹配,本例通过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的平移修正的更多相关文章
- Opencv Sift和Surf特征实现图像无缝拼接生成全景图像
Sift和Surf算法实现两幅图像拼接的过程是一样的,主要分为4大部分: 1. 特征点提取和描述 2. 特征点配对,找到两幅图像中匹配点的位置 3. 通过配对点,生成变换矩阵,并对图像1应用变换矩阵生 ...
- 基于SURF特征的图像与视频拼接技术的研究和实现(一)
基于SURF特征的图像与视频拼接技术的研究和实现(一) 一直有计划研究实时图像拼接,但是直到最近拜读西电2013年张亚娟的<基于SURF特征的图像与视频拼接技术的研究和实现>,条 ...
- OpenCV中IplImage图像格式与BYTE图像数据的转换
最近在将Karlsruhe Institute of Technology的Andreas Geiger发表在ACCV2010上的Efficent Large-Scale Stereo Matchin ...
- OpenCV中的SURF算法介绍
SURF:speed up robust feature,翻译为快速鲁棒特征.首先就其中涉及到的特征点和描述符做一些简单的介绍: 特征点和描述符 特征点分为两类:狭义特征点和广义特征点.狭义特征点的位 ...
- 【OpenCV新手教程之十八】OpenCV仿射变换 & SURF特征点描写叙述合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨) ...
- OpenCV中基于Haar特征和级联分类器的人脸检测
使用机器学习的方法进行人脸检测的第一步需要训练人脸分类器,这是一个耗时耗力的过程,需要收集大量的正负样本,并且样本质量的好坏对结果影响巨大,如果样本没有处理好,再优秀的机器学习分类算法都是零. 今年3 ...
- OpenCV中基于HOG特征的行人检测
目前基于机器学习方法的行人检测的主流特征描述子之一是HOG(Histogram of Oriented Gradient, 方向梯度直方图).HOG特征是用于目标检测的特征描述子,它通过计算和统计图像 ...
- [OpenCV-Python] OpenCV 中图像特征提取与描述 部分 V (一)
部分 V图像特征提取与描述 OpenCV-Python 中文教程(搬运)目录 29 理解图像特征 目标本节我会试着帮你理解什么是图像特征,为什么图像特征很重要,为什么角点很重要等.29.1 解释 我相 ...
- OpenCV教程(47) sift特征和surf特征
在前面三篇教程中的几种角检测方法,比如harris角检测,都是旋转无关的,即使我们转动图像,依然能检测出角的位置,但是图像缩放后,harris角检测可能会失效,比如下面的图像,图像放大之前可 ...
随机推荐
- 41.关于Intellij IDEA菜单项中Compile、Make和Build的区别
转自:https://www.cnblogs.com/ini_always/archive/2011/10/23/2221985.html Compile.Make和Build的区别 针对Java ...
- Redis笔记教程
一.redis简介 1.1.1.什么是redis? REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. 读 ...
- Weblogic问题汇总
1. weblogic unable to get file lock问题 在项目使用过程中,非正常结束Weblogic进程导致Weblogic无法启动,出现以下错误: <BEA-141281& ...
- HTML中input标签maxlength属性的妙处
HTML中的input标签可是很常用的. HTML本身也非常简单,就是若干标签,每个标签有若干属性. 我在学习HTML的过程中,也没有太过重视. 今年,在写前端表单验证的时候,发现maxlength这 ...
- winedt (latex 编译器)解决中文的问题(CJK & CTEX)
主要是导入相关的库支持: 1. CJK \usepackage{CJK}:CJK,是中日韩的英文首字母的组合,处理中文需要先导入这个包: \begin{CJK*}{GBK}{song}:默认句式,表示 ...
- delete noprompt archivelog 报错ORA-00245,RMAN-08132
在RMAN执行 delete noprompt archivelog until time 'sysdate-1'; 报错 ORA-00245: control file backup fai ...
- JS实现动画方向切换效果(包括:撞墙反弹,暂停继续左右运动等)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- HttpWatch--time chart分析
这是一个IE的插件,下载可以点这里.下载后解压如下图所示,一共有4个文件.HttpWatch Professional是单独软件,可以单独使用. 解压后有四个文件 插件安装时,只需运行httpwatc ...
- jQuery常用的API
1.jQuery给标签添加子元素(父子关系) jQuery对象.append("子"); 将div标签插入到ul标签之后 $("ul").append($('d ...
- SVGALib
SVGALib是一套运行于Linux及FreeBSD下的开放源代码低阶绘图函式库,它允许程式设计人员变更视讯模式及全屏幕图像,许多热门的电脑游戏如Quake及Doom都源自此技术. 范例 编辑 #in ...