图像拼接


前言

经过前两次实验,我们了解了一种局部图像描述方法SIFT,图像到图像的多种变换方式。接下来,要通过结合两者,来实现图像拼接


RANSAC

RANSAC是“RANdom SAmple Consensus”(随即一致性采样)的缩写。该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵,RANSAC的基本思想是,数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。

拼接

1.简单实现图像拼接

import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
import cv_read

# 计时开始
start_time = time.time()

#看看效果
MIN = 10
img1 = cv2.imread('pic/1.jpg')  # query
img2 = cv2.imread('pic/2.jpg')  # train
# SIFT特征点描述
sift = cv2.xfeatures2d.SIFT_create()
# 可以改为SURF
# surf=cv2.xfeatures2d.SURF_create(10000, nOctaves=4, extended=False, upright=True)
kp1, descrip_1 = sift.detectAndCompute(img1, None)
kp2, descrip_2 = sift.detectAndCompute(img2, None)
# FLANN算法 快速fast·近似approximate·近邻nearest neighbors·算法库library
# K-D树算法 k-d TreeAlgorithm
FLANN_INDEX_KDTREE = 0
# 参数algorithm用来指定匹配所使用的算法 参数tree用来指定算法中树的数量
indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
# 参数SearchParams用来指定递归遍历的次数。值越高结果越准确,耗时也越长。
searchParams = dict(checks=50)
flann = cv2.FlannBasedMatcher(indexParams, searchParams)
match = flann.knnMatch(descrip_1, descrip_2, k=2)  # 最近邻算法 k nearest neighbor
# 建立一个列表保存优秀的特征索引
good = []
for i, (m, n) in enumerate(match):
    if (m.distance < 0.75 * n.distance):  # 欧氏距离比率 匹配准则
        good.append(m)

if len(good) > MIN:
# 图二作为目标图像
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)  # 行转列,queryIdx查询点的索引升序
    ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)  # trainIdx是被查询点的索引
    H, mask = cv2.findHomography(src_pts, ano_pts, cv2.RANSAC, 5.0) # 得到单应性矩阵H
    warpImg = cv2.warpPerspective(img2, np.linalg.inv(H),
                                  (img1.shape[1] + img2.shape[1], img1.shape[0])) # 将单应性变换应用到图像img2上
    direct = warpImg.copy()
    direct[0:img1.shape[0], 0:img1.shape[1]] = img1

    simple = time.time()

    cv2.namedWindow('Result', cv2.WINDOW_NORMAL)
    cv2.imshow('Result',warpImg)

    rows, cols = img1.shape[:2]
    for col in range(0, cols):
        if img1[:, col].any() and warpImg[:, col].any():  # 开始重叠的最左端
            left = col
            break
    for col in range(cols - 1, 0, -1):
        if img1[:, 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 img1[row, col].any():  # 如果没有原图,用旋转的填充
                res[row, col] = warpImg[row, col]
            elif not warpImg[row, col].any():  # 如果没有Warp图,用原图替代
                res[row, col] = img1[row, col]
            else:
                srcImgLen = float(abs(col - left))
                testImgLen = float(abs(col - right))
                alpha = srcImgLen / (srcImgLen + testImgLen)
                res[row, col] = np.clip(img1[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)

    warpImg[0:img1.shape[0], 0:img1.shape[1]] = res
    final = time.time()
    img3 = cv2.cvtColor(direct, cv2.COLOR_BGR2RGB)
    plt.imshow(img3, ), plt.show()
    img4 = cv2.cvtColor(warpImg, cv2.COLOR_BGR2RGB)
    plt.imshow(img4, ), plt.show()
    print('simple stich cost %f' % (simple - start_time))
    print('\ntotal cost %f' % (final - start_time))
    cv2.imwrite(r'pic\simplepanorma.png', direct)
    cv2.imwrite(r'pic\bestpanorma.png', warpImg)

else:
    print('not enough matches!')

实验用的照片:
在这里插入图片描述
在这里插入图片描述
运行结果如下:
在这里插入图片描述

2.全景图像拼接

图像拼接主函数:

from numpy import *
from matplotlib.pyplot import *
from PIL import Image
import homography
import warp
from PCV.localdescriptors import sift


featname = [r'mosaic/' + str(i + 1) + '.sift' for i in range(5)]
imname = [r'mosaic/' + 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])

# 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)

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

# 扭曲图像
# img delta
# delta = 100  # 用于填充和平移 for padding and translation
# libraryimg delta
delta = 1000

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

单应性矩阵:

from numpy import *


# 1.单应性变换
def normallize(points):
    """在齐次坐标意义下,对点集进行归一化,是最后一行为1"""
    for row in points:
        row /= points[-1]
    return points


def make_homog(points):
    """将点集(dim×n的数组)转换为齐次坐标表示"""
    return vstack((points, ones((1, points.shape[1]))))


# 1.1直接线性变换算法
def H_from_points(fp, tp):
    """使用线性DLT方法,计算单应性矩阵H,使fp映射到tp。点自动进行归一化"""

    if fp.shape != tp.shape:
        raise RuntimeError("number of points do not match")

    # 对点进行归一化(对数值计算很重要)
    # --- 映射起始点 ---
    m = mean(fp[:2], axis=1)
    maxstd = max(std(fp[:2], axis=1)) + 1e-9
    C1 = diag([1 / maxstd, 1 / maxstd, 1])
    C1[0][2] = -m[0] / maxstd
    C1[1][2] = -m[1] / maxstd
    fp = dot(C1, fp)

    # --- 映射对应点 ---
    m = mean(tp[:2], axis=1)
    maxstd = max(std(tp[:2], axis=1)) + 1e-9
    C2 = diag([1 / maxstd, 1 / maxstd, 1])
    C2[0][2] = -m[0] / maxstd
    C2[1][2] = -m[1] / maxstd
    tp = dot(C2, tp)

    # 创建用于线性方法的矩阵,对于每个对应对,在矩阵中会出现两行数值
    nbr_correspondences = fp.shape[1]
    A = zeros((2 * nbr_correspondences, 9))
    for i in range(nbr_correspondences):
        A[2 * i] = [-fp[0][i], -fp[1][i], -1, 0, 0, 0,
                    tp[0][i] * fp[0][i], tp[0][i] * fp[1][i], tp[0][i]]
        A[2 * i + 1] = [0, 0, 0, -fp[0][i], -fp[1][i], -1,
                        tp[1][i] * fp[0][i], tp[1][i] * fp[1][i], tp[1][i]]

    U, S, V = linalg.svd(A)
    H = V[8].reshape((3, 3))

    # 反归一化
    H = dot(linalg.inv(C2), dot(H, C1))

    # 归一化,然后返回
    return H / H[2, 2]


# 1.2 仿射变换,计算H矩阵
def Haffine_from_points(fp, tp):
    """计算H仿射变换,使得tp是fp经过仿射变换H得到的"""

    if fp.shape != tp.shape:
        raise RuntimeError('number of points do not match')

    # 对点进行归一化(对数值计算很重要)
    # --- 映射起始点 ---
    m = mean(fp[:2], axis=1)
    maxstd = max(std(fp[:2], axis=1)) + 1e-9
    C1 = diag([1 / maxstd, 1 / maxstd, 1])
    C1[0][2] = -m[0] / maxstd
    C1[1][2] = -m[1] / maxstd
    fp_cond = dot(C1, fp)

    # --- 映射对应点 ---
    m = mean(tp[:2], axis=1)
    C2 = C1.copy()  # 两个点集,必须都进行相同的缩放
    C2[0][2] = -m[0] / maxstd
    C2[1][2] = -m[1] / maxstd
    tp_cond = dot(C2, tp)

    # 因为归一化后点的均值为0,所以平移量为0
    A = concatenate((fp_cond[:2], tp_cond[:2]), axis=0)
    U, S, V = linalg.svd(A.T)

    # 如Hartley和Zisserman著的Multiplr View Geometry In Computer,Scond Edition所示,
    # 创建矩阵B和C
    tmp = V[:2].T
    B = tmp[:2]
    C = tmp[2:4]

    tmp2 = concatenate((dot(C, linalg.pinv(B)), zeros((2, 1))), axis=1)
    H = vstack((tmp2, [0, 0, 1]))

    # 反归一化
    H = dot(linalg.inv(C2), dot(H, C1))

    return H / H[2, 2]


# RANSAC算法求解单应性矩阵
class RanSacModel(object):
    """
    用于测试单应性矩阵的类,其中单应性矩阵是由网站www.spicy.org/Cookbook/RANSAC上的
    ransac.py计算出来的
    """

    def __init__(self, debug=False):
        self.debug = debug

    def fit(self, data):
        """计算选取的4个对应的单应性矩阵"""

        # 将其转置,来调用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))


def H_from_ransac(fp, tp, model, maxiter=1000, match_theshpld=10):
    """
    使用RANSAC稳健性估计点对应间的单应性矩阵H(ransac.py为从
    www.scipy.org/Cookbook/RANSAC下载的版本)
    """

    # 输入:齐次坐标表示的点fp, tp(3×n的数组)

    import ransac

    # 对应点组
    data = vstack((fp, tp))

    # 计算H,并返回
    H, ransac_data = ransac.ransac(data.T, model, 4, maxiter, match_theshpld, 10,
                                   return_all=True)

    return H, ransac_data['inliers']

RANSAC:

import numpy
import scipy # use numpy if scipy unavailable
import scipy.linalg # use numpy if scipy unavailable

## Copyright (c) 2004-2007, Andrew D. Straw. All rights reserved.

## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are
## met:

##     * Redistributions of source code must retain the above copyright
##       notice, this list of conditions and the following disclaimer.

##     * Redistributions in binary form must reproduce the above
##       copyright notice, this list of conditions and the following
##       disclaimer in the documentation and/or other materials provided
##       with the distribution.

##     * Neither the name of the Andrew D. Straw nor the names of its
##       contributors may be used to endorse or promote products derived
##       from this software without specific prior written permission.

## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

def ransac(data,model,n,k,t,d,debug=False,return_all=False):
    """fit model parameters to data using the RANSAC algorithm
    
This implementation written from pseudocode found at
http://en.wikipedia.org/w/index.php?title=RANSAC&oldid=116358182

{{{
Given:
    data - a set of observed data points
    model - a model that can be fitted to data points
    n - the minimum number of data values required to fit the model
    k - the maximum number of iterations allowed in the algorithm
    t - a threshold value for determining when a data point fits a model
    d - the number of close data values required to assert that a model fits well to data
Return:
    bestfit - model parameters which best fit the data (or nil if no good model is found)
iterations = 0
bestfit = nil
besterr = something really large
while iterations < k {
    maybeinliers = n randomly selected values from data
    maybemodel = model parameters fitted to maybeinliers
    alsoinliers = empty set
    for every point in data not in maybeinliers {
        if point fits maybemodel with an error smaller than t
             add point to alsoinliers
    }
    if the number of elements in alsoinliers is > d {
        % this implies that we may have found a good model
        % now test how good it is
        bettermodel = model parameters fitted to all points in maybeinliers and alsoinliers
        thiserr = a measure of how well model fits these points
        if thiserr < besterr {
            bestfit = bettermodel
            besterr = thiserr
        }
    }
    increment iterations
}
return bestfit
}}}
"""
    iterations = 0
    bestfit = None
    besterr = numpy.inf
    best_inlier_idxs = None
    while iterations < k:
        maybe_idxs, test_idxs = random_partition(n,data.shape[0])
        maybeinliers = data[maybe_idxs,:]
        test_points = data[test_idxs]
        maybemodel = model.fit(maybeinliers)
        test_err = model.get_error( test_points, maybemodel)
        also_idxs = test_idxs[test_err < t] # select indices of rows with accepted points
        alsoinliers = data[also_idxs,:]
        if debug:
            print('test_err.min()',test_err.min())
            print('test_err.max()',test_err.max())
            print('numpy.mean(test_err)',numpy.mean(test_err))
            print('iteration %d:len(alsoinliers) = %d'%(
                iterations,len(alsoinliers)))
        if len(alsoinliers) > d:
            betterdata = numpy.concatenate( (maybeinliers, alsoinliers) )
            bettermodel = model.fit(betterdata)
            better_errs = model.get_error( betterdata, bettermodel)
            thiserr = numpy.mean( better_errs )
            if thiserr < besterr:
                bestfit = bettermodel
                besterr = thiserr
                best_inlier_idxs = numpy.concatenate( (maybe_idxs, also_idxs) )
        iterations+=1
    if bestfit is None:
        raise ValueError("did not meet fit acceptance criteria")
    if return_all:
        return bestfit, {'inliers':best_inlier_idxs}
    else:
        return bestfit

def random_partition(n,n_data):
    """return n random rows of data (and also the other len(data)-n rows)"""
    all_idxs = numpy.arange( n_data )
    numpy.random.shuffle(all_idxs)
    idxs1 = all_idxs[:n]
    idxs2 = all_idxs[n:]
    return idxs1, idxs2

class LinearLeastSquaresModel:
    """linear system solved using linear least squares

    This class serves as an example that fulfills the model interface
    needed by the ransac() function.
    
    """
    def __init__(self,input_columns,output_columns,debug=False):
        self.input_columns = input_columns
        self.output_columns = output_columns
        self.debug = debug
    def fit(self, data):
        A = numpy.vstack([data[:,i] for i in self.input_columns]).T
        B = numpy.vstack([data[:,i] for i in self.output_columns]).T
        x,resids,rank,s = scipy.linalg.lstsq(A,B)
        return x
    def get_error( self, data, model):
        A = numpy.vstack([data[:,i] for i in self.input_columns]).T
        B = numpy.vstack([data[:,i] for i in self.output_columns]).T
        B_fit = scipy.dot(A,model)
        err_per_point = numpy.sum((B-B_fit)**2,axis=1) # sum squared error per row
        return err_per_point
        
def test():
    # generate perfect input data

    n_samples = 500
    n_inputs = 1
    n_outputs = 1
    A_exact = 20*numpy.random.random((n_samples,n_inputs) )
    perfect_fit = 60*numpy.random.normal(size=(n_inputs,n_outputs) ) # the model
    B_exact = scipy.dot(A_exact,perfect_fit)
    assert B_exact.shape == (n_samples,n_outputs)

    # add a little gaussian noise (linear least squares alone should handle this well)
    A_noisy = A_exact + numpy.random.normal(size=A_exact.shape )
    B_noisy = B_exact + numpy.random.normal(size=B_exact.shape )

    if 1:
        # add some outliers
        n_outliers = 100
        all_idxs = numpy.arange( A_noisy.shape[0] )
        numpy.random.shuffle(all_idxs)
        outlier_idxs = all_idxs[:n_outliers]
        non_outlier_idxs = all_idxs[n_outliers:]
        A_noisy[outlier_idxs] =  20*numpy.random.random((n_outliers,n_inputs) )
        B_noisy[outlier_idxs] = 50*numpy.random.normal(size=(n_outliers,n_outputs) )

    # setup model

    all_data = numpy.hstack( (A_noisy,B_noisy) )
    input_columns = range(n_inputs) # the first columns of the array
    output_columns = [n_inputs+i for i in range(n_outputs)] # the last columns of the array
    debug = False
    model = LinearLeastSquaresModel(input_columns,output_columns,debug=debug)

    linear_fit,resids,rank,s = scipy.linalg.lstsq(all_data[:,input_columns],
                                                  all_data[:,output_columns])

    # run RANSAC algorithm
    ransac_fit, ransac_data = ransac(all_data,model,
                                     50, 1000, 7e3, 300, # misc. parameters
                                     debug=debug,return_all=True)
    if 1:
        import pylab

        sort_idxs = numpy.argsort(A_exact[:,0])
        A_col0_sorted = A_exact[sort_idxs] # maintain as rank-2 array

        if 1:
            pylab.plot( A_noisy[:,0], B_noisy[:,0], 'k.', label='data' )
            pylab.plot( A_noisy[ransac_data['inliers'],0], B_noisy[ransac_data['inliers'],0], 'bx', label='RANSAC data' )
        else:
            pylab.plot( A_noisy[non_outlier_idxs,0], B_noisy[non_outlier_idxs,0], 'k.', label='noisy data' )
            pylab.plot( A_noisy[outlier_idxs,0], B_noisy[outlier_idxs,0], 'r.', label='outlier data' )
        pylab.plot( A_col0_sorted[:,0],
                    numpy.dot(A_col0_sorted,ransac_fit)[:,0],
                    label='RANSAC fit' )
        pylab.plot( A_col0_sorted[:,0],
                    numpy.dot(A_col0_sorted,perfect_fit)[:,0],
                    label='exact system' )
        pylab.plot( A_col0_sorted[:,0],
                    numpy.dot(A_col0_sorted,linear_fit)[:,0],
                    label='linear fit' )
        pylab.legend()
        pylab.show()

if __name__=='__main__':
    test()
    

图像扭曲:

from numpy import *
import numpy as np
from scipy import ndimage
from scipy.spatial import Delaunay
from matplotlib.pyplot import *
from PIL import Image
import matplotlib.pyplot as plt
import homography

# 将图像放到图像中
def image_in_image(im1, im2, tp):
    """使用仿射变换将im1放置在im2上,使im1图像的角和tp尽可能的靠近
        tp是齐次表示的,并且是按照从左上角逆时针计算的"""

    # 扭曲的点
    m, n = im1.shape[:2]
    fp = array([[0, m, m, 0], [0, 0, n, n], [1, 1, 1, 1]])

    # 计算仿射变换,并且将其应用于图像im1中
    # 计算H矩阵
    H = homography.Haffine_from_points(tp, fp)
    im1_t = ndimage.affine_transform(im1, H[:2, :2],
                                     (H[0, 2], H[1, 2]), im2.shape[:2])
    alpha = (im1_t > 0)

    return (1 - alpha) * im2 + alpha * im1_t


# 三角形的alpha图像
def alpha_for_triangle(points, m, n):
    """对于带有由points定义角点的三角形,创建大小为(m,n)的alpha图
    (在归一化的齐次坐标意义下)"""

    alpha = zeros((m, n))

    points = np.array(points, dtype=np.int)

    for i in range(min(points[0]), max(points[0])):
        for j in range(min(points[1]), max(points[1])):
            x = linalg.solve(points, [i, j, 1])
            if min(x) > 0:
                alpha[i, j] = 1
    return alpha


# 三角剖分的函数
def triangulate_points(x, y):
    """二维点的 Delaunay 三角剖分"""
    tri = Delaunay(np.c_[x, y]).simplices
    return tri


def pw_affine(fromim, toim, fp, tp, tri):
    """ 从一幅图像中扭曲矩形图像块
    fromim= 将要扭曲的图像
    toim= 目标图像
    fp= 齐次坐标表示下,扭曲前的点
    tp= 齐次坐标表示下,扭曲后的点
    tri= 三角剖分 """

    im = toim.copy()

    # 检查图像是灰度图像还是彩色图象
    is_color = len(fromim.shape) == 3

    # 创建扭曲后的图像(如果需要对彩色图像的每个颜色通道进行迭代操作,那么有必要这样做)
    im_t = zeros(im.shape, 'u8')

    for t in tri:
        # 计算仿射变换
        H = homography.Haffine_from_points(tp[:, t], fp[:, t])

        if is_color:
            for col in range(fromim.shape[2]):
                im_t[:, :, col] = ndimage.affine_transform(
                    fromim[:, :, col], H[:2, :2], (H[0, 2], H[1, 2]), im.shape[:2])
                # im1_t = ndimage.affine_transform(im1, H[:2, :2],
                #                                  (H[0, 2], H[1, 2]), im2.shape[:2])
        else:
            im_t = ndimage.affine_transform(
                fromim, H[:2, :2], (H[0, 2], H[1, 2]), im.shape[:2])
        # 三角形的 alpha
        alpha = alpha_for_triangle(tp[:, t], im.shape[0], im.shape[1])

        # 将三角形加入到图像中
        im[alpha > 0] = im_t[alpha > 0]
    return im


# 绘制三角形
def plot_mesh(x, y, tri):
    """ 绘制三角形 """
    for t in tri:
        t_ext = [t[0], t[1], t[2], t[0]]  # 将第一个点加入到最后
        plot(x[t_ext], y[t_ext], 'r')

# 图像拼接
def panorama(H, fromim, toim, padding=2400, delta=2400):
    """
    使用单应性矩阵H(使用RANSAC健壮性估计得出),协调两幅图像,创建水平全景图形。结果
    为一副和toim具有相同高度的图像。padding指定填充像素的数目,delta指定额外的平移量
    """

    # 检查图像是灰度图像,还是彩色图像
    is_color = len(fromim.shape) == 3

    # 用于geometric_transform() 的单应性变换
    def transf(p):
        p2 = dot(H, [p[0], p[1], 1])
        return (p2[0] / p2[2], p2[1] / p2[2])

    if H[1, 2] < 0:  # fromim在右边
        print("warp - right")
        # 变换fromim
        if is_color:
            # 在目标图像的右边填充0
            toim_t = hstack((toim, zeros((toim.shape[0], padding, 3))))
            fromim_t = zeros((toim.shape[0], toim.shape[1] + padding, toim.shape[2]))
            for col in range(3):
                fromim_t[:, :, col] = ndimage.geometric_transform(fromim[:, :, col],
                                                                  transf, (toim.shape[0], toim.shape[1] + padding))
        else:
            # 在目标图像的右边填充0
            toim_t = hstack((toim, zeros((toim.shape[0], padding))))
            fromim_t = ndimage.geometric_transform(fromim, transf,
                                                   (toim.shape[0], toim.shape[1] + padding))
    else:
        print("warp - left")
        # 为了补偿填充效果,在左边加入平移量
        H_delta = array([[1, 0, 0], [0, 1, -delta], [0, 0, 1]])
        H = dot(H, H_delta)
        # fromim 变换
        if is_color:
            # 在图像左边填充0
            toim_t = hstack((zeros((toim.shape[0], padding, 3)), toim))
            fromim_t = zeros((toim.shape[0], toim.shape[1] + padding, toim.shape[2]))
            for col in range(3):
                fromim_t[:, :, col] = ndimage.geometric_transform(fromim[:, :, col],
                                                                  transf, (toim.shape[0], toim.shape[1] + padding))
        else:
            # 在目标图像的左边填充0
            toim_t = hstack((zeros((toim.shape[0], padding)), toim))
            fromim_t = ndimage.geometric_transform(fromim, transf,
                                                   (toim.shape[0], toim.shape[1] + padding))

    # 协调后返回(将fromim放置在toim上)
    if is_color:
        # 所有非黑色像素
        alpha = ((fromim_t[:, :, 0] * fromim_t[:, :, 1] * fromim_t[:, :, 2]) > 0)
        for col in range(3):
            toim_t[:, :, col] = fromim_t[:, :, col] * alpha + toim_t[:, :, col] * (1 - alpha)
    else:
        alpha = (fromim_t > 0)
        toim_t = fromim_t * alpha + toim_t * (1 - alpha)
    return toim_t

实验用图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
坏的结果:
在这里插入图片描述
原因不明,为什么会把最左的图片拼接到最右?

时间紧尚未有答案,之后还会持续更新

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
压缩包中包含的具体内容: 对给定数据中的6个不同场景图像,进行全景图拼接操作,具体要求如下: (1) 寻找关键点,获取关键点的位置和尺度信息(DoG检测子已由KeypointDetect文件夹中的detect_features_DoG.m文件实现;请参照该算子,自行编写程序实现Harris-Laplacian检测子)。 (2) 在每一幅图像中,对每个关键点提取待拼接图像的SIFT描述子(编辑SIFTDescriptor.m文件实现该操作,运行EvaluateSIFTDescriptor.m文件检查实现结果)。 (3) 比较来自两幅不同图像的SIFT描述子,寻找匹配关键点(编辑SIFTSimpleMatcher.m文件计算两幅图像SIFT描述子间的Euclidean距离,实现该操作,运行EvaluateSIFTMatcher.m文件检查实现结果)。 (4) 基于图像中的匹配关键点,对两幅图像进行配准。请分别采用最小二乘方法(编辑ComputeAffineMatrix.m文件实现该操作,运行EvaluateAffineMatrix.m文件检查实现结果)和RANSAC方法估计两幅图像间的变换矩阵(编辑RANSACFit.m 文件中的ComputeError()函数实现该操作,运行TransformationTester.m文件检查实现结果)。 (5) 基于变换矩阵,对其中一幅图像进行变换处理,将其与另一幅图像进行拼接。 (6) 对同一场景的多幅图像进行上述操作,实现场景的全景图拼接(编辑MultipleStitch.m文件中的makeTransformToReferenceFrame函数实现该操作)。可以运行StitchTester.m查看拼接结果。 (7) 请比较DoG检测子和Harris-Laplacian检测子的实验结果。图像拼接的效果对实验数据中的几个场景效果不同,请分析原因。 已经实现这些功能,并且编译运行均不报错!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值