图像拼接的几何原理
图像被投影到同一平面上,在拼接平面实现全景融合,简单来说,可以理解为2D拼接叠加
图像拼接整体流程
(一)根据给定图像/集,实现特征匹配
特征匹配用来计算图像之间的映射关系,得到每个匹配图像对之间的单应矩阵,结合上一步的映射模型,我们可以得到最终的图像变换序列,结合前面所提到的特征,选取sift特征描述子(关于sift描述,可以看之前的文章)
(二)通过匹配特征计算图像之间的变换结构
一幅图可以变化成另一幅图像处于相同坐标系的图像,有:
平移变换模型:只对图像进行相对于X轴和Y轴的整体平移。仿射变换模型(仿射变换模型共有6个自由度,只要三对对应变换点即可求出一个仿射变换模型。
仿射模型:具有平行保持性,也就是原来平行的直线经过变换后依旧是平行的)投影
变换模型:用于描述相机的平移,旋转,角度变换,变焦等一系列运动。通常使用齐次坐标。投影变换只具有直线保持性,原来是直线投影后一九是直线。
(三)利用图像变换结构,实现图像映射 、
在图像拼接中可以将图像投影到不同平面,如平面模型,柱状,球形等
平面模型:简单通用,进过图像配准后,使用变换模型直接将拼接图像映射到基准图形平面坐标系上。
柱面模型:先将平面图形映射到柱面上,完成拼接后再将结果映射回平面坐标系。
(四)针对叠加后的图像,采用APAP之类的算法对齐特征点
图像配准是将两张场景相关的图像进行映射,寻找其中的关系。Apap流程:提取两张图片的sift特征点对两张图片的特征点进行匹配, 匹配后,仍有很多错误点, RANSAC的改进算法进行特征点对的筛选。筛选后的特征点基本能够一一对应。使用DLT算法,将剩下的特征点对进行透视变换矩阵的估计。因为得到的透视变换矩阵是基于全局特征点对进行的,即一个刚性的单应性矩阵完成配准。为提高配准的精度,Apap将图像切割成无数多个小方块,对每个小方块的变换矩阵逐一估计。非常依赖于特征点对。若图像高频信息较少,特征点对过少,配准将完全失效,并且对大尺度的图像进行配准,其效果也不是很好,一切都决定于特征点对的数量。
(五)通过图割方法,自动选取拼接缝
图是一个具有权值的有向结构,通常采用一些节点,一些有向连接线表示,这些节点是像素值,或其他特征点。寻找代价最小的分割,典型算法是最小割最大流算法。最大流几句诗将图内带权值看作带有流量值的管道,将最大量水从源点送到汇点。
(六) 根据multi-band bleing策略实现融合
融合目的在于拼缝消除, Multi-Band能够达到比较好的融合效果,但是效率低,采用Laplacian(拉普拉斯)金字塔,通过对相邻两层的高斯金字塔进行差分,将原图分解成不同尺度的子图,对每一个之图进行加权平均,得到每一层的融合结果,最后进行金字塔的反向重建,得到最终融合效果过程。
代码:
# ch3_panorama_test.py
from pylab import *
from numpy import *
from PIL import Image
# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
"""
This is the panorama example from section 3.3.
"""
# set paths to data folder
featname = ['C:/Users//lenovo/Downloads/pcv-book-code-master/ch03/data/a'.sift' for i in range(4)]
imname = [''C:/Users//lenovo/Downloads/pcv-book-code-master/ch03/data/a'+str(i+1)+'.jpg' for i in range(4)]
# extract features and match
l = {}
d = {}
for i in range(4):
sift.process_image(imname[i],featname[i])
l[i],d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(3):
matches[i] = sift.match(d[i+1],d[i])
# visualize the matches (Figure 3-11 in the book)
for i in range(3):
im1 = array(Image.open(imname[i]))
im2 = array(Image.open(imname[i+1]))
figure()
sift.plot_matches(im2,im1,l[i+1],l[i],matches[i],show_below=True)
# function to convert the matches to hom. points
def convert_points(j):
ndx = matches[j].nonzero()[0]
fp = homography.make_homog(l[j+1][ndx,:2].T)
ndx2 = [int(matches[j][i]) for i in ndx]
tp = homography.make_homog(l[j][ndx2,:2].T)
# switch x and y - TODO this should move elsewhere
fp = vstack([fp[1],fp[0],fp[2]])
tp = vstack([tp[1],tp[0],tp[2]])
return fp,tp
# estimate the homographies
model = homography.RansacModel()
fp,tp = convert_points(1)
H_12 = homography.H_from_ransac(fp,tp,model)[0] #im 1 to 2
fp,tp = convert_points(0)
H_01 = homography.H_from_ransac(fp,tp,model)[0] #im 0 to 1
tp,fp = convert_points(2) #NB: reverse order
H_32 = homography.H_from_ransac(fp,tp,model)[0] #im 3 to 2
# warp the images
delta = 2000 # for padding and translation
im1 = array(Image.open(imname[1]), "uint8")
im2 = array(Image.open(imname[2]), "uint8")
im_12 = warp.panorama(H_12,im1,im2,delta,delta)
im1 = array(Image.open(imname[0]), "f")
im_02 = warp.panorama(dot(H_12,H_01),im1,im_12,delta,delta)
im1 = array(Image.open(imname[3]), "f")
im_32 = warp.panorama(H_32,im1,im_02,delta,delta)
figure()
imshow(array(im_42, "uint8"))
axis('off')
savefig("example5.png",dpi=300)
show()
实现效果:
错差较大的图片进行合成:
集美大学庄汉水楼
可以看出这三张图片空间错差极大,所以可想而知拼接出来的效果不尽人意。
那么接下来三张落差小的图片
拼接图:
室内图像:
合成图像:
(集美大学延奎图书馆内景)
mistake:
如果是 from PCV.geometry import warp, homography 报错
是因为PCV下面的warp.py里面的matplotlib.delaunay不再被使用了,所以把它换成一个相同功能的就可以:
1:把import matplotlib.delaunay as md 换成from scipy.spatial import Delaunay
2:warp.py里面的centers,edges,tri,neighbors = md.delaunay(x,y)换成tri= Delaunay(np.c_[x,y]).simplices就ok啦
还有就是图片要从右向左按顺序放,不能乱顺序。