python计算机视觉--全景图像拼接

目录

一、RANSAC算法

1.1 RANSAC算法简介

1.2 算法基本思想和流程

1.3 RANSAC求解单应性矩阵

二、图像映射与全景拼接

2.1 简介

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

2.3 将第二张图像叠加到第一张图像的坐标系中

2.4 图像映射的流程

2.5 图像拼接流程

三、全景拼接测试

3.1 代码展示

3.2 结果展示

3.2.1 场景一

3.2.2 场景二

3.2.3 场景三

3.3 实验总结

四、遇到的错误


一、RANSAC算法

1.1 RANSAC算法简介

RANSAC (随机一致性采样)是一种迭代算法,该算法从一组包含“外点(outlier)”的观测数据中估计数学模型的参数。“外点”指观测数据中的无效数据,通常为噪声或错误数据,比如图像匹配中的误匹配点和曲线拟合中的离群点。与“外点”相对应的是“内点(inlier)”,即用来估计模型参数的有效数据。因此,RANSAC也是一种“外点”检测算法。此外,RANSAC算法是一种非确定算法,它只能在一定概率下产生可信的结果,当迭代次数增加时,准确的概率也会增加。RANSAC算法是用来找到正确模型来拟合带有噪声数据的迭代方法。

1.2 算法基本思想和流程

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

(1)随机采样K个点,K是求解模型参数的最少点个数(对于直线拟合来说就是两个点(两点确定一条直线),对于拟合圆就是3个点)

(2)使用采样出的k个点估计模型参数,得到数据模型;

(3)将所有数据带入这个模型,计算出“内点”的数目;(累加在一定误差范围内的适合当前迭代得出的模型的数据);

(4)比较当前模型和之前推出的最好的模型的“内点“的数量,记录最大“内点”数的模型参数和“内点”数;

(5)重复1-4步,直到迭代结束或者当前模型已经足够好了(“内点数目大于一定数量”)。

1.3 RANSAC求解单应性矩阵

单应性变换:是将一个平面内的点映射到另一个平面内的二维投影变换。

RANSAC 循环:

(1)随机选择四对匹配特征

(2)根据DLT计算单应矩阵 H (唯一解)

(3)对所有匹配点,计算映射误差ε= ||p i ’, H p i ||

(4)根据误差阈值,确定inliers(例如3-5像素)

(5)针对最大inliers集合,重新计算单应矩阵 H。

二、图像映射与全景拼接

2.1 简介

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

        通常,这里的公共平面为中心图像平面。一种方法是创建一个很大的图像,比如将图像中全部填充0,使其和中心图像平行,然后将所有的图像扭曲到上面。由于我们所有的图像是由照相机水平旋转拍摄的,因此我们可以使用一个较简单的步骤:将中心图像左边或者右边的区域填充为0,以便为扭曲的图像腾出空间。
图像拼接的几何原理:全景融合的 3D 几何解释,图像被投影到共同的拼接平面上(同一坐标系), 在拼接平面上实现全景融合。在拼接的应用中,其实可以简化理解为 2D图像的变换,叠加过程。

基础流程:

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

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

(1)变换类型选择

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

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

图像滤波: 改变图像的像素点取值范围

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

 (3)2D 图像变换类型

python计算机视觉--全景图像拼接_第1张图片(4)参数求解

针对不同问题,需要多少对匹配特征才能计算出模型参数。

python计算机视觉--全景图像拼接_第2张图片

对于平移变换,有两个未知参数,需要一对点可以列出两个方程求解。 

 2.3 将第二张图像叠加到第一张图像的坐标系中

        给定变换模型 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.4 图像映射的流程

(1)针对两张/多张图像提取特征
(2)特征匹配
(3)根据图像变换特点,选取合适的变换结构
(4)根据DLT等方法计算变换结构
(5)采用正向/逆向映射,利用插值方式实现图像映射变换

2.5 图像拼接流程

(1)根据给定图像/集, 实现特征匹配

(2)通过匹配特征计算图像之间的变换结构

(3)利用图像变换结构, 实现图像映射

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

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

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

三、全景拼接测试

3.1 代码展示

# -*- coding: utf-8 -*-
"""
@author: RRJ
@software: PyCharm
@file: vistademo1.py
@time: 2022/4/9 17:37
"""

import matplotlib.pyplot as plt
from numpy import *
from PIL import Image

from newPCV.geometry import homography, warp
from newPCV.Localdescriptors import sift
from newPCV.tools import reviseSize

# 批量修改图像尺寸
path = "D:\\python\\RRJ\\pycharmproject\\全景拼接\\vistaImg\\Imgdemo3\\"
reviseSize.startAction(path,2000,1800)  # 设置图像的宽、高

# 设置数据文件夹的路径
featname = [path + str(i + 1) + '.sift' for i in range(5)]
imname = [path + str(i + 1) + '.jpg' for i in range(5)]

# 提取特征并匹配使用sift算法
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])

# 可视化匹配
for i in range(4):
    im1 = array(Image.open(imname[i]))
    im2 = array(Image.open(imname[i + 1]))
    plt.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]  # im1 到im2 的单应性矩阵

fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]  # im0 到im1 的单应性矩阵

tp, fp = convert_points(2)  # 注意:点是反序的
H_32 = homography.H_from_ransac(fp, tp, model)[0]  # im3 到im2 的单应性矩阵

tp, fp = convert_points(3)  # 注意:点是反序的
H_43 = homography.H_from_ransac(fp, tp, model)[0]  # im4 到im3 的单应性矩阵

# 扭曲图像
delta = 2000  # 用于填充和平移

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)

plt.figure()
plt.imshow(array(im_42, "uint8"))
plt.axis('off')
plt.show()

批量修改图像尺寸:reviseSize.py

from PIL import Image
import os.path
import glob

def convertjpg(jpgfile, outdir, width=640, height=480):
    img = Image.open(jpgfile)
    try:
        new_img = img.resize((width, height), Image.BILINEAR)
        new_img.save(os.path.join(outdir, os.path.basename(jpgfile)))
    except Exception as e:
        print(e)

def startAction(path,width,height):
    for jpgfile in glob.glob(path + "*.jpg"):
        convertjpg(jpgfile, path,width,height)
    print('修改完成')



3.2 结果展示

3.2.1 场景一

光线好,不同角度,不同景深(原图像集):

python计算机视觉--全景图像拼接_第3张图片

SIFT匹配:

python计算机视觉--全景图像拼接_第4张图片

python计算机视觉--全景图像拼接_第5张图片

全景拼接结果:

python计算机视觉--全景图像拼接_第6张图片

可以看到,效果非常差,完全看不出是个啥,同时没有注意到图像将图片的顺序进行颠倒,二球速度很慢,因此修改过之后再次测试。

python计算机视觉--全景图像拼接_第7张图片 修改之后可以看到效果好了一些,有明显的拼接缝,但是右边多出一部分,经过检查发现图像的排列顺序出了问题导致的,经修改后再此测试:

python计算机视觉--全景图像拼接_第8张图片这次经过比较满意,中间有一条较为明显的拼接缝,五张图像拼接基本还原了一幅全景图。

3.2.2 场景二

光线好,不同角度,相同景深(原图像集):

python计算机视觉--全景图像拼接_第9张图片

SIFT特征匹配:

python计算机视觉--全景图像拼接_第10张图片

全景拼接结果:

 python计算机视觉--全景图像拼接_第11张图片

 同样也是得到了很好的拼接,但是由于对原4000x3000的图像缩放成1080x810,所以得到的结果图像较小,拼接缝看不清。

3.2.3 场景三

光线好,相同角度,相同景深(原图像集):

这组图像是在一张广角拍摄的图像依次截取出来的,通过实验拼接结果和原图像对比测试算法的全景拼接效果:

python计算机视觉--全景图像拼接_第12张图片

 SIFT特征匹配:

python计算机视觉--全景图像拼接_第13张图片

全景拼接结果:

python计算机视觉--全景图像拼接_第14张图片

 原全景图像:

python计算机视觉--全景图像拼接_第15张图片

 结果分析:此场景下拼接结果可以说十分完美,甚至到不到拼接缝,可以说和原图一致,同样也说明如果一组图片是按照一条水平线稳定拍摄,再加上角度偏差小,景深相同,SIFT特征匹配可以拼接处一幅完美的全景图像。但仍有一些缺点是运行效率较慢,该场景图像大小是2000X1800,相较1080是慢了一倍,但是胜在效果理想(推荐2000x1800)。

3.3 实验总结

(1)图像的顺序极其重要,同时大小要一致。图片全景拼接把几张图片拼接在一起,先选取一个中间图像作为中心图像,也就是希望将其他图像变成的图像,由于匹配是从最右边的图像开始匹配,但是我们习惯是从左向右的顺序排列,所以需要注意将图片的顺序进行颠倒改变其命名顺序(即由从左向右1,2,3,4,5的存放顺序变为5,4,3,2,1),使得从左边图像开始扭曲,将所有图像扭曲到一个公共的图像平面上。

(2)图像的像素大小对运行效率影响较大,在不改变图像的的情况下使用2000x1800感觉还可以接受。

(3)如果角度偏差,景深不同则效果较差,相反,说明如果一组图片是按照一条水平线稳定拍摄,再加上角度偏差小,景深相同,得到的全景拼接图像效果最好。

四、遇到的错误

1.import matplotlib.delaunay as md中,在安装delaunay时报错:

python计算机视觉--全景图像拼接_第16张图片


解决方法:

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

import matplotlib.delaunay as md

改成下面的

 from scipy.spatial import Delaunay  

(2)把红色方框的里面的代码进行替换成黄色框中的:

python计算机视觉--全景图像拼接_第17张图片

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

2.'axis' 是 sum() 的无效关键字参数:

解决方法:删除axis=0再运行。


参考文章

(1)(2条消息) python计算机视觉-图像处理基础章节第三章之全景图像拼接_我超爱Debug的博客-CSDN博客

(2)(2条消息) 计算机视觉4—全景图像拼接_w.wyue的博客-CSDN博客

你可能感兴趣的:(python,图像处理,计算机视觉,pycharm)