1.针对某个场景拍摄多张/序列图像
2.计算第二张图像与第一张图像之间的变换关系
3.将第二张图像叠加到第一张图像的坐标系中
4.变换后的融合/合成
5.在多图场景中,重复上述过程
首先提取一张图片的特征点,生成对应描述子
接着进行特征匹配
同时,将图像变换到目标坐标系,计算变换结构,进行图像融合
全景融合相当于将图像投影到共同的拼接平面上(同一坐标系)
,接着在拼接平面上实现全景融合
在拼接的应用中,其实可以简化理解为 2D图像的变换,叠加过程
图像拼接的核心步骤在于计算图片间的变换结构,即如何实现图像的映射变换,我们通过RANSAC算法实现。
PANSAC是“RANdom SAmple Consensus”(随机一致性采样)的缩写。该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵,RANSAC基本的思想是,数据中包含着正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。其大致过程如下:
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
# 将匹配转换成齐次坐标点的函数
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)
fp = vstack([fp[1], fp[0], fp[2]])
tp = vstack([tp[1], tp[0], tp[2]])
return fp, tp
if __name__=='__main__':
featname = ['img\\jmu0' + str(i + 1) + '.sift' for i in range(5)]
imname = ['img\\jmu0' + str(i + 1) + '.jpg' for i in range(5)]
im = [array(Image.open(imname[i]).convert('L')) for i in range(5)]
l = {}
d = {}
for i in range(5):
sift.process_image(imname[i], featname[i])
l[i], d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(4):
matches[i] = sift.match(d[i + 1], d[i])
model = homography.RansacModel()
fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0]
fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]
tp, fp = convert_points(2)
H_32 = homography.H_from_ransac(fp, tp, model)[0]
tp, fp = convert_points(3)
H_43 = homography.H_from_ransac(fp, tp, model)[0]
delta = 1000
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)
im1 = array(Image.open(imname[4]), "f")
im_42 = warp.panorama(dot(H_32,H_43),im1,im_32,delta,2*delta)
figure()
imshow(array(im_42, "uint8"))
axis('off')
show()
通过对结过分析,得出了如下图所示问题,其中 “√” 表示效果相对较好。从中我们可以得出图片数据越多与场景月复杂则越容易出现错配,以一端为基准进行拼接容易产生图像扭曲,且图片数据越多越容易出现,具体各情况间下文分析。
结果如下,从结果中我们可以看出在黑夜环境下背景与建筑对比度高,能够很好进行特征匹配,但是最终的拼接结果还是稍有瑕疵。如下图中的警示杆没能够很好的进行拼接,个人认为这是由于两张图片的亮度相差较大,虽然sift特征匹配能够一定程度上对亮度有差异的图像特征进行匹配,但不能够实现完全匹配,在该情况中由于部分特征点的错配而导致了拼接错位。
同时结果中也可以看出有很明显的拼接缝隙,如下图,可以很明显的看到两侧图片的明暗对比,这是由于两张图片本就存在亮度差距,而该算法并没有实现对图片灰度值的优化,为了解决该问题可以在拼接后对图片全局进行均衡化以均衡明暗程度
室内拼接状况以引桐楼宿舍为例,数据如下
该组数据不为同一平面拍摄,更真实的还原了全景图像的拍摄。但其效果如图所示,图像的扭曲过于严重,拼接效果极差。这是由于该算法仅能对二维平面实现很好的特征匹配,而对于不在同一平面的三维场景
无法进行高准确度的匹配,因此出现了图片的过渡扭曲与拼接错位。
对于多建筑的户外场景,我们以鹭江道周边为例,图片数据如下图:
该拼接结果同样很明显的可以看出拼接缝隙即左右的图片明暗对比。但在一些细节之处,如图中国际银行大厦顶上的一片云彩都进行了很好的拼接。而若要解决图片明暗不均问题还是需要上文所提到进行均衡化。
对于同样多建筑的情景,通过复现书本中的代码与图片数据,可以发现在位于边缘区域的图片在拼接时出现“拉伸”的情况,这是由于图片的尺度不同而导致,在与周围图片进行拼接时对图片进行的一定程度的放缩。对于该问题可以使用平差法进行优化,将每张图片匹配后的误差总和计算均值,在均分到每一张图片,这样能够达到更优的图片拼接效果。
对于单调场景,我们取白城沙滩一隅为例,图片数据如下图:
在对于这种单调且景观及其相似的场景,尤其考研算法的精度,从以下结果中我们也可以看出出现了不少图片的错配。
在这种景物高度相似的场景下特征不易进行匹配,且容易出现错配,因此需要优化特征点的匹配算法才能够更好的解决该问题
对于复杂场景即内容丰富的场景,以集大校园为例,图片数据如下图:
在该情景下由于景物丰富,我们的算法可以找到大量的匹配特征点,充分利用了场景中的各个景物,因此也能够得到极优的图像拼接结果。但不容忽视的是对于旋转扭曲的图片该算法还是出现了拼接错误。
如下图所示,图片间虽然有很多特征点能够进行匹配但是在拼接过程中却出现了问题,个人认为这是由于图片中错配的特征点太多,导致取均值后出现误差过大而导致,若想解决该问题则需要运用更加精确的特征匹配算法或是对特征匹配的结果进行优化,出去错配特征。