目录
1. 图像拼接的简介
1.1 图像拼接的基础流程
是将多个重叠的图像对齐成一个大的组合,它代表了一个3D场景的一部分。
拼接可以看做是场景重建的一种特殊情况,其中图像仅通过平面单应性进行关联。
图像拼接在运动检测和跟踪,增强现实,分辨率增强,视频压缩和图像稳定等机器视觉领域有很大的应用。
1. 针对某个场景拍摄多张/序列图像
2. 计算第二张图像与第一张图像之间的变换关系
3. 将第二张图像叠加到第一张图像的坐标系中
4. 变换后的融合/合成
5. 在多图场景中,重复上述过程
1.2 图像拼接的数学原理
在图像拼接的过程中,我们需要将两幅图像叠加在一起。我们采用的模型大概可以归类为以下几种:位移、旋转、尺度(大小)、仿射、透视。
在方法上,我们会采用单应性变换的方式来处理图像。单应性变换,可简单理解为用来描述物体在世界坐标系和像素坐标系之间的位置映射关系,对应的变换矩阵称为单应性矩阵。
2. 实现方法
2.1 RANSAC方法
RASAC算法是一种学习技术,通过随机观察样本数据估计模型参数。给定一个数据集包含内点和外点,RASAC使用投票方案找到优化的拟合结果。数据集中的数据元素被用来投票给一个或多个模型。这个投票方案的实施基于两个假设:噪声特征不会对任何单一模型(少数异常值)进行一致性投票;有足够的特征得到好的模型(丢失数据少)。
RANSAC求解单应矩阵
RANSAC loop:
1. 随机选择四对匹配特征
2. 根据直接线性变换解法DLT计算单应矩阵 H ( 唯一解)
3. 对所有匹配点,计算映射误差ε= ||pi ’, H pi ||
4. 根据误差阈值,确定inliers(例如3-5像素)
5. 针对最大inliers集合,重新计算单应矩阵 H
2.2 Multi-Band Blending策略
在找完拼接缝后,由于图像噪声、光照、曝光度、模型匹配误差等因素,直接进行图像合成会在图像重叠区域的拼接处出现比较明显的边痕迹。这些边痕迹需要使用图像融合算法来消除。这里介绍一种方法—multi-band bleing。采用的方法是直接对带拼接的两个图片进行拉普拉斯金字塔分解,后一半对前一半进行融合。首先计算当前待拼接图像和已合成图像的重叠部分。并对图像A、B 重叠部分进行高斯金字塔和拉普拉斯金字塔分解。
3. 代码实现
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
np.seterr(invalid='ignore')
"""
This is the panorama example from section 3.3.
"""
# 设置数据文件夹的路径
featname = ['D:\\vscode\\python\\.vscode\\jpg\\' + str(i + 1) + '.sift' for i in range(3)]
imname = ['D:\\vscode\\python\\.vscode\\jpg\\' + str(i + 1) + '.jpg' for i in range(3)]
# 使用sift算法提取特征并匹配
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)
# 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
# 估计单应性矩阵
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 = 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)
figure()
imshow(array(im_02, "uint8"))
axis('off')
show()