全景图像拼接
1、原理介绍
图像拼接技术就是将数张有重叠部分的图像(可能是不同时间、不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分辨率图像的技术。图像拼接的输出是两个输入图像的并集。
关于全景融合的3D 几何解释:
图像被投影到共同的拼接平面上(同一坐标系)
在拼接平面上实现全景融合
2、实现步骤
1、特征点匹配
合成全景图的第一步是提取并且匹配所有素材图片的局部特征点。在合成全景图的流程里运用的是“点匹配”,也就是匹配局部图片信息。因为局部图片能提取的特征有限,比如说形状特征在这种情况就不适宜,所以一般采用通过整个局部的图案来判断是否符合作为特征的条件。其中运用最普遍的为SIFT特征(Scale-invariant feature transform),即尺度不变特征转换。即便找到了特征点,单纯匹配特征点周围的局部影像是行不通的。首先SIFT特征本身最大的优点就是“位置、尺度、旋转不变量",也就是说在一定范围内旋转或是拉远拉近相机,可以检测出相同的特征点位置(Repeatability)以及提取出相同的特征向量(Scale & Rotation Invariant), 所以不同角度条件拍摄的图片才可以通过SIFT特征匹配。形成特征向量之后下一个问题就是如何匹配了。
2、图片匹配
找到所有匹配(也就是重叠)的图片部分,接连所有图片之后就可以形成一个基本的全景图了。实际上每两张图片之间只需要那么几个相对精准匹配的点就可以估算出这两张图像里的几何关系。最普遍的方式是用RANSAC(RANdom SAmple Consensus, 随机抽样一致),之后利用这些匹配的点来"估算单应矩阵"
RANSAC(RANdom SAmple Consensus, 随机抽样一致):RANSAC是一种迭代算法(Iteration Method),用来从观测数据中估算出数学模型的参数,此基础上便可以分离内群(Inliers)与离群(Outliers)数据。简单来说就是一般来 讲观测的数据里经常会出现很多噪音,比如说像SIFT匹配有时就会因为不同地方有类似的图案导致匹配错误。而RANSAC就是通过反复取样,也就是从整个 观测数据中随机抽一些数据估算模型参数之后看和所有数据误差有多大,然后取误差最小视为最好以及分离内群与离群数据。
所以该方法的作用就是可以把不正确的特征匹配结果剔除,获得正确的透视矩阵。同时也有一个比较明显的缺点:当匹配点的数量太多时,运行速度会很慢。
实验部分
1、实验数据和代码
实验数据:
# 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 = ['D:\\join\\' + str(i + 1) + '.sift' for i in range(5)]
imname = ['D:\\join\\' + str(i + 1) + '.jpg' for i in range(5)]
# extract features and match
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])
print(matches)
# visualize the matches (Figure 3-11 in the book)
for i in range(4):
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(0)
H_01 = homography.H_from_ransac(fp,tp,model)[0] #im 0 to 1
tp,fp = convert_points(1) #NB: reverse order
H_21 = homography.H_from_ransac(fp,tp,model)[0] #im 2 to 1
# warp the images
delta = 2000 # for padding and translation
im1 = array(Image.open(imname[0]), "uint8")
im2 = array(Image.open(imname[1]), "uint8")
im_01 = warp.panorama(H_01,im1,im2,delta,delta)
im1 = array(Image.open(imname[2]), "f")
im_21 = warp.panorama(H_21,im1,im_01,delta,delta)
figure()
imshow(array(im_21, "uint8"))
axis('off')
savefig("example5.png",dpi=300)
show()
2、实验结果和分析
1.失败案例
结果分析:
1.可以发现左侧有曝光痕迹和明显的分界线,右侧存在大块黑影区域
2.拼接图的交界处,因为光照色泽的原因使得交界处过度不理想,而对此的处理思路采用的是加权融合,在重叠部分由前一副图像慢慢过度到第二幅图像,即将图像的重叠区域的像素值是按一定的权值相加所合成的新的图像
3.右侧出现黑幕的原因是拍摄角度小,导致拼接后无法填满
2、成功案例
结果分析:
1.明明是同一个天气条件下拍摄的照片,拼接后的拼接交界处还是会有有明显的衔接痕迹,即存在边缘效应是由于相机和光照强度(光泽)的差异导致一副图像内部以及图像之间的亮度的不均匀,所以拼接后的图像会出现明暗交替的现象。
2.注意到左上角的黑色区域,可能是由于构造全景时候会进行透视变换,而透视变换的时候,由于透视变换是将一副图像通过投影矩阵投射到指定平面也就是结果图上,因此这样的空间变换,会使得投射到的图像发生畸变因而不能保证完全填满。
3、解决方法
1.关于图像拼接完成后可能会出现图像与图像之间过度不连续,即存在明显的拼接缝隙,拼接缝隙两侧的灰度变化很明显。这个问题可以用最小割最大流来解决。
最小割最大流:
找到这么一条切割线运用的是一个最大流的方法。下面的这个图结构,可以把它理解为图像的重叠部分,具体步骤就是:不断的找从S到E的一条路径,每找到一条路径,就让该路径上的所有边的权重减去这条路径上权重值最小边的权重,同时在这条路径上构造一个方向的有向边,权重值设置为减去的值,另外如果减完后权重为0,则删除这条边。算法一直循环知道无法找到S到E的路径借结束,最后切割那些为空或是流已满的边
2.针对图像拼接过程中,出现在同一天气同一时刻下拍摄的照片进行拼接,由于曝光参数不同等原因,会导致图像重叠区域的拼接处出现比较明显的边痕迹。这些边痕迹需要使用图像融合算法来消除,如multi-band blending,将两张图片进行融合,最终达到整体的亮度和颜色的一致性。
Multi-Band
Multi-Band能够达到比较好的融合效果,但是效率低,采用Laplacian(拉普拉斯)金字塔,通过对相邻两层的高斯金字塔进行差分,将原图分解成不同尺度的子图,对每一个之图进行加权平均,得到每一层的融合结果,最后进行金字塔的反向重建,得到最终融合效果过程。