图像全景拼接

文章目录

  • 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. 根据给定的图像集,实现图像之间的特征匹配;
  2. 通过匹配特征,计算图像之间的变换结构;
  3. 利用图像的变换结构,实现图像间的映射;
  4. 针对叠加后的图像,采用相应算法,对齐特征点;
  5. 通过图割的方法,自动选取拼接缝;
  6. 图像进行融合。

1.1 特征点提取与匹配

利用SIFT提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置、尺度、方向信息。

具体步骤:

  1. 生成高斯差分金字塔(DOG金字塔),尺度空间构建
  2. 空间极值点检测(关键点的初步查探)
  3. 稳定关键点的精确定位
  4. 稳定关键点方向信息分配
  5. 关键点描述
  6. 特征点匹配

1.2 图像配准

图像配准是一种确定待拼接图像间的重叠区域以及重叠位置的技术,它是整个图像拼接的核心。本节采用的是基于特征点的图像配准方法,即通过匹配点对构建图像序列之间的变换矩阵,从而完成全景图像的拼接。
变换矩阵H求解是图像配准的核心,其求解的算法流程如下:

  1. 检测每幅图像中特征点。
  2. 计算特征点之间的匹配。
  3. 计算图像间变换矩阵的初始值。
  4. 迭代精炼H变换矩阵。
  5. 引导匹配。用估计的H去定义对极线附近的搜索区域,进一步确定特征点的对应。
  6. 重复迭代4和5直到对应点的数目稳定为止。

1.3 图像融合

因为相机和光照强度的差异,会造成一幅图像内部,以及图像之间亮度的不均匀,拼接后的图像会出现明暗交替,这样给观察造成极大的不便。 亮度与颜色均衡处理,通常的处理方式是通过相机的光照模型,校正一幅图像内部的光照不均匀性,然后通过相邻两幅图像重叠区域之间的关系,建立相邻两幅图像之间直方图映射表,通过映射表对两幅图像做整体的映射变换,最终达到整体的亮度和颜色的一致性。

2、实现多图像拼接(远景)

2.1 图片集说明

图像全景拼接_第1张图片
本组五张图片拍摄于同一地点:通过机位移动,拍摄到从左至右有部分重叠的五张图片,用于此次多图片拼接。

2.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

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

# set paths to data folder
# featname = ['D:/data/Univ'+str(i+1)+'.sift' for i in range(5)]
# imname = ['D:/data/Univ'+str(i+1)+'.jpg' for i in range(5)]
featname = ['pic' + str(i + 1) + '.sift' for i in range(5)]
imname = ['pic' + str(i + 1) + '.jpg' for i in range(5)]
# extract features and m
# 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

# 扭曲图像
delta = 2000  # 用于填充和平移 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')
show()

2.3 实现结果

图片压缩后的运行结果:图像全景拼接_第2张图片
图片压缩前的运行结果:图像全景拼接_第3张图片

2.4 实现小结

  • 当图片模糊度高(像素值小)时,拼接会将五张图片拆成2+3两组进行拼接,拼接效果不理想;
  • 图片较清晰时,以及能够实现较准确的拼接,虽然没有看到鬼影现象,但无可避免的还有过重的拼接痕迹;
  • 图像本身像素对于运行时间和实验效果都有很大的影响,图像像素要适当,不能太大也不能太小。

3、同一场景两图片拼接(近景+远景)

3.1 图片集说明

图像全景拼接_第4张图片
两图片针对同一场景,更换拍摄位置所得。图片中既有近景目标(紫色酒精瓶),也有远景目标(建筑物、树木等),属于视觉变化较大的场景。

3.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('pic8.jpg')

    img2 = cv.imread('pic9.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.3 实现结果

图像全景拼接_第5张图片

3.4 实现小结

  • 远景部分拼接痕迹平滑,整体拼接完善,效果较好;
  • 近景发生了明显鬼影现象,拼接痕迹过重;
  • 对比远景和近景两个场景的实验结果发现远景的拼接效果较好一些。

4、实验过程遇到的问题及解决

问题一

图像全景拼接_第6张图片
解决:须将warp文件中出现的所有print加上括号()

问题二

ModuleNotFoundError: No module named 'matplotlib.delaunay’
解决:第一步,把文件中

import matplotlib.delaunay as md

改为

from scipy.spatial import Delaunay

第二步,把triangulate_points(x,y)里面的代码替换成

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

如图:
图像全景拼接_第7张图片
图像全景拼接_第8张图片

5、实验总小结

  1. 针对第一组实验,压缩像素后发现拼接发生了拆分,拼接痕迹过重,于是像素改更高一点又一次实验,发现拼接已经相对自然,只有轻微的拼接痕迹。而像素提高时,运行时效变慢。可见,图像本身像素对于运行时间和实验效果都有很大的影响。

  2. 针对第二组实验,对比远景和近景两个场景的实验结果发现远景的拼接效果较好一些。我认为其原因为:近景远景提取到的特征点丰富,匹配点也比近景的丰富,即再进行拼接等一系列的操作时参考性更大,拼接效果自然更好一些。

  3. 图片拼接时,选取要较为清晰的图片,控制大小一样,才能更好的进行拼接。

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