图像处理之图像拼接(python)

一、算法目的
在同一位置拍摄两张以上图片,这些图片是单应性相关的,即图片之间有相同的拍摄区域。基于此将图片进行缝补,拼成一个大的图像来创建全景图像。

二、基本原理
要实现两张图片的简单拼接,其实只需找出两张图片中相似的点 (至少四个,因为 homography 矩阵的计算需要至少四个点), 计算一张图片可以变换到另一张图片的变换矩阵 (homography 单应性矩阵),用这个矩阵把那张图片变换后放到另一张图片相应的位置 ,就是相当于把两张图片中定好的四个相似的点重合在一起。如此,就可以实现简单的全景拼接。

实现步骤

  1. 读入连续图片并使用SIFT特征查找匹配对应点对
import sift
#sift程序应与运行图片在同一文件夹下
featname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.sift' for i in range(5)] 
imname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.jpg' for i in range(5)]
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])

图像处理之图像拼接(python)_第1张图片
sift算子存在错误的匹配点,因此需要用Ransac算法剔除错误匹配点。

  1. 利用RANSAC算法计算变换矩阵
    2.1 RANSAC算法
    RANSAC是"RANdom SAmple Consensus"(随机一致采样)的缩写。该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵。基本的思想是:数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。
    示例
    一个简单的例子是从一组观测数据中找出合适的2维直线。假设观测数据中包含局内点和局外点,其中局内点近似的被直线所通过,而局外点远离于直线。简单的最小二乘法不能找到适应于局内点的直线,原因是最小二乘法尽量去适应包括局外点在内的所有点。相反,RANSAC能得出一个仅仅用局内点计算出模型,并且概率还足够高。但是,RANSAC并不能保证结果一定正确,为了保证算法有足够高的合理概率,我们必须小心的选择算法的参数。
    图像处理之图像拼接(python)_第2张图片
    概述
    RANSAC算法的输入是一组观测数据,一个可以解释或者适应于观测数据的参数化模型,一些可信的参数。 通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
    (1) 先随机假设一小组局内点为初始值。然后用此局内点拟合一个模型,此模型适应于假设的局内点,所有的未知参数都能从假设的局内点计算得出。
    (2) 用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点,将局内点扩充。
    (3) 如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
    (4) 用所有假设的局内点去重新估计模型,因为此模型仅仅是在初始的假设的局内点估计的,后续有扩充后,需要更新。
    (5)通过估计局内点与模型的错误率来评估模型。
    整个这个过程为迭代一次,此过程被重复执行固定的次数,每次产生的模型有两个结局:
    a. 因为局内点太少,效果不如前一次的模型,而被舍弃,
    b. 存在比现有的更好的模型而被选用。
    求解单应性矩阵:
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算法选择的4个对应点对(data中的前4个点对),然后拟合一个单应性矩阵。get_error()方法对每个对应点对使用该单应性矩阵,然后返回相应的平方距离之和。因此ransac算法能够判定正确与错误的点。在实际中,还需在距离上使用一个阈值来决定合理的单应性矩阵是哪些。

3.进行图像拼接
估计出图像间的单应性矩阵后,需要将所有的图像扭曲到一个公共的图像平面上。通常,这里的公共平面为中心图像平面(否则需要进行大量变形)。一种方法是创建一个很大的图像,比如图像中全部填充0,使其和中心图像平行,然后将所有的图像扭曲到上面。由于我所有的图像是由照相机水平旋转拍摄的,因此可使用一个较简单的步骤:将中心图像左边或右边的区域填充0,以便为扭曲的图像腾出空间。

运行代码

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

"""
This is the panorama example from section 3.3.
"""

# set paths to data folder
featname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.sift' for i in range(5)] 
imname = ['C:/Users/Administrator/Documents/python/jmu'+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([fp[1],fp[0],fp[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 = 1200 # 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("example.png",dpi=300)
show()

三、实现结果
3.1 室内场景
初始图像
图像处理之图像拼接(python)_第3张图片
拼接后的图像
图像处理之图像拼接(python)_第4张图片
室内图片拼接图片中出现问题。
分析:
1、分割明显
2、照片歪斜严重
3、物体变形严重

原因分析:
1、建筑物相似
2、角度选取不佳
3、第二张和最后一张明显的匹配标志物

3.2 室外场景
原始图像
图像处理之图像拼接(python)_第5张图片
拼接图
图像处理之图像拼接(python)_第6张图片
原始图像
图像处理之图像拼接(python)_第7张图片
拼接后的图
图像处理之图像拼接(python)_第8张图片
可从图片看出,合并效果并不好,画圈处有明显的颜色区分。

图像处理之图像拼接(python)_第9张图片
对于图片拼接不自然问题,应进行图片融合。理论上可使用Graph cuts方法解决。
如有错误欢迎指正~

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