计算机视觉4—全景图像拼接

目录

1.RANSAC简介

1.1 算法基本思想和流程

1.2 RANSAC 求解单应矩阵

2.图像映射与全景拼接

2.1 计算第二张图像与第一张图像之间的变换关系

2.2 将第二张图像叠加到第一张图像的座标系中

2.3. 图像映射小结

2.4 图像拼接整体流程

3.实验分析

3.1 代码实现

3.2 实验分析

    3.2.1 实验场景一

    3.2.2 实验场景二

3.3 实验总结

4.错误


1.RANSAC简介

RANSAC(Random Sample Consensus)随机采样一致算法是根据一组包含异常数据的样本数据集,计算出数据的数学模型参数,得到有效样本数据的算法。

1.1 算法基本思想和流程

RANSAC是通过反复选择数据集去估计出模型,一直迭代到估计出认为比较好的模型。具体的实现步骤可以分为以下几步:

  1. 选择出可以估计出模型的最小数据集;(对于直线拟合来说就是两个点,对于计算Homography矩阵就是4个点)
  2. 使用这个数据集来计算出数据模型;
  3. 将所有数据带入这个模型,计算出“内点”的数目;(累加在一定误差范围内的适合当前迭代推出模型的数据)
  4. 比较当前模型和之前推出的最好的模型的“内点“的数量,记录最大“内点”数的模型参数和“内点”数;
  5. 重复1-4步,直到迭代结束或者当前模型已经足够好了(“内点数目大于一定数量”)。

1.2 RANSAC 求解单应矩阵

RANSAC 循环:

  1. 随机选择四对匹配特征
  2. 根据DLT计算单应矩阵 H (唯一解)
  3. 对所有匹配点,计算映射误差ε= ||p i ’, H p i ||
  4. 根据误差阈值,确定inliers(例如3-5像素)
  5. 针对最大inliers集合,重新计算单应矩阵 H

 

2.图像映射与全景拼接

首先是在连续图像对间使用SIFT特征寻找匹配对应点对,SIFT是具有较强稳健性的描述子,能够比其他描述子产生更少的错误点,但是该方法仍不是很完美;使用RANSAC算法估计出图像间的单应性矩阵,判定哪些点对是正确的,哪些点对是错误的,即使用一个阈值来决定哪些单应性矩阵是合理的;然后将所有的图像扭曲到一个公共的图像平面上。

基于SIFT特征匹配的RNASAC在上一篇博客也有相应实验的叙述,可参见博客SIFT理解与应用

通常,这里的公共平面为中心图像平面。一种方法是创建一个很大的图像,比如将图像中全部填充0,使其和中心图像平行,然后将所有的图像扭曲到上面。由于我们所有的图像是由照相机水平旋转拍摄的,因此我们可以使用一个较简单的步骤:将中心图像左边或者右边的区域填充为0,以便为扭曲的图像腾出空间。

图像拼接的几何原理:全景融合的 3D 几何解释,图像被投影到共同的拼接平面上(同一座标系), 在拼接平面上实现全景融合。在拼接的应用中,其实可以简化理解为 2D图像的变换,叠加过程。

基础流程
① 针对某个场景拍摄多张/序列图像
② 计算第二张图像与第一张图像之间的变换关系
③ 将第二张图像叠加到第一张图像的座标系中
④ 变换后的融合/合成
⑤ 在多图场景中,重复上述过程

2.1 计算第二张图像与第一张图像之间的变换关系

(1)变换类型选择

将两幅图像叠加在一起,选择需要采用模型,如位移、旋转、尺度大小、仿射和透视等。

(2)2D 图像变换原理:

图像滤波: 改变图像的像素点取值范围g\left ( x \right )=h\left ( f\left ( x \right ) \right )

图像变换: 改变图像的座标取值范围g\left ( x \right )=f\left ( h\left ( x \right ) \right )

(3)2D 图像变换类型

  #DoF Preserves Icon
平移 {x}'=x+t,t=\left (t_{_{x}},t_{y} \right ) 2 orientation  

旋转+平移 (刚体)

{x}'=Rx+t

R=\begin{bmatrix} cos\theta &-sin\theta \\sin\theta & cos\theta \end{bmatrix},t=\left ( \left t_{x} ,t_{y}\right )

3 lengths

相似

{x}'=sRx+t=\begin{bmatrix} a & -b & t_{x}\\ b & a & t_{y} \end{bmatrix}\begin{pmatrix} x\\ y\\ 1 \end{pmatrix}

4 angles

仿射

 

{x}'=\begin{bmatrix} a_{00} & a_{01} & a_{02}\\ a_{10} & a_{11} & a_{12} \end{bmatrix}\begin{pmatrix} x\\ y\\ 1 \end{pmatrix}
6 parallelism

单应性变换

 

{x}'=Hx

\begin{bmatrix} w{x}'\\ w{y}'\\ w \end{bmatrix}=\begin{bmatrix} h_{00} & h_{01} & h_{02}\\ h_{10} & h_{11} & h_{12}\\ h_{20} & h_{21} & h_{22} \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

8 straight lines

(4)参数求解:平移问题、仿射变换和单应性变换等

2.2 将第二张图像叠加到第一张图像的座标系中

2D 图像变换

给定变换模型 x'= h(x) ,以及输入图像f(x), 根据f(x)计算变换后的图像 g(x')= f(h(x))。前向映射:对于 f(x) 中的每个像素 x,根据变换模型计算相应的映射座标x' = h(x),并将x的像素值赋给 g(x')。逆向映射:对于 g(x')中的每个像素 x',根据变换模型计算相应的映射座标 x = h -1 (x'),并将x的像素值赋给g(x')。

2.3. 图像映射小结

 流程:
① 针对两张/多张图像提取特征
② 特征匹配
③ 根据图像变换特点,选取合适的变换结构
④ 根据DLT等方法计算变换结构
⑤ 采用正向/逆向映射,利用插值方式实现图像映射变换

2.4 图像拼接整体流程

  1. 根据给定图像/集, 实现特征匹配
  2. 通过匹配特征计算图像之间的变换结构
  3. 利用图像变换结构, 实现图像映射
  4. 针对叠加后的图像, 采用APAP算法对齐特征点
  5. 通过图割方法, 自动选取拼接缝
  6. 根据multi-band bleing策略实现融合

(1)针对叠加后的图像, 采用APAP算法对齐特征点

对两张图片进行SIFT特征匹配, 然后对SIFT特征匹配得到的特征点使用 RANSAC算法进行去除错误的特征点对。筛选后的特征点基本能够一一对应。使用DLT算法,将剩下的特征点对进行透视变换矩阵的估计。因为得到的透视变换矩阵是基于全局特征点对进行的,即一个刚性的单应性矩阵完成配准。为提高配准的精度,Apap将图像切割成无数多个小方块,对每个小方块的变换矩阵逐一估计。非常依赖于特征点对。若图像高频信息较少,特征点对过少,配准将完全失效,并且对大尺度的图像进行配准,其效果也不是很好,一切都决定于特征点对的数量。

(2)通过图割方法, 自动选取拼接缝

寻找代价最小的分割,典型算法是最小割最大流算法,将图内带权值的看作带有流量值的管道,将最大量水从源点送到汇点。

(3)根据multi-band bleing策略实现融合

融合目的在于拼缝消除, Multi-Band能够达到比较好的融合效果,但是效率低,采用Laplacian(拉普拉斯)金字塔,通过对相邻两层的高斯金字塔进行差分,将原图分解成不同尺度的子图,对每一个之图进行加权平均,得到每一层的融合结果,最后进行金字塔的反向重建,得到最终融合效果过程。
 

3.实验分析

3.1 代码实现

# -*- coding: utf-8 -*-
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift

featname = ['data/pic1/c' + str(i + 1) + '.sift' for i in range(3)]
imname = ['data/pic1/c' + str(i + 1) + '.jpg' for i in range(3)]

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()

3.2 实验分析

3.2.1 实验场景一

针对固定点位拍摄多张图片,以中间图片为中心

原图

 

特征匹配图

 

拼接全景图

小结:

 从该全景拼接图可以看出左边的拼接效果较好,虽然因图片曝光度的不同存在一定的边缘效应,但特征点能相对应,两图的拼接没有扭曲或者其他的问题;而全景拼接图的右边,虽然实现了拼接在一起,但是右边的图片被变大扭曲了,边缘楼房明显倾斜变形,且越右边变形的越厉害,右边的图片整个都被拉伸扭曲变形。从SIFT特征匹配的结果来看,两两匹配得到的特征点很多,匹配效果也很好,但是最后的全景拼接图只有左边能够实现较好的点对点拼接。

 

3.2.2 实验场景二

针对同一场景(选取视差变化大的场景,也就是有近景目标),更换拍摄位置

原图

 

特征匹配图

 

拼接全景图

小结:

该全景拼接图相对于实验(1)得到的全景拼接图效果要好,左边拼接依然存在曝光度不同导致的边缘效应,但是图片没有被大幅度扭曲,只是在花盆左后面旁边的瓷砖拼接有点扭曲,两张图片过渡不连续存在的拼接缝隙;此时的全景拼接图没有再出现右边扭曲变形的情况,反而因为曝光度的相似性,特征点能一一对应得到了较好的拼接效果。

3.3 实验总结

  • 图片全景拼接把几张图片拼接在一起,先选取一个中间图像作为中心图像,也就是希望将其他图像变成的图像,由于匹配是从最右边的图像开始计算,所以需要注意将图片的顺序进行颠倒(即由1,2,3,4,5的存放顺序变为5,4,3,2,1),使得从左边图像开始扭曲,将所有图像扭曲到一个公共的图像平面上。
  • 图像右边的黑色区域可以通过减小代码中的delta相应减小,但不能设置的过小,delta值太小会使得图片不能完整呈现,即可能存在部分图片被截取了。
  • 两个实验场景我选取的都是建筑物较多的室外场景,背景比较复杂,但也因为繁复的建筑物背景可以检测出较多的特征点,总结两个实验都是左边的匹配效果较佳,即特征点能够较好的实现一一对应。
  • 曝光度的不同容易导致拼接全景图存在边缘效应。

4.错误

错误提示为ImportError:  No module named delaunay。

解决:

1.先进入到进入到python目录下的Lib\site-packages\PCV\geometry\warp.py,把黑色方框中的 

import matplotlib.delaunay as md

  改为红色方框的  

 from scipy.spatial import Delaunay  

 

2.把红色方框的...triangulate_points(x,y)里面的代码替换成

tri = Delaunay(np.c_[x,y]).simplices

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章