大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
前言
论文及源码地址:APAP项目入口
论文精读:【论文精读】As-Projective-As-Possible Image Stitching with Moving DLT
源码用的MDLT code,解压后的文件夹是mdlt
注意,matlab是基于列向量优先的!
让我们从main.m开始!
准备工作与全局变量
比较高版本的matlab需要将多线程部分改成如下代码:
%poolsize = matlabpool('size');
poolsize = parpool('local');
if poolsize == 0 %if not, we attempt to do it:
parpool open;
end
全局变量如下,fitfn等是准备好的求全局单应的函数,在文件夹modelspecific中,C1,C2是网格数:
%-------------------------
% User defined parameters.
%-------------------------
% Global model specific function handlers.
clear global;
global fitfn resfn degenfn psize numpar
fitfn = 'homography_fit';
resfn = 'homography_res';
degenfn = 'homography_degen';
psize = 4;
numpar = 9;
M = 500; % Number of hypotheses for RANSAC.
thr = 0.1; % RANSAC threshold.
C1 = 100; % Resolution/grid-size for the mapping function in MDLT (C1 x C2).
C2 = 100;
gamma = 0.1; % Normalizer for Moving DLT. (0.0015-0.1 are usually good numbers).
sigma = 8.5; % Bandwidth for Moving DLT. (Between 8-12 are good numbers).
scale = 1; % Scale of input images (maybe for large images you would like to use a smaller scale).
基于全局单应的图像拼接过程
1.读入图像,特征点检测与匹配
采用原作者注释掉的读取自己输入的两张图片形式,具体代码细节及思想见代码注释,下同。
%------------------
% Images to stitch.
%------------------
path1 = 'images/case26/6.JPG';
path2 = 'images/case26/7.JPG';
%-------------
% Read images.
%-------------
fprintf('Read images and SIFT matching\n');tic;
fprintf('> Reading images...');tic;
img1 = imresize(imread(sprintf('%s',path1)),scale);
img2 = imresize(imread(sprintf('%s',path2)),scale);
fprintf('done (%fs)\n',toc);
%--------------------------------------
% SIFT keypoint detection and matching.
%--------------------------------------
fprintf(' Keypoint detection and matching...');tic;
%single(rgb2gray(img1))将img1转换成单精度灰度图,sift固定格式,必须是单精度的
%'PeakThresh':DoG算法的阈值,值越小,检测到的特征点越多
%'edgethresh':消除DoG尺度空间峰值,值越大,检测到的特征点越多
%kp每一列是一个四元组[x,y,s,th],代表一个特征点信息,分别x,y坐标,s为长度空间大小,th指的是主方向
%ds是特征描述子,也就是那个128维的向量,128×特征点数的矩阵
[ kp1,ds1 ] = vl_sift(single(rgb2gray(img1)),'PeakThresh', 0,'edgethresh',500);
[ kp2,ds2 ] = vl_sift(single(rgb2gray(img2)),'PeakThresh', 0,'edgethresh',500);
%vl_ubcmatch:欧式距离匹配,返回匹配矩阵和匹配对间距离
%[matches, scores] = vl_ubcmatch(ds1, ds2) ;
%matches返回的是2*特征点数量矩阵,
%第一行是一幅图中的匹配点索引,第二行是另一幅图匹配点索引
%每一列是一个匹配点对索引,scores是距离
matches = vl_ubcmatch(ds1,ds2);
fprintf('done (%fs)\n',toc);
2.数据归一化,RANSAC剔除异常值
% Normalise point distribution.
% 数据归一化的作用是方便后面RANSAC剔除错误点,消除干扰,提高精度,简化计算
fprintf(' Normalising point distribution...');tic;
%matches(1,:):匹配点第一行索引
%matches(2,:):另一张图中的匹配点索引
%kp1(1:2,matches(1,:)):提取出匹配点索引的对应横纵坐标
%size(matches,2):取matches的第二维也就是列,共482列,也就是匹配点数目
%ones(1,size(matches,2)):生成1×482全1矩阵
%data_orig是6×482,每个列是两个点的[x y 1]
data_orig = [ kp1(1:2,matches(1,:)) ; ones(1,size(matches,2)) ; kp2(1:2,matches(2,:)) ; ones(1,size(matches,2)) ];
%T矩阵为3×3矩阵
%normalise2dpts作用:把一系列的齐次坐标[x y 1]归一化,使得这些点以原点为中心,距离原点均值为sqrt(2)。
[ dat_norm_img1,T1 ] = normalise2dpts(data_orig(1:3,:));
[ dat_norm_img2,T2 ] = normalise2dpts(data_orig(4:6,:));
%data_norm:6*482
data_norm = [ dat_norm_img1 ; dat_norm_img2 ];
fprintf('done (%fs)\n',toc);
% %展示输入图像
% if size(img1,1) == size(img2,1)
% % Show input images.
% fprintf(' Showing input images...');tic;
% figure;
% imshow([img1,img2]);
% title('Input images');
% fprintf('done (%fs)\n',toc);
% end
%-----------------
% Outlier removal.前半部分没读懂代码 mutigs文件夹里有该函数
%-----------------
fprintf('Outlier removal\n');tic;
% Multi-GS
%设置随机种子,rand('state',0),相当于C++中的srand(0)
rng(0);
[ ~,res,~,~ ] = multigsSampling(100,data_norm,M,10);
con = sum(res<=thr);
[ ~, maxinx ] = max(con);
%找到匹配度最高的特征点序列,inliers存的是匹配对的索引
inliers = find(res(:,maxinx)<=thr);
if size(img1,1) == size(img2,1)
% Show results of RANSAC.
fprintf(' Showing results of RANSAC...');tic;
figure;
imshow([img1 img2]);
%添加新绘图保持原绘图
hold on;
%ro是形状:红圈,LineWidth线宽为2
%data_orig前两行是一个图的匹配点,用红圈画,4,5行是另一个图的点,红圈画
plot(data_orig(1,:),data_orig(2,:),'ro','LineWidth',2);
%+size(img1,2)为了让它在第二张图位置显示
plot(data_orig(4,:)+size(img1,2),data_orig(5,:),'ro','LineWidth',2);
for i=1:length(inliers)
%选择对应内点索引的坐标,12行是一幅图,45行是另一幅图,绿圈画
plot(data_orig(1,inliers(i)),data_orig(2,inliers(i)),'go','LineWidth',2);
plot(data_orig(4,inliers(i))+size(img1,2),data_orig(5,inliers(i)),'go','LineWidth',2);
%1,4行对应横坐标矩阵,2,5行对应纵坐标矩阵,绿线相连
plot([data_orig(1,inliers(i)) data_orig(4,inliers(i))+size(img1,2)],[data_orig(2,inliers(i)) data_orig(5,inliers(i))],'g-');
end
title('Ransac''s results');
fprintf('done (%fs)\n',toc);
end
3.计算全局单应,获取拼接图大小,拼接
%-----------------------
% Global homography (H).计算全局单应
%-----------------------
fprintf('DLT (projective transform) on inliers\n');
% Refine homography using DLT on inliers.
fprintf('> Refining homography (H) using DLT...');tic;
%feval调用参数中fitfn函数,参数为data_norm(:,inliers),即归一化后数据内点索引所在的列
[ h,A,D1,D2 ] = feval(fitfn,data_norm(:,inliers));
%上一步处理之后要再处理回去
Hg = T2\(reshape(h,3,3)*T1);%Hg是全局单应矩阵
fprintf('done (%fs)\n',toc);
%----------------------------------------------------
% Obtaining size of canvas (using global Homography).
%----------------------------------------------------
fprintf('Canvas size and offset (using global Homography)\n');
fprintf('> Getting canvas size...');tic;
%获得拼接图大小,左图不变,右图单应变换
% Map four corners of the right image.
%由于求得的H 是将左图映射到右图,
%即 H*x_left = x_right,所以 x_left = inv(H) * x_right.
%取右图的四个顶点的齐次坐标 分别作为 x_right 的值
%以img1左顶点为坐标源点,得到新的四个顶点坐标:TL, BL, TR, BR
TL = Hg\[1;1;1];% 即 inv(Hg)*[1;1;1], 得到非齐次形式
TL = round([ TL(1)/TL(3) ; TL(2)/TL(3) ]);% 齐次化!
BL = Hg\[1;size(img2,1);1];
BL = round([ BL(1)/BL(3) ; BL(2)/BL(3) ]);
TR = Hg\[size(img2,2);1;1];
TR = round([ TR(1)/TR(3) ; TR(2)/TR(3) ]);
BR = Hg\[size(img2,2);size(img2,1);1];
BR = round([ BR(1)/BR(3) ; BR(2)/BR(3) ]);
% Canvas size.确定画布的尺寸(cw, ch)
% 投影面的 宽,通过最大的x(列)-最小的x(列)+1
cw = max([1 size(img1,2) TL(1) BL(1) TR(1) BR(1)]) - min([1 size(img1,2) TL(1) BL(1) TR(1) BR(1)]) + 1;
% 投影面的 高
ch = max([1 size(img1,1) TL(2) BL(2) TR(2) BR(2)]) - min([1 size(img1,1) TL(2) BL(2) TR(2) BR(2)]) + 1;
fprintf('done (%fs)\n',toc);
% Offset for left image.确定左图的偏移量off,即左图img1 左顶点 在画布坐标系中的 坐标
fprintf('> Getting offset...');tic;
off = [ 1 - min([1 size(img1,2) TL(1) BL(1) TR(1) BR(1)]) + 1 ; 1 - min([1 size(img1,1) TL(2) BL(2) TR(2) BR(2)]) + 1 ];
fprintf('done (%fs)\n',toc);
%--------------------------------------------
% Image stitching with global homography (H).
%--------------------------------------------
% Warping source image with global homography
fprintf('Image stitching with global homography (H) and linear blending\n');
fprintf('> Warping images by global homography...');tic;
%创建一个拼接图的空画布
warped_img1 = uint8(zeros(ch,cw,3));
%将左图按照左图偏移off放到画布上
warped_img1(off(2):(off(2)+size(img1,1)-1),off(1):(off(1)+size(img1,2)-1),:) = img1;
%调用imagewarping.cpp,实现warp
warped_img2 = imagewarping(double(ch),double(cw),double(img2),Hg,double(off));
%c++中维度变了,变回matlab所需维度
warped_img2 = reshape(uint8(warped_img2),size(warped_img2,1),size(warped_img2,2)/3,3);
fprintf('done (%fs)\n',toc);
在这之后我添加了单张图放在画布上的结果,以及融合之前的结果
%--------------------------------------------
% 显示单独放在画布上的两张图和融合前的拼接图
%--------------------------------------------
%得到的warped_img1,warped_img2是单独放在整个画布上的
figure;
imshow(warped_img1);
title('warpedimg1');
figure;
imshow(warped_img2);
title('warpedimg2');
output_canvas(:,:,1) = warped_img1(:,:,1)+warped_img2(:,:,1);
output_canvas(:,:,2) = warped_img1(:,:,2)+warped_img2(:,:,2);
output_canvas(:,:,3) = warped_img1(:,:,3)+warped_img2(:,:,3);
output_canvas = uint8(output_canvas);
figure;
imshow(output_canvas);
title('output_canvas');
融合前的效果:
可见融合是为了消除两张拼接图光照不同的影响。
4.加权融合
% Blending images by simple average (linear blending)
fprintf(' Homography linear image blending (averaging)...');tic;
linear_hom = imageblending(warped_img1,warped_img2);
fprintf('done (%fs)\n',toc);
figure;
imshow(linear_hom);
title('Image Stitching with global homography');
imageblending.m:
function output_canvas = imageBlending(warped_img1,warped_img2)
%本函数作用:加权平均融合
%将输入warp图形二值化并填充孔
w1 = imfill(im2bw(uint8(warped_img1), 0),'holes');
w2 = imfill(im2bw(uint8(warped_img2), 0),'holes');
%转换为灰度图
w1 = mat2gray(w1);
w2 = mat2gray(w2);
%注意转换类型
%matlab处理图像一定先将图像转换为double,im2double
%1 有些函数支持double型,而不支持uint8的数据类型,所以要转换
%2 精度问题了,因为uint8进行数据处理的时候,容易造成数据溢出或精度不够。
warped_img1 = double(warped_img1);
warped_img2 = double(warped_img2);
%每个维度根据两张图的二值化灰度图进行融合,主要是处理两张图亮度不同
%注意都是矩阵运算: .*,./ 点乘点除
output_canvas(:,:,1) = ((warped_img1(:,:,1).*w1)+(warped_img2(:,:,1).*w2))./(w1+w2);
output_canvas(:,:,2) = ((warped_img1(:,:,2).*w1)+(warped_img2(:,:,2).*w2))./(w1+w2);
output_canvas(:,:,3) = ((warped_img1(:,:,3).*w1)+(warped_img2(:,:,3).*w2))./(w1+w2);
%最后转换回uint8
output_canvas = uint8(output_canvas);
融合之后的结果:
消除了光照带来的影响,但是没对齐。
总结
本章讲述了用matlab实现全局单应Global homography的过程及源码分析,本章并不算论文APAP中的部分,而是用来对比APAP方法的优点的,那么我们将在下一章重点讲解APAP的实现原理及过程,欢迎对图像拼接领域的感兴趣的朋友们讨论!
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/190475.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...