2、特征提取与匹配

OpenCV中关于SURF算法的部分,常常涉及到的是SURF、SurfFeatureDetector、SurfDescriptorExtractor这三个类;

features2d.hpp头文件中,有:typedef SURF SurfFeatureDetector;和typedef SURF SurfDescriptorExtractor;typedef声明是为现有类型创建一个新的名字,类型别名,即SURF类有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。

也就是说,SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。

因此包含必要的头文件:

#include<iostream>

#include<stdio.h>

#include"highgui/highgui.hpp"

#include"opencv2/nonfree/nonfree.hpp"

#include"opencv2/legacy/legacy.hpp"

usingnamespace cv;

usingnamespace std;

2.1 拼接图像的加载:

实例:

MatimageRgbLeft = imread("img_left_1.JPG");

MatimageRgbRight = imread("img_right_1.JPG");

//灰度图转换,在特征提取与匹配模块使用灰度图像,因此需要将图像转为灰度图

MatimageGrayLeft, imageGrayRight;

cvtColor(imageRgbLeft,imageGrayLeft, CV_RGB2GRAY);

cvtColor(imageRgbRight,imageGrayRight, CV_RGB2GRAY);

2.2 提取特征点   

实例:

intminHessian = 800; //设定阈值minHessian,关系到最后提取的特征点,这个是surf算法的一个参数;

SurfFeatureDetectorsurfDetector(minHessian);   // 海塞矩阵阈值

vector<KeyPoint>keyPointLeft, keyPointRight;// 定义两个KeyPoint 向keypoints_object, keypoints_scene来存放检测出来的特征点;

surfDetector.detect(imageGrayLeft,keyPointLeft);

surfDetector.detect(imageGrayRight,keyPointRight);

2.3 drawKeypoints()

作用:绘制关键点。

形式:void drawKeypoints(const Mat&image, constvector<KeyPoint>& keypoints, Mat& outImage, constScalar&color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT );

参数:

image:const Mat&类型的src,输入图像;

keypoints:const vector<KeyPoint>&类型的keypoints,根据源图像得到的特征点,它是一个输出参数;

outImage:Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符falgs;

color:const Scalar&类型的color,关键点的颜色,有默认值Scalar::all(-1);

flags:int类型的flags,绘制关键点是的特征标识符,有默认值DrawMatchesFlags::DEFAULT;

实例:

MatimageLeftPoint, imageRightPoint;

drawKeypoints(imageRgbLeft,keyPointLeft, imageLeftPoint, Scalar::all(-1), DrawMatchesFlags::DEFAULT);

imshow("imageLeftPoint",imageLeftPoint);

drawKeypoints(imageRgbRight,keyPointRight, imageRightPoint, Scalar::all(-1), DrawMatchesFlags::DEFAULT);

imshow("imageRightPoint",imageRightPoint);

waitKey(0);

特征点提取的效果图如下:

图9-3:左图图像特征点提取结果

图9-4:右图图像特征点提取结果

2.4特征点描述,为特征点匹配做准备   

实例:

SurfDescriptorExtractorsurfDescriptor;

MatimageDescLeft, imageDescRight;

surfDescriptor.compute(imageGrayLeft,keyPointLeft, imageDescLeft);

surfDescriptor.compute(imageRgbRight,keyPointRight, imageDescRight);

2.5获得匹配特征点,并提取最优配对    

用BruteForceMatcher类中的函数match来匹配两幅图像的特征向量

实例:

BruteForceMatchermatcher;

vector<DMatch>matchePoints;

matcher.match(imageDescLeft,imageDescRight, matchePoints, Mat());

BruteForceMatcher是由DescriptorMatcher派生出来的一个类,而DescriptorMatcher定义了不同的匹配策略的共同接口。调用match方法后,在其第三个参数输出一个cv::DMatch向量。于是我们定义一个std::vector<DMatch>类型的matches。

用FlannBasedMatcher类中的函数match来匹配两幅图像的特征向量

实例:

FlannBasedMatchermatcher;

vector<DMatch>matchePoints;

matcher.match(imageDescLeft,imageDescRight, matchePoints, Mat());

FlannBasedMatcher算法更快但是找到的是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度,但是相应地算法速度或者算法精度会受到影响。

2.6 drawMatches()

//作用:从两幅图片中画出发现的匹配的关键点。

形式:void drawMatches(const Mat& img1, constvector<KeyPoint>& keypoints1, const Mat& img2, constvector<KeyPoint>& keypoints2, const vector<DMatch>&matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1),const Scalar& singlePointColor=Scalar::all(-1), constvector<char>& matchesMask=vector<char>(), int flags=DrawMatchesFlags::DEFAULT);

参数:

img1、img2:两幅源图像;

keypoints1、keypoints2:两幅源图像中的关键点;

matches1to2:从第一幅图像来匹配第二幅图像,即从keypoints1[i]中寻找与keypoints2[i]的对应点;

outImg:输出图像;它的值取决于flags--在图像中绘制的内容;

matchColor:匹配颜色(线和点的颜色),如果matchColor==Scalar::all(-1),颜色随机生成;

singlePointColor:单个关键点的颜色,即没有关键点与之匹配,如果matchColor==Scalar::all(-1),颜色随机生成;

matchesMask:掩码决定画哪个匹配的关键点,如果掩码为空,画出所有的匹配的关键点;

flags:设置绘图功能,可能标志位的值由“DrawMatchesFlags”确定;

实例:

Matfirstmatches;

drawMatches(imageRgbLeft,keyPointLeft, imageRgbRight, keyPointRight,

matchePoints, firstmatches,Scalar::all(-1), Scalar::all(-1),

vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

imshow("first_matches",firstmatches);

waitKey(0);

画出匹配效果图如下:

图9-5:特征匹配结果

2.7匹配特征点sort排序

sort方法可以对匹配点进行从小到大的排序,用sort排序之前,每个匹配点对间的距离(即匹配稳健性程度)是随机分布的,排序之后,距离按由小到大的顺序排列,越靠前的,匹配度越高,可以通过排序后把靠前的匹配提取出来。本例中提取前20个最优匹配。

实例:

sort(matchePoints.begin(),matchePoints.end()); //特征点排序

//获取排在前N个的最优匹配特征点

vector<Point2f>imagePointsLeft, imagePointsRight;

intj = 0;

for(int i = 0; i<matchePoints.size(); i++)

{

j++;

imagePointsLeft.push_back(keyPointLeft[matchePoints[i].queryIdx].pt);

imagePointsRight.push_back(keyPointRight[matchePoints[i].trainIdx].pt);

if (j > 20)

break;

}

3、图像配准

3.1 findHomography()和RANSAC去除误匹配

作用:寻找两个平面匹配上的关键点的变换。

形式:Mat findHomography(InputArray srcPoints, InputArray dstPoints, intmethod=0, double ransacReprojThreshold=3, OutputArray mask=noArray() );

参数:

srcPoints:原始平面中点的坐标,即一个CV_32FC2 or vector<Point2f> 型矩阵;

dstPoints:目标平面中点的坐标,即一个CV_32FC2 or vector<Point2f> 型矩阵;

method:用于计算单应矩阵的方法--0:使用所有点的常规方法;

CV_RANSAC:基于RANSAC的鲁棒方法;

CV_LMEDS:最不平均方法;

ransacReprojThreshold:仅当使用CV_RANSAC方法时,把一双点作为内窗的最大投影误差;

mask:鲁棒方法的可选输出掩码设置;

//获取图像1到图像2的投影映射矩阵,尺寸为3*3

vector<unsignedchar> inliersMask(imagePointsLeft.size());

Mathomo = findHomography(imagePointsLeft, imagePointsRight, CV_RANSAC, 5, inliersMask);//使用CV_RANSAC来去除误匹配

vector<DMatch>matches_ransac;

//手动的保留RANSAC过滤后的匹配点对

for(int i = 0; i<inliersMask.size(); i++)

{

//cout<<inliersMask[i]<<endl;

cout << (int)(inliersMask[i])<< endl;

if (inliersMask[i])

{

matches_ransac.push_back(matchePoints[i]);

}

}

Matsecondmatches;

drawMatches(imageRgbLeft,keyPointLeft, imageRgbRight, keyPointRight,

matches_ransac, secondmatches,Scalar::all(-1), Scalar::all(-1),

vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//使用drawMatches画出RANSAC之后的匹配点

imshow("secondmatches",secondmatches);

waitKey(0);

匹配效果图如下:

图9-6:RANSAC去除误匹配后特征匹配结果

H矩阵修正:如果直接使用H矩阵进行图像的拼接将使得在左图没有重叠区域的像素点的坐标变化为负值,这是由于左图是按照右图的坐标系进行配准,所以左右相对于右图的横坐标在非重叠区域都是负值,所以需要引用校正矩阵,其中H矩阵的第一行数据影响水平(x)方向的偏移,第二行数据影响垂直(y)方向的偏移。所以水平偏移需要修正H矩阵的第一行数据,垂直偏移需要修正H矩阵的第二行数据。H矩阵数值的调整可以通过用一个3*3的矩阵相乘实现,水平方向的修正矩阵:

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

垂直方向上的修正矩阵:

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

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

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

实例:

//计算修正矩阵,首先定义原始图像矩形的4个顶点坐标,然后进行放射变化操作,计算配准后顶点坐标,由于左图非重叠区域坐标会变为负值,所以取(0,0)点的变化后横坐标绝对值即为非重叠区域的宽度。

vector<Point2f>obj_corners(4);

obj_corners[0]= Point(0, 0); obj_corners[1] = Point(imageRgbLeft.cols, 0);

obj_corners[2]= Point(imageRgbLeft.cols, imageRgbLeft.rows); obj_corners[3] = Point(0,imageRgbLeft.rows);

vector<Point2f>scene_corners(4);

perspectiveTransform(obj_corners,scene_corners, homo);

MatadjustMat = (Mat_<double>(3, 3) << 1.0, 0, abs(scene_corners[0].x),0, 1.0, 0, 0, 0, 1.0);

MatadjustHomo = adjustMat*homo;

3.2透视变化

voidwarpPerspective( InputArray src, OutputArray dst,

InputArrayM, Size dsize,

intflags=INTER_LINEAR,

intborderMode=BORDER_CONSTANT,

constScalar& borderValue=Scalar());

src:输入2通道或3通道的浮点阵列,每个元素是一个将被转换的2D/3D向量;

dst:与输入有相同大小和类型的输出矩阵;

M:3x3 或4x4的浮点转换矩阵;

Dsize:拼接图像完成图像大小;

MatimageTransformLeft;

warpPerspective(imageRgbLeft,imageTransformLeft, adjustHomo, Size(imageRgbRight.cols +abs(scene_corners[0].x), imageRgbRight.rows));

效果图如下:

图9-7:图像配准前原图

图9-8:图像配准结果图

4、图像融合

4.1 定位重叠区域

//在最强匹配点左侧的重叠区域进行累加,是衔接稳定过渡,消除突变

Matimage1Overlap, image2Overlap; //图1和图2的重叠部分

image1Overlap= imageTransformLeft(Rect(Point(abs(scene_corners[0].x), 0), Point(abs(scene_corners[0].x)+ (scene_corners[2].x), imageRgbRight.rows)));

image2Overlap= imageRgbRight(Rect(0, 0, image1Overlap.cols, image1Overlap.rows));

Matimage1ROICopy = image1Overlap.clone(); //复制一份图1的重叠部分

4.2重叠区域加权融合

for(int i = 0; i<image1Overlap.rows; i++)

{

for (int j = 0; j<image1Overlap.cols;j++)

{

double weight;

weight = (double)j /image1Overlap.cols;  //随距离改变而改变的叠加系数

image1Overlap.at<Vec3b>(i, j)[0]= (1 - weight)*image1ROICopy.at<Vec3b>(i, j)[0] +weight*image2Overlap.at<Vec3b>(i, j)[0];

image1Overlap.at<Vec3b>(i, j)[1]= (1 - weight)*image1ROICopy.at<Vec3b>(i, j)[1] +weight*image2Overlap.at<Vec3b>(i, j)[1];

image1Overlap.at<Vec3b>(i, j)[2]= (1 - weight)*image1ROICopy.at<Vec3b>(i, j)[2] +weight*image2Overlap.at<Vec3b>(i, j)[2];

}

}

4.3 非重叠区域融合

MatROIMat = imageRgbRight(Rect(Point(image1Overlap.cols, 0),Point(imageRgbRight.cols, imageRgbRight.rows)));  //图2中不重合的部分

ROIMat.copyTo(Mat(imageTransformLeft,Rect(abs(scene_corners[0].x) + (scene_corners[2].x), 0, ROIMat.cols,imageRgbRight.rows))); //不重合的部分直接衔接上去

namedWindow("拼接结果", 0);

imshow("拼接结果", imageTransformLeft);

//imwrite("D:\\拼接结果.jpg", imageTransform1);

waitKey(0);

图像融合后效果图如下:

图9-9:图像融合结果

图像拼接算法总结(二)相关推荐

  1. ransac剔除误匹配matlab代码,基于APAP图像拼接算法的改进

    硕 士 学 位 论 文 基于 APAP 图像拼接算法 的 改进 学 科 专 业  软件工程  学 位 类 型工 学硕 士学位 研 究 生 姓 名  刘 诗  导 师姓名 ...

  2. 【算法系列 二】Stack

    为什么80%的码农都做不了架构师?>>>    栈应用的场景: 1.括号问题 2.后缀表达式 3.深度优先遍历 4.保存现场 1. 给定字符串,仅由"()[]{}" ...

  3. 【深度学习】基于深度神经网络进行权重剪枝的算法(二)

    [深度学习]基于深度神经网络进行权重剪枝的算法(二) 文章目录 1 摘要 2 介绍 3 OBD 4 一个例子 1 摘要 通过从网络中删除不重要的权重,可以有更好的泛化能力.需求更少的训练样本.更少的学 ...

  4. DL之PerceptronAdalineGD:基于iris莺尾花数据集利用Perceptron感知机和AdalineGD算法实现二分类

    DL之Perceptron&AdalineGD:基于iris莺尾花数据集利用Perceptron感知机和AdalineGD算法实现二分类 目录 基于iris莺尾花数据集利用Perceptron ...

  5. ML:基于自定义数据集利用Logistic、梯度下降算法GD、LoR逻辑回归、Perceptron感知器、SVM支持向量机、LDA线性判别分析算法进行二分类预测(决策边界可视化)

    ML:基于自定义数据集利用Logistic.梯度下降算法GD.LoR逻辑回归.Perceptron感知器.支持向量机(SVM_Linear.SVM_Rbf).LDA线性判别分析算法进行二分类预测(决策 ...

  6. 看动画学算法之:二叉搜索树BST

    文章目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 看动画学算法之:二叉搜索树BST 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的 ...

  7. k近邻算法C++二维情况下的实现

    k近邻算法C++二维实现 这是一个k近邻算法的二维实现(即K=2的情况). #include <cstdio> #include <cstring> #include < ...

  8. java计算一个多边形的重心_2D凸多边形碰撞检测算法(二) - GJK(上)

    2D凸多边形碰撞检测算法(二) - GJK(上) 原理 在 Narrow Phase 精细碰撞检测中,除了 SAT ,另外一个就是 GJK(Gilbert–Johnson–Keerthi)算法.它足够 ...

  9. 运筹学与最优化方法_[公开课]运筹学之线性规划算法十二讲

    运筹学之线性规划算法十二讲 这是最美好的时代,同样带来最无助的希望:这是最丰富多彩的时代,思想同样的匮乏:这是最互联互通的时代,一样找不到自己和同路的人:这是最光明的时代,我们依然要经过黑暗的摸索和摸 ...

  10. 深度学习笔记(10) 优化算法(二)

    深度学习笔记(10) 优化算法(二) 1. Adam 优化算法 2. 学习率衰减 3. 局部最优的问题 1. Adam 优化算法 Adam代表的是 Adaptive Moment Estimation ...

最新文章

  1. python环境搭建
  2. android隐藏状态栏
  3. 如何编程实现开启或关闭GPS(转)
  4. 【Android工具】更新WPS全功能正版授权无广告版
  5. 物联网产品:你需要知道的9种智能家居产品
  6. Android View坐标系
  7. Java 基础系列-LocalDate相关
  8. mysql数据库基操,都坐下!
  9. RestTemplate 配置http连接池
  10. 案例分享:Qt modbus485调试工具(读写Byte、Int、DInt、Real、DReal)(当前v1.3.0)
  11. Godaddy美国主机推荐
  12. 网络映射iscsi服务器,群晖iSCSI管理器连接网络硬盘详细介绍和配置使用说明
  13. 全向移动机器人运动参数校准
  14. 刷卡分期的套路有多深?信用卡、花呗、白条没有善人
  15. 详解:S锁(读锁)和X锁(写锁)
  16. Win10笔记本电脑硬盘如何分区
  17. 糖友低碳水、高蛋白饮食可行吗
  18. 游戏设计的艺术:一本透镜的书——第十五章 其中一种体验是故事
  19. 笔戈科技到底是不是魅族的?
  20. 阿里巴巴集团副总裁陈丽娟对话VMware全球副总裁原欣:助力企业数字化转型

热门文章

  1. 9. MySQL -- JDBC入门
  2. WebStorm设置中文,没有显示安装官方插件来这
  3. 【OSPF路由配置命令汇总大全】面面俱到
  4. 计算机学院单片机毕设,单片机毕业设计完整版.pdf
  5. 基于VC++的局域网内主机监控系统设计与实现
  6. Uber为何必须尽快上市:增长即将放缓
  7. ESP-IDF3.0
  8. 2023北京智源大会亮点回顾 | 高性能计算、深度学习和大模型:打造通用人工智能AGI的金三角
  9. 移动端适配--概述、简单适配原理、通用适配方案实现
  10. JCFXBL 框架日志存储结构说明