RANSAC算法实现图像全景拼接

一、全景拼接的原理

1.RANSAC算法介绍

RANSAC(Random Sample Consensus)即随机采样一致性,该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵,RANSAC的作用就在于,找到正确数据点的同时摒弃噪声点。

2.使用RANSAC算法来求解单应性矩阵

在进行图像拼接时,我们首先要解决的是找到图像之间的匹配的对应点。通常我们采用SIFT算法来实现特征点的自动匹配,SIFT是具有很强稳健性的描述子,比起图像块相关的Harris角点,它能产生更少的错误的匹配,但仍然还是存在错误的对应点。所以需要用RANSAC算法,对SIFT算法产生的128维特征描述符进行剔除误匹配点。
由直线的知识点可知,两点可以确定一条直线,所以可以随机的在数据点集中选择两点,从而确定一条直线。然后通过设置给定的阈值,计算在直线两旁的符合阈值范围的点,统计点的个数inliers。inliers最多的点集所在的直线,就是我们要选取的最佳直线。
RANSAC算法就是在一原理的基础上,进行的改进,从而根据阈值,剔除错误的匹配点。首先,从已求得的匹配点对中抽取几对匹配点,计算变换矩阵。然后对所有匹配点,计算映射误差。接着根据误差阈值,确定inliers。最后针对最大inliers集合,重新计算单应矩阵 H。

3.拼接图像

在估计出图像的单应性矩阵后,我们需要将所有的图像扭曲到一个公共的图像平面上。通常我们以中心图像平面作为公共平面,然后将中心图像左边或者右边的区域填充为0,以便为扭曲的图像腾出空间。

二、全景拼接实验

此次实验分为两组不同情况拍摄的场景的图片的拼接

1.针对固定点位拍摄多张图片,以中间图片为中心,实现图像的拼接融合

(1)输入图片

在这里插入图片描述

(2)代码

# -*- coding: utf-8 -*-
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
import os

print os.getcwd()
"""
This is the panorama example from section 3.3.
"""

# set paths to data folder
featname = ['D:\computervision-picture\sample' + str(i + 1) + '.sift' for i in range(5)]
imname = ['D:\computervision-picture\sample' + 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])

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)

# 将匹配转换成齐次坐标点的函数
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()

# H_from_ransac函数的返回结果为单应性矩阵和对应该单应性矩阵的正确点对
fp, tp = convert_points(1)
# im1 到 im2 的单应性矩阵
H_12 = homography.H_from_ransac(fp, tp, model)[0]

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

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

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

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

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

(3)运行结果

特征匹配结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
拼接结果:
在这里插入图片描述

(4)分析小结

①通过实验结果可以看出在这个建筑物右侧边缘部分匹配出现偏差最大,左侧区域基本上拼接完整,但是不可避免地出现了鬼影重叠现象,重点问题还是右侧景深稍微复杂的一块区域。至于鬼影现象,后续可以用multi band实现融合。
②该实验图片的选取也是实验能比较成功的一个因素。我之前第一组选取的图片因为曝光度,光照等因素,使得特征点没那么理想,以致于无法实现拼接,后续换了一组图片,便可以实现拼接。
③在实验过程中可以发现,图片像素值过大的话,代码运行得比较慢,像素值太小,导致特征点不明显,拼接效果不好,所以要自己适当调整图片的像素值来达到理想的拼接效果。

(5)遇到的问题及解决方法

在这里插入图片描述
解决办法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解决办法:修改图片像素值时将其改成一样大小。

2.针对同一场景(需选取视差变化大的场景,也就是有近景目标),更换拍摄位置,分析拼接结果

(1)输入图片

在这里插入图片描述

(2)代码

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

if __name__ == '__main__':
    top, bot, left, right = 100, 100, 0, 500
    img1 = cv.imread('D:\computervision-picture\home1.jpg')
    img2 = cv.imread('D:\computervision-picture\home2.jpg')
    srcImg = cv.copyMakeBorder(img1, top, bot, left, right, cv.BORDER_CONSTANT, value=(0, 0, 0))
    testImg = cv.copyMakeBorder(img2, top, bot, left, right, cv.BORDER_CONSTANT, value=(0, 0, 0))
    img1gray = cv.cvtColor(srcImg, cv.COLOR_BGR2GRAY)
    img2gray = cv.cvtColor(testImg, cv.COLOR_BGR2GRAY)
    sift = cv.xfeatures2d_SIFT().create()
    # find the keypoints and descriptors with SIFT
    kp1, des1 = sift.detectAndCompute(img1gray, None)
    kp2, des2 = sift.detectAndCompute(img2gray, None)
    # FLANN parameters
    FLANN_INDEX_KDTREE = 1
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)
    flann = cv.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)

    # Need to draw only good matches, so create a mask
    matchesMask = [[0, 0] for i in range(len(matches))]

    good = []
    pts1 = []
    pts2 = []
    # ratio test as per Lowe's paper
    for i, (m, n) in enumerate(matches):
        if m.distance < 0.7 * n.distance:
            good.append(m)
            pts2.append(kp2[m.trainIdx].pt)
            pts1.append(kp1[m.queryIdx].pt)
            matchesMask[i] = [1, 0]

    draw_params = dict(matchColor=(0, 255, 0),
                       singlePointColor=(255, 0, 0),
                       matchesMask=matchesMask,
                       flags=0)
    img3 = cv.drawMatchesKnn(img1gray, kp1, img2gray, kp2, matches, None, **draw_params)
    plt.imshow(img3, ), plt.show()

    rows, cols = srcImg.shape[:2]
    MIN_MATCH_COUNT = 10
    if len(good) > MIN_MATCH_COUNT:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
        M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC, 5.0)
        warpImg = cv.warpPerspective(testImg, np.array(M), (testImg.shape[1], testImg.shape[0]),
                                     flags=cv.WARP_INVERSE_MAP)

        for col in range(0, cols):
            if srcImg[:, col].any() and warpImg[:, col].any():
                left = col
                break
        for col in range(cols - 1, 0, -1):
            if srcImg[:, col].any() and warpImg[:, col].any():
                right = col
                break

        res = np.zeros([rows, cols, 3], np.uint8)
        for row in range(0, rows):
            for col in range(0, cols):
                if not srcImg[row, col].any():
                    res[row, col] = warpImg[row, col]
                elif not warpImg[row, col].any():
                    res[row, col] = srcImg[row, col]
                else:
                    srcImgLen = float(abs(col - left))
                    testImgLen = float(abs(col - right))
                    alpha = srcImgLen / (srcImgLen + testImgLen)
                    res[row, col] = np.clip(srcImg[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)

        # opencv is bgr, matplotlib is rgb
        res = cv.cvtColor(res, cv.COLOR_BGR2RGB)
        # show the result
        plt.figure()
        plt.imshow(res)
        plt.show()
    else:
        print("Not enough matches are found - {}/{}".format(len(good), MIN_MATCH_COUNT))
        matchesMask = None

(3)运行结果

特征匹配结果:
在这里插入图片描述
拼接结果:在这里插入图片描述

(4)分析小结

这次拼接效果算是比较完整,但是由于图片的像素值被自己改小了,所以稍微有点糊。远处景深单一的区域大致都已经完全正确拼接,近处目标拼接地不是很理想,鬼影重叠现象比较严重,其次拼接图像旁边留黑处也很多,原因也是因为图片像素值太小。

(5)遇到的问题及解决方法

基于上述留黑部分太大,稍微增大像素值之后的结果
在这里插入图片描述
可以看到拼接图像留黑部分有减少且图像变得更加清晰。

三.实验总结

室外景深落差较小的场景,拼接效果比较理想,但由于图像曝光度的不同,导致在图像的边界上存在边缘效应,颜色差异明显,这也是该算法需要改进的地方。
室外景深落差较大的场景,远处的区域拼接的比较完整,但美中不足的就是,近处的目标在拼接时,发生了扭曲。
此外,在利用RANSAC算法进行拼接的时候都不可避免地出现了重叠鬼影现象,这也是后续要利用multi band算法融合改进的地方。
通过此次实验也发现了拍摄测试图像时的一些技巧:为了拼接出效果比较好的图像,在保证有相同匹配点的情况下,拍摄图像的间隔尽可能大一些,且一定要站在同一点,水平移动手机进行拍摄,就像拍摄全景图那样。若人拍摄的位置发生移动的话,算法可能就会因为找不到正确的点对而报错,(自己在此次实验过程中就有遇到这类问题)。而且最好不要拍摄那种对称的建筑物,且两边的的特征点长的几乎一样的。这样会使算法的匹配出现失误。 最后在给图像编号进行测试时,切记,一定要从右往左进行编号,因为我们的算法的匹配是从最右边的图像计算出来的,代码中有一步骤是将对应的顺序进行颠倒,使其从左边图像开始进行扭曲。

版权声明:本文为WWQ0726原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/WWQ0726/article/details/105068728

智能推荐

全景图像拼接

目录   1.概念 2.基本原理 3.代码实现 4.实验结果与分析 4.1 固定点位拍摄多张图片实现图像的拼接融合 4.2 视差变化大的场景进行图像拼接 5.总结 1.概念 图像全景拼接就是将多张图片(两两间存在一定的重叠部分,可以是不同时间、不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分辨率图像的技术。 2.基本原理 在图像拼接过程中,首先通过sift算法提取图像的...

图像全景拼接

文章目录 1、图像全景拼接过程简要介绍 1.1 特征点提取与匹配 1.2 图像配准 1.3 图像融合 2、实现多图像拼接(远景) 2.1 图片集说明 2.2 实现代码 2.3 实现结果 2.4 实现小结 3、同一场景两图片拼接(近景+远景) 3.1 图片集说明 3.2 实现代码 3.3 实现结果 3.4 实现小结 4、实验过程遇到的问题及解决 问题一 问题二 5、实验总小结 1、图像全景拼接过程简...

全景图像拼接

文章目录 1、全景图像拼接 1.1 原理 1.2 基本流程 2、实验过程 2.1 场景一:固定点拍摄多张图片 2.1.1 实验结果展示 2.1.2 小结 2.1.3 代码 2.2 场景二:视差变化大 2.2.1 实验结果展示 2.2.2 小结 2.2.3 代码 3、总结 3.1 实验总结 3.2 实验遇到的问题 1、全景图像拼接 1.1 原理 全景图像拼接,即将两幅或多幅具有重叠区域的图像,合并成...

图像全景拼接

图像全景拼接 1、全景拼接简介 在此前了解了SIFT的特征点的提取与匹配,并且在SIFT的基础上,采用RANSAC算法对图像变换矩阵进行求解与精炼,删除错配。全景拼接需要在此基础上完成。 2、全景拼接步骤 1.在连续图像对间使用sift特征寻找匹配对应点对 2.进行可视化匹配,匹配两幅图像之间的特征 3.将匹配转换成齐次坐标点的函数,估计单应性矩阵 4.通过估计出图像间的单应性矩阵,将所有的图像扭...

apap图像全景拼接

文章目录 1、基本原理 1.1图像配准 1.2关于最小割 1.3关于最大流 1.4apap的实现流程 2、代码实现 3、实验场景 3.1场景一 3.2场景二 4、遇到的问题 1、基本原理 1.1图像配准 图像配准(apap)是将两张场景相关的图像进行映射,寻找其中的关系,多用在医学图像配准、图像拼接、不同摄像机的几何标定等方面,其研究也较为成熟。OpenCv中的stitching类就是使用了200...

猜你喜欢

图像全景拼接

一、全景拼接的原理介绍 全景拼接是将多副图之间的共同之处进行匹配连接,合成完整的一幅图像 二、步骤 1:从输入的两张图片里检测关键点、提取局部不变特征。 2:匹配的两幅图像之间的特征 3:使用RANSAC算法利用匹配特征向量估计单应矩阵(homography matrix)。 4:利用Step3得到的单应矩阵应用扭曲变换。 三、方法: 1.特征点提取: sift、surf、orb等特征点提取; 2...

Python爬虫(十三)——Scrapy爬取豆瓣图书

文章目录 Python爬虫(十三)——Scrapy爬取豆瓣图书 步骤 建立项目和Spider模板 编写Spider 编写Pipelines 配置settings 执行程序 完整代码 鸣谢 Python爬虫(十三)——Scrapy爬取豆瓣图书 这次我们爬取豆瓣图书的top250的目录后进入书籍界面爬取界面中的书籍标签。 步骤 建立项目和Spider模板...

python爬虫:爬取拉勾网职位并分析

博客内容:爬取拉钩网上的数据并进行分析,参考代码链接为点击打开链接。 网页爬取部分: 数据分析代码: 数据分析结果: 图一:工资频率直方图 图二:岗位位置分布图 图三:岗位福利待遇词云图...

IDEA 报错 Error:java:错误:不支持发行版本5

在idea中创建maven运行代码时,出现以下错误: 我自己用的是JDK13,这个报错应该是java版本不匹配,以下有两种解决方法: (1)第一种: ①File—>Project Structure ②查看Project与Module中的版本是否与本地一致 ③点击设置File—>Settings—>Build,Execution,Deploym...