前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >单应性矩阵应用-基于特征的图像拼接

单应性矩阵应用-基于特征的图像拼接

作者头像
OpenCV学堂
发布2020-02-21 13:28:52
3K1
发布2020-02-21 13:28:52
举报
前言

前面写了一篇关于单应性矩阵的相关文章,结尾说到基于特征的图像拼接跟对象检测中单应性矩阵应用场景。得到很多人留言反馈,让我继续写,于是就有这篇文章。这里有两张照片(我手机拍的),背景是我老家的平房,周围是一片开阔地带,都是麦子。有图为证:

图一:

图二:

思路

这里是两张图像的拼接,多张图像与此类似。主要是应用特征提取模块的AKAZE图像特征点与描述子提取,当然你也可以选择ORB、SIFT、SURF等特征提取方法。匹配方法主要是基于暴力匹配/FLANN+KNN完成,图像对齐与配准通过RANSAC跟透视变换实现,最后通过简单的权重图像叠加实现融合、得到拼接之后得全景图像。这个其中单应性矩阵发现是很重要的一步,如果不知道这个是什么请看这里:

OpenCV单应性矩阵发现参数估算方法详解

基本流程

1.加载输入图像

2.创建AKAZE特征提取器

3.提取关键点跟描述子特征

4.描述子匹配并提取匹配较好的关键点

5.单应性矩阵图像对齐

6.创建融合遮罩层,准备开始融合

7.图像透视变换与融合操作

8.输出拼接之后的全景图

关键代码

在具体代码实现步骤之前,先说一下软件版本

代码语言:javascript
复制
-VS2015
-OpenCV4.2
-Windows 10 64位

代码实现:提取特征与描述子

代码语言:javascript
复制
// 提取特征点与描述子
vector<KeyPoint> keypoints_right, keypoints_left;
Mat descriptors_right, descriptors_left;
auto detector = AKAZE::create();
detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);
detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);

提取好的匹配描述子

代码语言:javascript
复制
// 暴力匹配
vector<DMatch> matches;
auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);

// 发现匹配
std::vector< std::vector<DMatch> > knn_matches;
matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);
const float ratio_thresh = 0.7f;
std::vector<DMatch> good_matches;
for (size_t i = 0; i < knn_matches.size(); i++)
{
      if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
      {
              good_matches.push_back(knn_matches[i][0]);
      }
}
printf("total good match points : %d\n", good_matches.size());
std::cout << std::endl;

Mat dst;
drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);

创建mask对象

代码语言:javascript
复制
// create mask
int win_size = 800;
int h1 = left.rows;
int w1 = left.cols;
int h2 = right.rows;
int w2 = right.cols;
int h = max(h1, h2);
int w = w1 + w2;
Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);
Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);
Rect roi;
roi.height = h;
roi.width = win_size;
roi.y = 0;
roi.x = w1 - win_size;

// left mask
Mat temp = mask1(roi);
linspace(temp, 1, 0, win_size);

// right mask
temp = mask2(roi);
linspace(temp, 0, 1, win_size);

对齐生成全景图像

代码语言:javascript
复制
// generate panorama
Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);
roi.x = 0;
roi.y = 0;
roi.width = w1;
roi.height = h1;
left.copyTo(panorama_01(roi));
Mat m1;
vector<Mat> mv;
mv.push_back(mask1);
mv.push_back(mask1);
mv.push_back(mask1);
merge(mv, m1);
panorama_01.convertTo(panorama_01, CV_32F);
multiply(panorama_01, m1, panorama_01);


Mat panorama_02;
warpPerspective(right, panorama_02, H, Size(w, h));
mv.clear();
mv.push_back(mask2);
mv.push_back(mask2);
mv.push_back(mask2);
Mat m2;
merge(mv, m2);
panorama_02.convertTo(panorama_02, CV_32F);
multiply(panorama_02, m2, panorama_02);

上述代码中panorama_01实现对第一张图像内容提取与mask权重生成混合,panorama_02完成对第二张图的内容透视变换与mask权重生成混合。特别注意的是顺序很重要。单应性矩阵发现代码可以看之前文章即可,这里不再赘述。

合并全景图像

代码语言:javascript
复制
// 合并全景图
Mat panorama;
add(panorama_01, panorama_02, panorama);
panorama.convertTo(panorama, CV_8U);
imwrite("D:/panorama.png", panorama);

程序运行->特征点匹配如下:

最终拼接的全景图如下:

想知道如何改进这个输出结果,让输出结果融合的根据自然与真实,请听下回再说吧!过年了终于有点时间写点干货回报一下大家!请大家多多支持!多多反馈!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 OpenCV学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档