在我之前的博文中,提到了sift特征点的匹配和图像之间的仿射变换。这次所提到的全景图像的拼接,就有利用到这两个知识。全景图像拼接,顾名思义是在一个相同的位置,拍摄了多幅图像,这些图像中存在这与其他图像重合的部分,我们利用算法来将这些图像拼接成为一张全景的图像。
两张图像要能拼接在一起成为一张图像,就需要这两张图像中存在有重合的部分。通过这些重合的部分使用sift特征点匹配的算法,来寻找到重合部分的特征点。需要注意的是,虽然sift算法比Harris角点的效果更好,但是也会出现错误点,并非完美的匹配方法。
在找到特征点对之后,因为上文提到sift并非所有匹配点都是正确的,这里我们用到了RANSAC这个方法。这个方法的作用是找到一个合理的模型来描述正确的数据并且尽量忽视噪点的影响。
这两张图是一个很好的例子,在这个平面中,大部分的正确数据是集中在某个区域。而且周围存在噪声点。RANSAC算法随机选择两个点来画出直线,计算这个直线能经过多少点或者多少个点到直线的距离小于阈值,根据点的多少来确定正确模型。这样就能很好的画出正确的模型并且排除噪声点的干扰。
这样我们也能通过RANSAC的方法来确定两张图像的单应性矩阵。因为单应性矩阵的确定需要最少四个点对。通过RANSAC我们可以选择到合理的点对,来计算单应性矩阵。
单应性矩阵是要实现图像拼接重要方法。它表示了两张图象之间的对应特征点的变换关系。有了这个关系,我们就可以实现图像正确的拼接到另一张图像上。
# 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 = ['E:/Py_code/photo/thrid_2/cjg' + str(i + 1) + '.sift' for i in range(5)]
imname = ['E:/Py_code/photo/thrid_2/cjg' + 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])
# 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(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
tp, fp = convert_points(3) # NB: reverse order
H_43 = homography.H_from_ransac(fp, tp, model)[0] # im 4 to 3
# 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)
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')
savefig("example5.png", dpi=300)
show()
这是五张集美大学中山纪念馆的拍摄图像拼接的结果。这五张图像,我以中山纪念馆为中点,对称的选取大概30°-40°左右拍摄另外四张图片。发现拼接结果比之前没有明确拍摄目的的图像拼接的结果好很多。
室内的近景拍摄宿舍图像拼接出来的效果一直存在问题,我认为是宿舍的近景拍摄的距离过近,导致在拼接时候边缘的图像会过分的拉伸而使拼接出现问题。在宿舍中,衣柜与书架由于颜色和部分构造基本相同,这也是问题之一。于是使用集美大学的西苑食堂的照片来进行图像拼接。
对于景深落差大,我自己的个人理解为,当我们拍摄图像时,我们所需要拍摄的目标,和我们的相机之间存在某个物体,比如雕塑,停放的车辆等。这样我们在旋转拍摄角度的时候,物体和目标之间在相机偏差的位置就会有变化,这样导致在拍摄的图像中目标和物体的位置就会有差距,这样的图像做全景图像的拼接就会有重影产生。
比如这样集美大学中陈嘉庚先生的雕塑和远处的尚大楼。因为在拍摄的图像中,嘉庚先生的雕塑和尚大楼之间的差距都有差距,导致出现了重影的现象。重影的消除,我们可以采用图割法或者最大流最小割
假设有两张图像A,B。A,B之间拼接之后存在着重影的现象。那我们把A,B重合的地方分别取出来为PartA,和PartB。把PartA的像素减去相应位置PartB的像素,得到差值作为一个矩阵,再将矩阵中的值和相邻的值相减作为连接点之间的权值。这样就知道当权值的地方说明这里两张图像有很大的不同,所以我们找到一条切割路径使权值之和最小。这样以这条路径作为图像的拼接点,就会使图像消除重影。
1.实验中,拼接图像大小会影响的程序的速度,所以在拍摄图像上传到电脑时候尽量别上传原图,如果上传原图最好能进行压缩。
2.代码中有个参数delta。这个参数是针对你拍摄图像时,你相对平移的距离的变量,当你拍摄近景时候,这个参数尽量该小,远景相反。
3.dpi是图像精细度的变量,可以通过修改来改变图像的分辨率,数值越大表示图像越精细,即分辨率越高。