maltab-图像拼接(左右两幅图)
图像拼接
参考自 https://blog.csdn.net/m0_37565736/article/details/79865990 并修改了其中错误的地方,添加自己的讲解或者看法。
我要拼接的是一副画卷,如下(大小一样的,都是3000*4000像素)


首先,就是读取图像
clear all
clc
file1='G:/picture/a.jpg';
file2='G:/picture/b.jpg';
I1=imread(file1);%读取图片
I2=imread(file2); imgs=[I1,I2];
figure,imshow(imgs);%并排显示两幅待拼接图像
title('待拼接图像');
但是,读出来的图像在imshow中被旋转了一下,出来是这个样子的

这样就这样吧。。不影响操作
下一步将其灰度化,并提取SURF特征点。
img1=rgb2gray(I1);
img2=rgb2gray(I2);
imageSize=size(img1); p1=detectSURFFeatures(img1);
p2=detectSURFFeatures(img2);%检测SURF特征点
然后提取特征向量并加以匹配
[img1Features, p1] = extractFeatures(img1, p1);%使用64维向量表示特征描述子,
%第一个返回的参数即为每个特征点对应的特征描述子,第二个参数是特征点
[img2Features, p2] = extractFeatures(img2, p2);
boxPairs = matchFeatures(img1Features, img2Features);%特征描述子匹配 matchedimg1Points = p1(boxPairs(:, 1));%第二个参数:可以不加,因为其为n行1列的结构体数组
matchedimg2Points = p2(boxPairs(:, 2));

这时我们可以看到,匹配成功的特征点密密麻麻的,甚至篮圈里面的特征点匹配还有问题,怎么可能匹配上嘛。
所以,我们接下来要去除误匹配点,进行MSAC算法实现。另外,通过特征点匹配还得到了第二幅图的变换矩阵tform,第二幅图要经过变换矩阵变成和第一幅图的坐标一致
[tform, inlierimg2Points, inlierimg1Points] = ...
estimateGeometricTransform(matchedimg2Points, matchedimg1Points, 'projective');%射影变换,tfrom映射点对1内点到点对2内点
%该函数使用随机样本一致性(RANSAC)算法的变体MSAC算法实现,去除误匹配点
%The returned geometric transformation matrix maps the inliers in matchedPoints1
%to the inliers in matchedPoints2.返回的几何映射矩阵映射第一参数内点到第二参数内点 showMatchedFeatures(I1, I2, inlierimg1Points, ...
inlierimg2Points, 'montage');
title('Matched Points (Inliers Only)');

好,精确匹配之后,看到匹配的总体就好多了,看上去很准确。把两幅图共有的部分都匹配上了。
下一步进行图像合并,tform是变换矩阵,以第一幅图像为基准坐标,第二幅图要进行变换与其对应,
Rfixed就是第一幅图的世界二维坐标。
Rfixed = imref2d(size(I1));
[registered2, Rregistered] = imwarp(I2, tform);
%[registered1, Rregistered1] = imwarp(I1, tform);
figure()
imshowpair(I1,Rfixed,registered2,Rregistered,'blend');
title('图像差异');

按照特征点直接来拼接,是这个样子滴,看到第二幅图经过变换矩阵后稍微倾斜了一下,然后特征点匹配后会出现两条明显的边缘,在y=0和y=1500的地方。
下面的操作就是要消除这两个边缘。让其完美的融入。
我用的是参考博客上的渐入渐出的原理。大家有好的方法欢迎给博主分享。
渐入渐出融合
所谓渐入渐出就是将两幅图重合的区域按照距离两幅图的距离按照一定的权重重新分配重合部分图画的三原色权重。比如最中间的就是0.5 0.5的比例。
那么,下一步就是找到他们重叠区域了,也就是相同掩模区。
先确定一下整体的像素大小以及两幅图的实际具体位置。
[xlim, ylim] = outputLimits(tform, [1 imageSize(2)], [1 imageSize(1)]);%输出坐标范围 x:23.8~4334 y:-1844~1447
% 找到输出空间限制的最大最小值
xMin = min([1; xlim(:)]);%1
xMax = max([imageSize(2); xlim(:)]);%4334 yMin = min([1; ylim(:)]);%-1844
yMax = max([imageSize(1); ylim(:)]);%3000 % 全景图的宽高
width = round(xMax - xMin);
height = round(yMax - yMin); %创建2D空间参考对象定义全景图尺寸
xLimits = [xMin xMax];
yLimits = [yMin yMax];
panoramaView = imref2d([height width ], xLimits, yLimits); % 变换图片到全景图.
unwarpedImage = imwarp(I1,projective2d(eye(3)), 'OutputView', panoramaView);
warpedImage = imwarp(I2, tform, 'OutputView', panoramaView);
以第一幅图为基准找到重叠区域(掩模区)

第三张图就是两幅图的共同区域了。我们在这个区域里面对两幅图像色彩的权重进行重新的分配,然后显示图像。

这幅图就是 第一张图的权重分配,理他近的边缘权重为1,离他最远的重合位置边缘权重为0.为了避免使用for循环增加计算时间,我只能将整个x轴范围都采用了权重再分配,而不是那个精确的重叠区域不规则图形。
% % %%%%%%%%%%%%%%%%%%%%%%%%%%渐入渐出融合%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MAKE MASKS FOR BOTH IMAGES
% warpedImage(isnan(warpedImage))=0; % newImage = zeros(size(warpedImage));
% newImage(1:size(I1,1), 1: size(I1,2)+100,:) = I1;
newImage=unwarpedImage;
newImage=double(newImage); balck1=(warpedImage(:,:,1)==0 & warpedImage(:,:,2)==0 & warpedImage(:,:,3)==0);
balck2=(newImage(:,:,1)==0 & newImage(:,:,2)==0 & newImage(:,:,3)==0);
black=and(balck1,balck2);
black=~black; maskA = (warpedImage(:,:,1)>0 |warpedImage(:,:,2)>0 | warpedImage(:,:,3)>0);%变换图像掩膜
mask1 = (newImage(:,:,1)>0 | newImage(:,:,2)>0 | newImage(:,:,3)>0);%非变换图像掩膜
mask1 = and(maskA, mask1);%重叠区掩膜
% figure,imshow(mask1)
[row,col] = find(mask1==1);
left = min(col);
right = max(col);%获得重叠区左右范围
up=min(row);
down=max(row);
mask = ones(size(mask1));
% figure()
% imshow(mask)
%mask(:,left:right) = repmat(linspace(0,1,right-left+1),size(mask,1),1);%复制平铺矩阵
mask(up:down,:) = repmat(linspace(1,0,down-up+1)',1,size(mask,2));%复制平铺矩阵
% BLEND EACH CHANNEL
warpedImage=double(warpedImage);
% figure()
% warpedImage=uint8(warpedImage);
% imshow(warpedImage)
% figure()
% imshow(mask)
warpedImage(:,:,1) = warpedImage(:,:,1).*mask;
warpedImage(:,:,2) = warpedImage(:,:,2).*mask;
warpedImage(:,:,3) = warpedImage(:,:,3).*mask; % REVERSE THE ALPHA VALUE
%mask(:,left:right) = repmat(linspace(1,0,right-left+1),size(mask,1),1);
mask(up:down,:) = repmat(linspace(0,1,down-up+1)',1,size(mask,2));%复制平铺矩阵
newImage(:,:,1) = newImage(:,:,1).*mask;
newImage(:,:,2) = newImage(:,:,2).*mask;
newImage(:,:,3) = newImage(:,:,3).*mask; newImage(:,:,1) = warpedImage(:,:,1) + newImage(:,:,1);
newImage(:,:,2) = warpedImage(:,:,2) + newImage(:,:,2);
newImage(:,:,3) = warpedImage(:,:,3) + newImage(:,:,3); % newImage(:,:,1) = newImage(:,:,1).*black;
% newImage(:,:,2) = newImage(:,:,2).*black;
% newImage(:,:,3) = newImage(:,:,3).*black;
newImage=uint8(newImage);
figure()
imshow(newImage);
title('渐入渐出融合');

看出,效果还是不错的,没有一点点边缘的痕迹。但是最下方有书本的阴影出现,我猜想是我对重叠区域的处理不够准确引起的。大家可以试试准确的处理这个不规则重叠区域的权重再分配。
欢迎大家向博主分享,一同学习。
maltab-图像拼接(左右两幅图)的更多相关文章
- OpenCV——直方图计算、寻早最值位置和对比匹配(判断两幅图的相似程度)
- CV 两幅图像配准
http://www.cnblogs.com/Lemon-Li/p/3504717.html 图像配准算法一般可分为: 一.基于图像灰度统计特性配准算法:二.基于图像特征配准算法:三.基于图像理解的配 ...
- Codeforces Gym101170I:Iron and Coal(建多幅图+多次BFS)***
题目链接 题意 有n个点,其中有m个点是铁矿,k个点是煤,从1号点出发,你可以派一些士兵跑向不同的点,问占领至少一个铁矿和一个煤的时候,最少需要占领多少个点. 思路 建两幅图,其中一幅是正向边,一幅是 ...
- ggplot2在一幅图上画两条曲线
ggplot2在一幅图上画两条曲线 print(data)后的结果是 C BROWN.P MI.P 0 0.9216 0.9282 30 0.9240 0.9282 100 0.9255 0.9282 ...
- Hadoop阅读笔记(四)——一幅图看透MapReduce机制
时至今日,已然看到第十章,似乎越是焦躁什么时候能翻完这本圣经的时候也让自己变得更加浮躁,想想后面还有一半的行程没走,我觉得这样“有口无心”的学习方式是不奏效的,或者是收效甚微的.如果有幸能有大牛路过, ...
- OpenCv实现两幅图像的拼接
直接贴上源码 来源:http://www.myexception.cn/image/1498389.html 实验效果 Left.jpg right.jpg ImageMatch.jpg #inclu ...
- 图说Java —— 理解Java机制最受欢迎的8幅图
原文链接: Top 8 Diagrams for Understanding Java 翻译人员: 铁锚 翻译时间: 2013年10月29日 世间总是一图胜过千万言! 下面的8幅图来自于 Progr ...
- 理解Java机制最受欢迎的8幅图
原文链接: Top 8 Diagrams for Understanding Java 翻译人员: 铁锚 翻译时间: 2013年10月29日 世间总是一图胜过千万言! 下面的8幅图来自于 Progr ...
- 一幅图秒懂LoadAverage(负载)
转自:http://www.habadog.com/2015/02/27/what-is-load-average/ 一幅图秒懂LoadAverage(负载) 一.什么是Load Average? ...
随机推荐
- 2018.8.8 SpringMVC分层
分层: 表示层:请求分发,调用处理器,页面展示. 业务层:业务处理接口和实现. 持久层:数据访问和持久化. 各层之间解耦,下层对上层透明. 具体代码分析如下图,图转自https://blog.csdn ...
- 采用CAS算法 实现高性能的Disruptor 完成多线程下并发、等待、先后等操作
来源:https://blog.csdn.net/tianyaleixiaowu/article/details/79787377 拓展: https://www.jianshu.com/p/d24b ...
- django-微信小程序登录
小程序登录逻辑前端通过调用wx.login()获取code, 将code和用户基本信息发送到后端,后端通过request.get向微信服务器发送get请求获取用户openid和session_key, ...
- Echart横坐标时间轴滑动
主要针对于dataZoom的使用,代码如下: option = { title: { text: '未来一周气温变化', subtext: '纯属虚构' }, tooltip: { trigger: ...
- MQ实战
MQ是什么? MQ(消息队列)是一种跨进程的通信机制,用于上下游传递消息. MQ的优点 异步处理,代码解藕. spring中集成MQ的实现 1. xml配置 <?xml version=&quo ...
- 剑指offer 12.代码的完整性 数值的整数次方
题目描述 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 本人渣渣代码: public double Power(double ba ...
- MongoDB Sharding分片 shell 脚本
#!/bin/sh CONFIG_NAME=$ CONFIG_PORT=$ SERIAL_NUM=$ STORAGE_HOME=$ if [ ! -n "$CONFIG_NAME" ...
- STM32 USB-三个HID-interface 复合(组合)设备的代码实现-基于固件库(原创)
一.概论: 在STM32_USB-FS-Device_Lib_V4.1.0的Custom_HID工程基础上进行修改: 开发一款设备,有三个HID接口,mouse+pen+自定义HID 其中:0_HID ...
- WinForm外包公司 WInform外包项目监控案例展示
北京动点飞扬软件开发团队 C# WInform监控项目案例展示 长年承接WInForm C#项目开发,商业案例欢迎联系我们索取 有相关项目外包定制开发 欢迎联系我们 qq372900288 Tel 1 ...
- python3-基础2
数据类型:数字 .字符.列表.字典.集合 字符串: 要用引号引起来 单引号 双引号 三引号 字符串只能存一个值,没有单独字符一说 取字符串值 print(name[0]) 中括号表示 常用 ...