全景图像拼接基本原理
图像全景拼接就是将多张图片(两两间存在一定的重叠部分,可以是不同时间、不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分辨率图像的技术。
也就是将两幅存在一定重合部分的图像,只需找出两张图片中相似的点 (至少四个,因为 homography 矩阵的计算需要至少四个点), 计算一张图片可以变换到另一张图片的变换矩阵 (homography 单应性矩阵),用这个矩阵把那张图片变换后放到另一张图片相应的位置 ,就是相当于把两张图片中定好的四个相似的点重合在一起。如此,就可以实现简单的全景拼接。
RANSAC算法
RANSAC 是“RANdom SAmple Consensus”(随机一致性采样)的缩写。该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵,RANSAC 基本的思想是,数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。
RANSAC 的标准例子:用一条直线拟合带有噪声数据的点集。简单的最小二乘在该例子中可能会失效,但是RANSAC 能够挑选出正确的点,然后获取能够正确拟合的直线。
算法基本思想及流程
基本思想描述如下:
A.首先,考虑一个最小抽样集的势为n的模型(n为初始化模型参数所需的最小样本数)和一个样本集P,集合P的样本数#§>n,从P中随机抽取包含n个样本的P的子集S初始化模型M;
B.余集SC=P\S中与模型M的误差小于某一设定阈值t的样本集以及S构成S*。S可认为是内点集,它们构成S的一致集;
C.若#(S)≥N,认为得到正确的模型参数,并利用集S采用最小二乘等方法重新计算新的模型M;重新随机抽取新的S,重复以上过程。
D.在完成一定的抽样次数后,若未找到一致集则算法失败,否则选取抽样后得到的最大一致集判断内外点,算法结束。
算法求解单应性矩阵
我们使用 RANSAC 算法来求解单应性矩阵,首先需要将下面模型类添加到
homography.py 文件中:
class RansacModel(object):
""" Class for testing homography fit with ransac.py from
http://www.scipy.org/Cookbook/RANSAC"""
def __init__(self,debug=False):
self.debug = debugdef fit(self, data):
""" 计算选取四个对应的单应性矩阵 """
# 将其转置,来调用H_from_points()计算单应性矩阵
data = data.T
#映射的起始点
fp = data[:3,:4]
# 映射的目标点
tp = data[3:,:4]
#计算单应性矩阵然后返回
return H_from_points(fp,tp)
def get_error( self, data, H):
""" 对所有的对应计算单应性矩阵,然后对每个变换后的点,返回相应的误差 """
data = data.T
#映射的起始点
fp = data[:3]
# 映射的目标点
tp = data[3:]
#变换fp
fp_transformed = dot(H,fp)
#归一化齐次坐标
for i in range(3):
fp_transformed[i] /= fp_transformed[2]
return sqrt( sum((tp-fp_transformed)**2,axis=0) )
可以看到,这个类包含 fit() 方法。该方法仅仅接受由ransac.py 选择的4个对应点对(data 中的前4个点对),然后拟合一个单应性矩阵。记住,4个点对是计算单应性矩阵所需的最少数目。
由于 get_error() 方法对每个对应点对使用该单应性矩阵,然后返回相应的平方距离之和,因此 RANSAC 算法能够判定哪些点对是正确的,哪些是错误的。在实际中,我们需要在距离上使用一个阈值来决定哪些单应性矩阵是合理的。
拼接图像
估计出图像间的单应性矩阵(使用 RANSAC 算法),现在我们需要将所有的图像扭曲到一个公共的图像平面上。通常,这里的公共平面为中心图像平面(否则,需要进行大量变形)。
一种方法是创建一个很大的图像,比如图像中全部填充 0,使其和中心图像平行,然后将所有的图像扭曲到上面。由于我们所有的图像是由照相机水平旋转拍摄的,因此我们可以使用一个较简单的步骤:将中心图像左边或者右边的区域填充0,以便为扭曲的图像腾出空间。
代码实现
# -*- coding: utf-8 -*-
from numpy import array
from numpy.ma import vstack, dot
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from VC.SIFT import sift
featname = ['C:/Users/Administrator/PycharmProjects/pythonProject/VC/Joint/jmu1/' + str(i + 1) + '.sift' for i in range(30,33)]
imname = ['C:/Users/Administrator/PycharmProjects/pythonProject/VC/Joint/jmu1/' + str(i + 1) + '.jpg' for i in range(30,33)]
l = {}
d = {}
for i in range(3):
sift.process_image(imname[i], featname[i])
l[i], d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(2):
matches[i] = sift.match(d[i + 1], d[i])
for i in range(2):
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)
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
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
delta = 1000 # 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)
figure()
imshow(array(im_02, "uint8"))
axis('off')
show()
运行结果
注意:图像由右往左进行编号
原始图像:
拼接后:
实验小结
总结一下图像拼接整体流程
1.根据给定图像集, 实现特征匹配
2.通过匹配特征计算图像之间的变换结构
3. 利用图像变换结构, 实现图像映射
4. 针对叠加后的图像, 采用APAP算法对齐特征点
5.通过图割方法, 自动选取拼接缝
6.根据multi-band bleing策略实现融合
总结实验出现的一些问题
首先是出现了——图像拼接 ModuleNotFoundError: No module named ‘matplotlib.delaunay’ 的错误
查找资料发现将 import matplotlib.delaunay as md 改成 from scipy.spatial import Delaunay即可
其次是在拼接禹州以及建发楼出现了——ValueError:didnotmeet fit acceptance criteria
查找资料可能是因为拍摄的图像水平落差比较大导致两张图没有共同点,但是之前sift匹配建发楼也失败了,抑或是因为树木的遮挡,以及大片树叶对图片角点选取产生的噪声影响较大。还需多反思总结,继续努力。