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

小白学视觉

共 4732字,需浏览 10分钟

 ·

2021-05-30 01:49

点击上方小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

本文转自:深度学习这件小事


前言


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

图一:

图二:


思路


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

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


基本流程

1.加载输入图像

2.创建AKAZE特征提取器

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

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

5.单应性矩阵图像对齐

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

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

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


关键代码


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

-VS2015-OpenCV4.2-Windows 10 64位

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

1// 提取特征点与描述子
2vector<KeyPoint> keypoints_right, keypoints_left;
3Mat descriptors_right, descriptors_left;
4auto detector = AKAZE::create();
5detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);
6detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);


提取好的匹配描述子

 1// 暴力匹配
2vector<DMatch> matches;
3auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);
4
5// 发现匹配
6std::vectorstd::vector<DMatch> > knn_matches;
7matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);
8const float ratio_thresh = 0.7f;
9std::vector<DMatch> good_matches;
10for (size_t i = 0; i < knn_matches.size(); i++)
11{
12      if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
13      {
14              good_matches.push_back(knn_matches[i][0]);
15      }
16}
17printf("total good match points : %d\n", good_matches.size());
18std::cout << std::endl;
19
20Mat dst;
21drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);


创建mask对象

 1// create mask
2int win_size = 800;
3int h1 = left.rows;
4int w1 = left.cols;
5int h2 = right.rows;
6int w2 = right.cols;
7int h = max(h1, h2);
8int w = w1 + w2;
9Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);
10Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);
11Rect roi;
12roi.height = h;
13roi.width = win_size;
14roi.y = 0;
15roi.x = w1 - win_size;
16
17// left mask
18Mat temp = mask1(roi);
19linspace(temp, 10, win_size);
20
21// right mask
22temp = mask2(roi);
23linspace(temp, 01, win_size);


对齐生成全景图像

 1// generate panorama
2Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);
3roi.x = 0;
4roi.y = 0;
5roi.width = w1;
6roi.height = h1;
7left.copyTo(panorama_01(roi));
8Mat m1;
9vector<Mat> mv;
10mv.push_back(mask1);
11mv.push_back(mask1);
12mv.push_back(mask1);
13merge(mv, m1);
14panorama_01.convertTo(panorama_01, CV_32F);
15multiply(panorama_01, m1, panorama_01);
16
17
18Mat panorama_02;
19warpPerspective(right, panorama_02, H, Size(w, h));
20mv.clear();
21mv.push_back(mask2);
22mv.push_back(mask2);
23mv.push_back(mask2);
24Mat m2;
25merge(mv, m2);
26panorama_02.convertTo(panorama_02, CV_32F);
27multiply(panorama_02, m2, panorama_02);

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


合并全景图像

1// 合并全景图
2Mat panorama;
3add(panorama_01, panorama_02, panorama);
4panorama.convertTo(panorama, CV_8U);
5imwrite("D:/panorama.png", panorama);


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

最终拼接的全景图如下:

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


下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群


欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~


浏览 29
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报