python-全景拼接图像

文章目录

  • 一、全景拼接介绍
    • 1.1 流程:
    • 1.2 sift匹配与RANSAC
    • 1.3 图像配准
    • 1.4 最大流最小割
    • 1.5 图像融合-blending
  • 二、实验与分析
    • 2.1 近景拼接
    • 2.2 室外远景拼接
    • 2.3 视差大的图像拼接
    • 2.4 实验代码:
  • 三、实验总结

一、全景拼接介绍

在同一位置(即图像的照相机位置相同)拍摄的两幅或者多幅图像是单应性相关的。我们经常使用该约束将很多图像缝补,来拼成一个大的图像来创建全景图像。

1.1 流程:

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

1.2 sift匹配与RANSAC

sift匹配与RANSAC在上一个博客有详细的介绍过,SIFT算法的描述子稳健性很强,比Harris角点要来得精确,但是它的匹配正确率也并不是百分百的,会受到一些噪声点的干扰,有时就会因为不同地方有类似的图案导致匹配错误。那么RANSAC算法便是用来找到正确模型来拟合带有噪声数据的迭代方法。RANSAC通过反复取样,也就是从整个观测数据中随机抽一些数据估算模型参数之后看和所有数据误差有多大,然后取误差最小视为最好以及分离内群与离群数据。基本的思想是,数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。
举个简单的例子,直线的拟合便是RANSAC的一个标准化体现:

  • 在所有的点中随机选择两个点。
  • 根据这两个点作一条直线。
  • 设定一个阈值,计算在这条线上的点的数量,记为inliners。
  • 根据最大的inliners那条线进行后续计算。

同理,RANSAC算法可以应用到其它模块中,例如用于图像变换的单应性矩阵的计算。
在拼接的过程中,通过将响速和单应矩阵H相乘,然后对齐次坐标进行归一化来实现像素间的映射。通过查看H中的平移量,我们可以决定应该将该图像填补到左边还是右边。当该图像填补到左边时,由于目标图像中点的坐标也变化了,所以在“左边”情况中,需要在单应矩阵中加入平移。

1.3 图像配准

图像配准是对图像进行变换,使变换后的图像能够在很好的拼接在上一张图片的坐标系。
为了能够进行图像对比和更精细的图像分析,图像配准是一步非常重要的操作。
图像配准的方法可以参考这篇博客:https://blog.csdn.net/gaoyu1253401563/article/details/80631601

1.4 最大流最小割

最大流
给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow)。
最小割
割是网络中定点的一个划分,它把网络中的所有顶点划分成两个顶点集合S和T,其中源点s∈S,汇点t∈T。记为CUT(S,T),满足条件的从S到T的最小割(Min cut)。

python-全景拼接图像_第1张图片
可以计算出对于这两种情况净流f(S,T)等于19。
一个直观的解释是:根据网络流的定义,只有源点s会产生流量,汇点t会接收流量。因此任意非s和t的点u,其净流量一定为0,也即是Σ(f(u,v))=0。而源点s的流量最终都会通过割(S,T)的边到达汇点t,所以网络流的流f等于割的静流f(S,T)。

对于一个网络流图G=(V,E),其中有源点s和汇点t,那么下面三个条件是等价的:

  • 流f是图G的最大流
  • 残留网络Gf不存在增广路
  • 对于G的某一个割(S,T),此时f = C(S,T)
  • 找到最小割后,沿最小割进行分割,可以得到比较好的效果。
    例如:
    python-全景拼接图像_第2张图片

1.5 图像融合-blending

图像拼接之后可以发现,在拼接的交界处有明显的衔接痕迹,存在边缘效应,因为光照色泽的原因使得图片交界处的过渡很糟糕,所以需要特定的处理解决这种不自然。那么这时候可以采用blending方法。multi-band blending是目前图像融和方面比较好的方法。
原理:
1.建立两幅图像的拉普拉斯金字塔
2.求高斯金字塔(掩模金字塔-为了拼接左右两幅图像)因为其具有尺度不变性
3. 进行拼接blendLapPyrs() ; 在每一层上将左右laplacian图像直接拼起来得结果金字塔resultLapPyr
4.重建图像: 从最高层结果图
将左右laplacian图像拼成的resultLapPyr金字塔中每一层,从上到下插值放大并和下一层相加,即得blend图像结果(reconstructImgFromLapPyramid)

且我们可以将拉普拉斯金字塔理解为高斯金字塔的逆形式。
python-全景拼接图像_第3张图片
python-全景拼接图像_第4张图片
python-全景拼接图像_第5张图片

二、实验与分析

2.1 近景拼接

2.1.1 实验结果:
python-全景拼接图像_第6张图片python-全景拼接图像_第7张图片python-全景拼接图像_第8张图片python-全景拼接图像_第9张图片
拼接结果:
python-全景拼接图像_第10张图片
2.1.2 分析:
上面实验用的五张图是比较近距离的场景,由匹配的结果我们可以发现每两张图像间的匹配还是比较准确的,但是在拼接的结果图里看到拼接的时候发生很大的错位和明显的明暗差距。在观察图片的时候发现最左边和最右边的图光线是比较亮的,因为距离也比较近,每张图相似的部分很多,最终的拼接结果图中显示的几乎都是最左边和最右边的图的拼接,中间比较暗的三张图片形成明显的接缝。

2.2 室外远景拼接

2.2.1实验结果:
python-全景拼接图像_第11张图片python-全景拼接图像_第12张图片python-全景拼接图像_第13张图片python-全景拼接图像_第14张图片
拼接结果:
python-全景拼接图像_第15张图片
2.2.2 分析:
这是站在同一位置拍摄的不同角度的五张图片,最后一张匹配图匹配的结果不太好,可以看到杆子的顶部匹配到了另一幅图中的房顶,拼接的视觉效果也不太好,拼接线太明显,同一位置左右拍的时候每张图光线差比较大,远处的物体也能看到断层的感觉。

2.3 视差大的图像拼接

2.3.1 实验结果:
python-全景拼接图像_第16张图片python-全景拼接图像_第17张图片python-全景拼接图像_第18张图片python-全景拼接图像_第19张图片
拼接结果:
python-全景拼接图像_第20张图片
2.3.2 分析:
选用的图是有远近视差的场景,近距离的树枝加上桥后面的树木的远景。我们也可以看到两两图之间的匹配是比较良好的,一一对应。选用的这五张图光照是比较均匀的,每张图的曝光差不多,拼接结果刚一看右侧部分的衔接很好,但是在最左边的桥下影子部分可以看到,影子也是有错开的。

2.4 实验代码:

# -*- coding: utf-8 -*-
from pylab import *
from numpy import *
from PIL import Image
import numpy as np

np.seterr(divide='ignore', invalid='ignore')

# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift

# set paths to data folder
featname = ['C:/Users/asus/Pictures/window/RANSAC/pinjie3/' + str(i + 1) + '.sift' for i in range(5)]
imname = ['C:/Users/asus/Pictures/window/RANSAC/pinjie3/' + 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 = 160  # 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("example1.png", dpi=300)
show()

三、实验总结

(1)拍摄光照明暗变化明显的话会导致拼接结果在拼接处很明显的,出现断层现象。
(2)图片像素太大的时候,运行五张图片用的时间很长,电脑还可能会死机,因此可以拍摄照片之后适当的修改一下图片的像素大小,提高运行效率。

问题:
在进行室内这五张图的拼接时会报错:did not meet fit acceptance criteria 。现在还没找到原因
python-全景拼接图像_第21张图片

你可能感兴趣的:(计算机视觉)