目录

引言

一、 单应性变换

1.1 直接线性变换算法

1.2 仿射变换

二、 图像扭曲

2.1 图像中的图像

2.2 分段仿射扭曲

2.2 图像配准

三、创建全景图

3.1 RANSAC(随机一致性采样)

3.2 拼接图像

四、总结


引言

本章讨论图像之间的变换,以及一些计算变换的方法,这些变换可以用于图像扭曲变形和图像配准。

一、 单应性变换

单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换,这里的平面是指图像或者三维中的平面表面。单应性变换具有很强的实用性,比如说图像的配准、图像纠正和纹理扭曲,以及创建全景图像等。本质上,单应性变换H,按照下面的方程映射二维中的点:

对于图像平面内的点,齐次坐标是一个非常有用的表达方式。点的齐次坐标是依赖于其尺度定义的,为此,表示的是同一个二维点。单应性矩阵仅依赖尺度定义,具有8个独立的自由度。

创建一个能够实现对点进行归一化和转换齐次坐标的功能:

def normalize(points):"""在其次坐标意义下对点集进行归一化处理:param points: 点集:return:"""for row in points:row /= points[-1]return pointsdef make_homog(points):"""将点集转化为齐次坐标表示:param points: 点集:return:"""return vstack((points, ones((1, points.shape[1]))))

进行点和变换的处理时,按照列优先的原则存储这些点,故n个三维点集将会依次存储为齐次坐标意义下的一个3*n数组。

在这些投影变换中存在一些特别重要的变换如:

仿射变换:

w=1,不具有投影变换所具有的强大变形能力。仿射变换包含一个可逆矩阵A和一个平移向量

相似变换:

包含尺度变换的二维刚体变换,上式的s向量指定了变换的尺度,R是角度为的旋转矩阵,是一个平移向量,s=1时,该变换甭管保存距离不变,此时变换为刚体变换。

1.1 直接线性变换算法

单应性矩阵可以由两幅图像中对于点计算出来,我们已经了解到一个完全射影变换具有8个自由度,而每个对应点可以写出两个方程,对应x和y坐标,所以计算单应性矩阵H要4个对应点对。计算H的方程如下:

python实现的算法如下:

def H_from_points(fp, tp):"""使用使用DLT方法,计算单应性矩阵H,使fp映射到tp,点自动进行归一化:param fp::param tp::return:"""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-9C1 = diag([1/maxstd], 1/maxstd, 1)C1[0][2] = -m[0]/maxstdC1[1][2] = -m[1]/maxstdfp = dot(C1, fp)# 映射对应点m = mean(tp[:2], axis=1)maxstd = max(std(tp[:2], axis=1)) + 1e-9C2 = diag([1/maxstd], 1/maxstd, 1)C2[0][2] = -m[0]/maxstdC2[1][2] = -m[1]/maxstdtp = dot(C2, tp)# 创建用于线性方法的矩阵,对于每个对应对在矩阵中会出现两行数值nbr_correrpondences = fp.shape[1]A = zeros(2 * nbr_correrpondences, 9)for i in range(nbr_correrpondences):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]

函数的第一步操作是检查点对两个数组中点的数目是否相同,如果不相同函数将会给出错误提示,对这些点进行归一化操作,使其均值为,方差为 ,因为算法的稳定性取决于坐标的表示情况和部分数值计算的问题,因此归一化非常重要,之后利用对应点对构造矩阵A,最小二乘解即为矩阵SVD分解后得到矩阵V的最后一行。该行经过变形后得到矩阵H,并对得到的矩阵进行处理和归一化,返回输出。

1.2 仿射变换

先前提到过仿射变换是一种重要的投影变换,其具有6个自由度,因此需要3个对应点估计矩阵H,将最后两个元素设置为0,就可以使用DLT算法估计得出,下面给出python的算法实现:

def Haffine_from_points(fp, tp):"""计算H,仿射变换,使得tp是fp经过仿射变换H得到的:param fp: :param tp: :return: """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-9C1 = diag([1/maxstd], 1/maxstd, 1)C1[0][2] = -m[0]/maxstdC1[1][2] = -m[1]/maxstdfp_cond = dot(C1, fp)# 映射对应点m = mean(tp[:2], axis=1)maxstd = max(std(tp[:2], axis=1)) + 1e-9C2 = diag([1/maxstd], 1/maxstd, 1)C2[0][2] = -m[0]/maxstdC2[1][2] = -m[1]/maxstdtp_cond = dot(C2, tp)# 因为归一化后的点均值为0,故平移量为0A = concatenate((fp_cond[:2], tp_cond[:2]), axis=0)U, S, A = linalg.svd(A.T)# 创建矩阵B,Ctemp = V[:2].TB = temp[:2]C = temp[2:4]temp2 = concatenate((dot(C, linalg.pinv(B)), zeros((2, 1))), axis=1)H = vstack((temp2, [0, 0, 1]))# 反归一化H = dot(linalg.inv(C2), dot(H,C1))return H / H[2, 2]

二、 图像扭曲

对图像应用仿射变换就将其称为图像扭曲,扭曲操作可使用Scipy工具包里的ndimage包实现,具体命令为:

transformed_im = ndimage.affine_transform(im, A, b, size)

实验:

# 图像扭曲
from scipy import ndimage
from PIL import Image
from numpy import *
from pylab import *img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
gray()
H = array([[1.4, 0.05, -100], [0.05, 1.5, -100], [0, 0, 1]])
img_re = ndimage.affine_transform(img, H[:2, :2], (H[0, 2], H[1, 2]))
subplot(121)
imshow(img), title('original')
subplot(122)
imshow(img_re), title('result')
show()

从实验结果中可以看出,经过ndimage.affine_transform()函数方法处理过的图像发生了偏折,且丢失的像素使用0进行填充。

2.1 图像中的图像

将一幅图像或图像的一部分放置在另一幅图像中,使得它们能够和指定的标记物对齐,这就是仿射扭曲的简单例子。

下面就使用仿射变换将一幅图像放置到另一幅图像做出实验:

# 仿射变换
from scipy import ndimage
from PIL import Image
from numpy import *
from pylab import *
from PCV.geometry import warpimg1 = array(Image.open('D:\\picture\\tem1.jpg').convert('L'))
img2 = array(Image.open('D:\\picture\\test3.jpg').convert('L'))
gray()
# 选定一些目标点
tp1 = array([[264, 538, 540, 264], [40, 36, 605, 605], [1, 1, 1, 1]])
tp2 = array([[500,248,250,500],[100,120,300,300],[2,2,2,2]])
img3 = warp.image_in_image(img2, img1, tp1)
img4 = warp.image_in_image(img2, img1, tp2)
subplot(221)
imshow(img1), title('original1'), axis('off')
subplot(222)
imshow(img2), title('original2'), axis('off')
subplot(223)
imshow(img3), title('tp1_result'), axis('off')
subplot(224)
imshow(img4), title('tp2_result'), axis('off')
show()

实验时使用了先前下载的工具包,程序运行时出现了ModuleNotFoundError: No module named 'matplotlib.delaunay'的错误,需要在wrap.py库中将

import matplotlib.delaunay as md

改成:

from scipy.spatial import Delaunay

并将 wrap.py里的triangulate_points(x,y)函数中的内容改成:

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

这时就能够成功运行了 ,在程序中,对选择的目标点进行了修改,可看出图片映射在另一幅图像上的状态发生了改变。当我们需要将图像放置在我们想要的位置上时,需要知道指定的目标点的坐标。

2.2 分段仿射扭曲

三角形图像块的仿射扭曲可以实现角点的精确匹配,分段仿射扭曲是对应点对集合之间常见的扭曲方式。通过给定任意图像的标记点,并将这些点进行三角剖分,然后实验仿射扭曲来扭曲每个三角形,我们可以将图像和另一幅图像的对应标记点扭曲对应。

三角化点的操作一般实验狄洛克三角剖分方法,其实验代码如下:

from numpy import *
from matplotlib.pyplot import *
from scipy.spatial import Delaunay
x, y = array(random.standard_normal((2, 100)))
tri = Delaunay(np.c_[x, y]).simplices
figure()
for t in tri:# 将第一个点加入到最后t_ext = [t[0], t[1], t[2], t[0]] plot(x[t_ext], y[t_ext], 'r')
plot(x, y, '*')
axis('off')
figure()
plot(x, y, '*')
axis('off')
show()

在Matplotlib库中就含有狄洛克三角剖分,需要我们注意的是不是在Pylab库中,所以在导入包时应该为:

from matplotlib.pyplot import *

其输出结果为:

对于一幅彩色图像,当我们需要对其进行图像扭曲时就需要额外进行考虑,下面的函数提供了一个方法:

def pw_affine(fromim,toim,fp,tp,tri):"""从图像中扭曲矩形图像块:param fromim: 将要扭曲的图像:param toim: 目标图像:param fp: 齐次坐标下,扭曲前的点:param tp: 齐次坐标下,扭曲后的点:param tri: 三角部分:return: """im = toim.copy()# 检查图像是灰度图像还是彩色图像is_color = len(fromim.shape) == 3# 创建扭曲后的图像im_t = zeros(im.shape, 'uint8') 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])else:im_t = ndimage.affine_transform(fromim,H[:2,:2],(H[0,2],H[1,2]),im.shape[:2])# 三角形的alphaalpha = alpha_for_triangle(tp[:,t],im.shape[0],im.shape[1])# 将三角形加入到图像中im[alpha>0] = im_t[alpha>0]return im

分析:这里首先检查图像是灰度图像还是彩色图像,当为彩色图像时,就要分别对每个颜色通道进行扭曲处理, 我们知道对于每个三角形而言,仿射变换是唯一确定的,因此使用了homography包中的Haffine_from_points()函数对其进行处理。

2.2 图像配准

图像配准就是将不同时间、成像设备或不同条件下(比如气候、照度、摄像位置和角度等)获取的两幅或多幅图像进行匹配、叠加的过程,它已经被广泛地应用于遥感数据分析、计算机视觉和图像处理。

由于配准图像的多样性和各种类型的退化,不能设计出适合所有配准任务的通用方法。每种方法不仅要考虑图像之间假定的几何变形类型,还要考虑辐射变形和噪声损坏,所需配准的准确率和应用数据特征。一般图像配准的流程为:

1、对两幅图像进行特征提取得到特征点;

2、通过进行相似性度量找到匹配的特征点对;

3、通过匹配的特征点对得到图像空间坐标变换参数;

4、最后由坐标变换参数进行图像配准。

在配准的过程中特征提取尤为关键,准确的特征提取为特征匹配的成功进行提供了保障。因此,寻求具有良好不变性和准确性的特征提取方法,对于匹配精度至关重要。先前我们学习到的SIRT特征提取就不失为一个很好的方法。

三、创建全景图

在同一个位置拍摄的两幅或者多福图像是单应性相关的,通常使用该约束将图像缝补起来,拼接成一个大的图像来创建全景图像。

3.1 RANSAC(随机一致性采样)

该方法是用来找到正确模型拟合带有噪声数据的迭代方法。其基本思想是,数据中包含有正确的点和噪声点,合理的模型能够在描述正确数据点的同时摒弃噪声点。

RANSAC的基本假设是: 
(1)数据由“局内点”组成,例如:数据的分布可以用一些模型参数来解释; 
(2)“局外点”是不能适应该模型的数据; 
(3)除此之外的数据属于噪声。 
局外点产生的原因有:噪声的极值;错误的测量方法;对数据的错误假设。 
RANSAC也做了以下假设:给定一组(通常很小的)局内点,存在一个可以估计模型参数的过程;而该模型能够解释或者适用于局内点。

在估计参数模型时,通常使用p表示一些迭代过程中从数据集内随机选取出来的点均为局内点的概率;此时结果模型就很可能会有作用,所以p也表征了算法所产生有用结果的概率。

假设每个点是真正内群的概率为 w:

w = 内群的数目/(内群数目+外群数目)

通常我们不知道 w 是多少,是所选择的n个点都是内群的机率, 是所选择的n个点至少有一个不是内群的机率, 是表示重复 k 次都没有全部的n个点都是内群的机率, 假设算法跑 k 次以后成功的机率是p,那么

我们可以通过P反算得到抽取次数K:

所以如果希望成功机率高:

1、当n不变时,k越大,则p越大;

2、当w不变时,n越大,所需的k就越大。

3、通常w未知,所以n选小一点比较好。

下面给出实验示例:

import numpy as np
import scipy.linalg as sl
import scipy as spdef ransac(symbol, model, n, k, t, d, debug=False, return_all=False):""":param symbol: 样本点:param model: 假设的模型:param n: 生成模型所需的最小样本点:param k: 最大迭代次数:param t: 阈值:param d: 拟合较好时,需要的样本点最少的个数,当做阈值看待:param debug::param return_all::return:"""iterations = 0bestfit = Nonebesterr = np.inf  # 设置默认值best_inlier_idxs = Nonewhile iterations < k:maybe_idxs, test_idxs = random_partition(n, symbol.shape[0])# 获取maybe{_idxs行数据(Xi,Yi)maybe_inliers = symbol[maybe_idxs, :]# 若干行(xi,yi)数据点test_points = symbol[test_idxs]# 拟合模型maybemodel = model.fit(maybe_inliers)# 计算误差test_err = model.get_error(test_points, maybemodel)also_idxs = test_idxs[test_err < t]also_inliers = symbol[also_idxs, :]if debug:print('test_err.min()', test_err.min())print('test_err.max*(', test_err.max())print('np.mean(test_err)', np.mean(test_err))print('iteration %d:len(also_inliers) = %d' %(iterations, len(also_inliers)))if len(also_inliers > d):bettersymbol = np.concatenate((maybe_inliers, also_inliers))bettermodel = model.fit(bettersymbol)better_err = model.get_error(bettersymbol, bettermodel)# 将平均误差作为新误差this_err = np.mean(better_err)if this_err < besterr:bestfit = bettermodelbesterr = this_errbest_inlier_idxs = np.concatenate((maybe_idxs, also_idxs))iterations += 1if bestfit is None:raise ValueError("didn't met fit criteria")if return_all:return bestfit, {'inliers':best_inlier_idxs}else:return bestfitdef random_partition(n, n_symbol):# 获取n_symbol下的索引all_idxs = np.arange(n_symbol)# 打乱下标索引np.random.shuffle(all_idxs)idxs_1 = all_idxs[:n]idxs_2 = all_idxs[n:]return idxs_1, idxs_2class LinearLeastSquaresModel:"""最小二乘求线性解用于ransac的输入模型"""def __init__(self, input_columns, output_columns, debug=False):self.input_columns = input_columnsself.output_columns = output_columnsself.debug = debugdef fit(self, symbol):# 第一列xi-->行xiA = np.vstack([symbol[:, i] for i in self.input_columns]).T# 第第二列yi-->行yiB = np.vstack([symbol[:, i] for i in self.output_columns]).T# residues:残差和x, resids, rank, s = sl.lstsq(A, B)# 返回最小平方和向量return xdef get_error(self, symbol, model):A = np.vstack([symbol[:, i] for i in self.input_columns]).TB = np.vstack([symbol[:, i] for i in self.output_columns]).T# 计算y值B_fit = model.k+A+model.bB_fit = sp.dot(A, model)err_per_point = np.sum((B - B_fit)**2, axis=1)return err_per_pointdef test():# 生成理想数据n_samples = 500n_inputs = 1n_output = 1A_exact = 20 * np.random.random((n_samples, n_inputs))perfect_fit = 60 * np.random.normal(size=(n_inputs, n_output))B_exact = sp.dot(A_exact, perfect_fit)# 加入高斯噪声A_noise = A_exact + np.random.normal(size=A_exact.shape)B_noise = B_exact + np.random.normal(size=B_exact.shape)if 1:# 添加局外点n_outliers = 100all_idxs = np.arange(A_noise.shape[0])np.random.shuffle(all_idxs)outlier_idxs = all_idxs[:n_outliers]non_outlier_idxs = all_idxs[n_outliers:]A_noise[outlier_idxs] = 20 * np.random.random((n_outliers, n_inputs))B_noise[outlier_idxs] = 50 * np.random.normal(size=(n_outliers, n_output))all_data = np.hstack((A_noise, B_noise))input_columns = range(n_inputs)  # the first columns of the arrayoutput_columns = [n_inputs + i for i in range(n_output)]  # the last columns of the arraydebug = Falsemodel = LinearLeastSquaresModel(input_columns, output_columns, debug=debug)linear_fit, resids, rank, s = np.linalg.lstsq(all_data[:, input_columns], all_data[:, output_columns])# run RANSAC algorithmransac_fit, ransac_data = ransac(all_data, model,5, 5000, 7e4, 50,debug=debug, return_all=True)if 1:import pylabsort_idxs = np.argsort(A_exact[:, 0])A_col0_sorted = A_exact[sort_idxs]if 1:pylab.plot(A_noise[:, 0], B_noise[:, 0], 'k.', label='data')pylab.plot(A_noise[ransac_data['inliers'], 0], B_noise[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],np.dot(A_col0_sorted, ransac_fit)[:, 0],label='RANSAC fit')pylab.plot(A_col0_sorted[:, 0],np.dot(A_col0_sorted, perfect_fit)[:, 0],label='exact system')pylab.plot(A_col0_sorted[:, 0],np.dot(A_col0_sorted, linear_fit)[:, 0],label='linear fit')pylab.legend()pylab.show()if __name__ == '__main__':test()

分析:ransac就是用来解决样本中的噪声问题的,且最多可处理50%的噪声情况。其优点在于能够很好的估计模型参数,在运行过程中,如果出现了一种足够好的模型,就会跳出主循环,当直接从maybe_model计算this_err时虽然会节约比较两种模型的错误时间,但对于噪声会更加的敏感。不过由于其计算参数的迭代次数没有上限,所以其运行的时间没有保证,倘若强行设置迭代次数可能会导致错误的结果。

3.2 拼接图像

使用ransac算法实现将所有的图像扭曲到一个公共平面上,一般情况下公共平面就为中心图像平面,否则就需要大量的变形。因为拍摄图像均是以照相机水平旋转拍摄的,因此使用一个简单的步骤:将中心图像的左边或右边区域填充为0,以便为扭曲的图像空出位置。

函数代码如下:

def panorama(H,fromim,toim,padding=2400,delta=2400):"""使用单应性矩阵H协调两幅图像,创建水平全景图像:param H:单应性矩阵,Ransac算法估计得出:param fromim:图像:param toim:输出图像:param padding:指定填充像素的数目:param delta: 指定额外的平移量:return:"""# 检查是否为灰度图像is_color = len(fromim.shape) == 3# 用于gemoetric_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')# 变换 fromimif is_color:# 在目标图像右边填充0toim_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:# 在目标图像的右边填充0toim_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)# 变换 fromimif is_color:# 在目标图像左边填充0toim_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:# 在目标图像左边填充0toim_t = hstack((zeros((toim.shape[0],padding)),toim))fromim_t = ndimage.geometric_transform(fromim,transf,(toim.shape[0],toim.shape[1]+padding))# 协调后返回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

对于通用的gemoetric_transform()函数,transf()被指定为能够描述像素到像素间映射的函数,函数通过将像素与H相乘,然后对齐次坐标进行归一化来实现像素之间的映射。

下面给出实验代码:

from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift# 需要根据自己的图像地址和图像数量修改地址和循环次数
featname = ['D:\\picture\\test_img\\tupian' + str(i + 1) + '.sift' for i in range(5)]
imname = ['D:\\picture\\test_img\\tupian' + 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):    # 循环次数 = 图像数量 - 1matches[i] = sift.match(d[i + 1], d[i])for i in range(4):    # 循环次数 = 图像数量 - 1im1 = 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)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 2fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]  # im 0 to 1tp, fp = convert_points(2)  # NB: reverse order
H_32 = homography.H_from_ransac(fp, tp, model)[0]  # im 3 to 2tp, fp = convert_points(3)  # NB: reverse order
H_43 = homography.H_from_ransac(fp, tp, model)[0]  # im 4 to 3# 扭曲图像
delta = 100im1 = 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()

实验结果图:

最初的实验中使用的图片是手机中存储的一些随手拍的相关的图,经过处理后要么是报错显示没有匹配的关键点,要么就是扭曲的图像过于奇葩,上面的结果图是相对较好的结果了,这里要提醒一下选择图片最好是站在一个位置拍的同一个事物的几个面为好。

四、总结

在这次的学习里我遇到了不少困难包括库的安装与实验代码的调试,同时也发现了很多自己的不足,本章内容仍旧需要继续加强学习。

python计算机视觉学习第三章——图像到图像的映射相关推荐

  1. Python爬虫学习第三章-4.3-使用xpath解析爬取全国城市名称

    Python爬虫学习第三章-4.3-使用xpath解析爬取全国城市名称   这一节主要是使用xpath解析爬取全国城市名称 这里使用的网址是:空气质量历史数据查询   这一个案例体现的点主要是xpat ...

  2. Python计算机视觉编程第三章——图像到图像的映射

    Python计算机视觉编程 图像到图像的映射 (一)单应性变换 1.1 直接线性变换算法 1.2 仿射变换 (二)图像扭曲 2.1 图像中的图像 2.2 图像配准 (三)创建全景图 3.1 RANSA ...

  3. python计算机视觉学习第七章——图像搜索

    目录 一.基于内容的图像检索 二. 视觉单词 三. 图像索引 3.1 建立数据库 3.2 添加图像 ​编辑四.在数据库中搜素图像 4.1 利用索引获取候选图像 4.2 用一幅图像进行查询 4.3 确定 ...

  4. Python计算机视觉编程 第三章 图像到图像的映射

    第三章 图像到图像的映射 3.1 单应性变换 3.1.1直接线性变换算法 3.1.2仿射变换 3.2图像扭曲 3.2.1图像中的图像 3.2.2图像配准 3.3创建全景图 3.3.1RANSAC 3. ...

  5. Python计算机视觉编程第1章基本的图像操作和处理

    目录 第 1 章 基本的图像操作和处理 1.1 PIL:Python图像处理类库 1.1.1 转换图像格式 1.1.2 创建缩略图 1.2 Matplotlib 1.2.1 绘制图像.点和线 1.2. ...

  6. [学习笔记] python深度学习---第三章 神经网络入门

    一.神经网络剖析 1. 训练神经网络主要围绕以下四个方面: (1) 层,多个层组合成网络(或模型). (2)输入数据和相应的目标. (3)损失函数,即用于学习的反馈信号. (4)优化器,决定学习过程如 ...

  7. python计算机视觉学习第8章——图像内容分类

    目录 引言 一. K邻近分类算法(KNN) 1.1 简单二维示例 1.2 用稠密SIFT作为图像特征 1.3 图像分类:手势识别 二 .贝叶斯分类器 三.支持向量机 3.1 使用LibSVM 四. 光 ...

  8. 《Python深度学习》第一章笔记

    <Python深度学习>第一章笔记 1.1人工智能.机器学习.深度学习 人工智能 机器学习 深度学习 深度学习的工作原理 1.2深度学习之前:机器学习简史 概率建模 早期神经网络 核方法 ...

  9. Python精确指南——第三章 Selenium和爬虫

    3       Selenium 3.1     介绍 网络爬虫在互联网领域有着广泛的应用. Selenium是一个页面自动化控制框架.能够模拟实际操作,自动化获取网站提供的页面资源信息. Selen ...

最新文章

  1. 数学图形(1.46)高次方程曲线
  2. Pandas简明教程:三、Pandas文件读写
  3. Python爬虫实战六之抓取爱问知识人问题并保存至数据库
  4. c#如何跳出一个函数_C# mysql 学生信息管理系统
  5. CC2538相关资料
  6. background 旋转_基于HTML5 Canvas 实现矢量工控风机叶轮旋转
  7. C++中的string类型转换为int类型
  8. SAP cloud platform + 504 gateway time out Cloud connector
  9. tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析
  10. 最简单的基于FFmpeg的AVfilter例子(水印叠加)
  11. mysql 1000万数据读取_插入1000万条数据到mysql数据库表
  12. 计算机基础17秋在线作业3,南开17秋学期《计算机应用基础》在线作业3
  13. 【收藏】基于Dockerfile搭建JAVA Tomcat运行环境
  14. linux安装思源字体下载,fedora25安装字体-以思源字体为例 适合中文用户
  15. python制作fnt字体打包工具
  16. h2o api java_h2o 准备
  17. OBIEE接受外部参数
  18. 腾讯视频 Node.js 服务是如何支撑国庆阅兵直播高并发的?
  19. leet290单词规律
  20. Android动画详解之Android 动画属性和实现方法之帧动画(二)

热门文章

  1. 网络知识 ACL NAT IPv6
  2. 谷歌浏览器翻译修复工具
  3. Nachos内存管理实现
  4. PHP+Nginx+宝塔+rsync代码同步 实现Nginx负载均衡
  5. stm32f103系列开发板控制对数码管来显示自定义时间(自主学习)
  6. 京东秒杀程序定时自动抢购茅台,原来我们一直抢不到是有原因的!(完整源码在文末)
  7. 我为什么加入了 TDengine
  8. 如何分辨IP地址的类型
  9. VIT attention实现(paddle2.2)
  10. Echarts最简单的折线图、柱图、饼图、仪表盘+sql语句