本章讲解图像之间的变换,通过单应性变换实现图像扭曲和图像配准。
3.1 单应性变换
定义:单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换,可用于图像配准、图像纠正、纹理扭曲和创建全景图像。
设单应性变换为
H
H
H,则在齐次坐标意义下,映射二维中的点的方程为:
[
x
′
y
′
w
′
]
=
[
h
1
h
2
h
3
h
4
h
5
h
6
h
7
h
8
h
9
]
[
x
y
w
]
\begin{bmatrix} x' \\ y' \\ w' \\ \end{bmatrix} = \begin{bmatrix} h_1 &h_2 &h_3 \\ h_4 & h_5 & h_6 \\ h_7 & h_8 & h_9 \\ \end{bmatrix}\begin{bmatrix} x \\ y \\ w \\ \end{bmatrix}
⎣⎡x′y′w′⎦⎤=⎣⎡h1h4h7h2h5h8h3h6h9⎦⎤⎣⎡xyw⎦⎤
或写成
x
′
=
H
x
x' = Hx
x′=Hx
(令
w
=
1
w=1
w=1来归一化点,这样,点具有唯一的图像坐标
x
x
x和
y
y
y)
点的齐次坐标相关介绍:
- 投影平面上的任何点都可以表示成一三元组 ( x, y, z ),称之为该点的’齐次坐标或投影坐标,其中 x、y 及 z 不全为 0。
- 以齐次坐标表表示的点,若该坐标内的数值全乘上一相同非零实数,仍会表示该点。相反地,两个齐次坐标表示同一点,当且仅当其中一个齐次坐标可由另一个齐次坐标乘上一相同非零常数得取得。例如x=[x,y,w]=[ax,ay,aw]=[x/w,y/w,1]表示同一个二维点。
- 从普通坐标转换成齐次坐标时,
如果(x,y,z)是个点,则变为(x,y,z,1)
如果(x,y,z)是个向量,则变为(x,y,z,0)- 对于平移T、旋转R、缩放S这3个最常见的仿射变换,平移变换只对于点才有意义,因为普通向量没有位置概念,只有大小和方向.而旋转和缩放对于向量和点都有意义,从中可以看出,齐次坐标用于仿射变换非常方便。
对于点和变换的处理,按照列优先的原则存储点
对于聚类和分类的特征,使用行数组存储数据
在齐次坐标意义下,对点集进行归一化,是最后一行为1:
def normalize(points):
""" Normalize a collection of points in
homogeneous coordinates so that last row = 1. """
for row in points:
row /= points[-1]
return points
将点集(dim×n的数组)转换为齐次坐标表示:
def make_homog(points):
""" Convert a set of points (dim*n array) to
homogeneous coordinates. """
return vstack((points,ones((1,points.shape[1]))))
投影变换中,有一些重要的变换,如仿射变换和相似变换。
仿射变换:
[
x
′
y
′
1
]
=
[
a
1
a
2
t
x
a
3
a
4
t
y
0
0
1
]
[
x
y
1
]
\begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} a_1 & a_2 & t_x \\ a_3 & a_4 & t_y \\ 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}
⎣⎡x′y′1⎦⎤=⎣⎡a1a30a2a40txty1⎦⎤⎣⎡xy1⎦⎤
或
x
′
=
[
A
t
0
1
]
x
x'= \begin{bmatrix} A & t\\ 0 & 1 \\ \end{bmatrix}x
x′=[A0t1]x
相似变换:
[
x
′
y
′
1
]
=
[
s
cos
(
θ
)
−
s
sin
(
θ
)
t
x
s
sin
(
θ
)
s
cos
(
θ
)
t
y
0
0
1
]
[
x
y
1
]
\begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} s\cos(\theta)& -s\sin(\theta) & t_x \\ s\sin(\theta) & s\cos(\theta) & t_y \\ 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}
⎣⎡x′y′1⎦⎤=⎣⎡scos(θ)ssin(θ)0−ssin(θ)scos(θ)0txty1⎦⎤⎣⎡xy1⎦⎤
或
x
′
=
[
s
R
t
0
1
]
x
x'= \begin{bmatrix} sR & t\\ 0 & 1 \\ \end{bmatrix}x
x′=[sR0t1]x
当
s
s
s为1时,改变换能够保持距离不变,此时,变换称为刚体变换。
3.1.1 直接线性变换算法
一个完全射影变换 h 9 = 1 h_9=1 h9=1,具有8个自由度,根据对应点对可以写出两个方程,分别对应 x x x和 y y y坐标,因此,计算单应性矩阵 H H H需要4个对应点对。
公式推导:
由
[
x
′
y
′
1
]
=
[
h
1
h
2
h
3
h
4
h
5
h
6
h
7
h
8
h
9
]
[
x
y
1
]
\begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} h_1 &h_2 &h_3 \\ h_4 & h_5 & h_6 \\ h_7 & h_8 & h_9 \\ \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}
⎣⎡x′y′1⎦⎤=⎣⎡h1h4h7h2h5h8h3h6h9⎦⎤⎣⎡xy1⎦⎤
得到
x
i
′
=
h
1
x
i
+
h
2
y
i
+
h
3
h
7
x
i
+
h
8
x
i
+
h
9
x_i'=\frac{h_1x_i+h_2y_i+h_3}{h_7x_i+h_8x_i+h_9}
xi′=h7xi+h8xi+h9h1xi+h2yi+h3
y
i
′
=
h
4
x
i
+
h
5
y
i
+
h
6
h
7
x
i
+
h
8
x
i
+
h
9
y_i'=\frac{h_4x_i+h_5y_i+h_6}{h_7x_i+h_8x_i+h_9}
yi′=h7xi+h8xi+h9h4xi+h5yi+h6
即
x
i
′
(
h
7
x
i
+
h
8
x
i
+
h
9
)
=
h
1
x
i
+
h
2
y
i
+
h
3
x_i'(h_7x_i+h_8x_i+h_9)=h_1x_i+h_2y_i+h_3
xi′(h7xi+h8xi+h9)=h1xi+h2yi+h3
y
i
′
(
h
7
x
i
+
h
8
x
i
+
h
9
)
=
h
4
x
i
+
h
5
y
i
+
h
6
y_i'(h_7x_i+h_8x_i+h_9)=h_4x_i+h_5y_i+h_6
yi′(h7xi+h8xi+h9)=h4xi+h5yi+h6
构造齐次线性方程组
[
x
i
y
i
1
0
0
0
−
x
i
′
x
i
−
x
i
′
y
i
−
x
i
′
0
0
0
x
i
y
i
1
−
y
i
′
x
i
−
y
i
′
y
i
−
y
i
′
]
[
h
1
h
2
h
3
h
4
h
5
h
6
h
7
h
8
h
9
]
=
[
0
0
]
\begin{bmatrix} x_i & y_i & 1& 0&0&0 &-x_i'x_i & -x_i'y_i &-x_i'\\ 0 & 0& 0 & x_i &y_i &1& -y_i'x_i & -y_i'y_i & -y_i'\\ \end{bmatrix} \begin{bmatrix} h_1\\h_2\\h_3\\h_4\\h_5\\h_6\\h_7\\h_8\\h_9 \\ \end{bmatrix}= \begin{bmatrix} 0 \\ 0 \\ \end{bmatrix}
[xi0yi0100xi0yi01−xi′xi−yi′xi−xi′yi−yi′yi−xi′−yi′]⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡h1h2h3h4h5h6h7h8h9⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=[00]
即
[
x
1
y
1
1
0
0
0
−
x
1
′
x
1
−
x
1
′
y
1
−
x
1
′
0
0
0
x
1
y
1
1
−
y
1
′
x
1
−
y
1
′
y
1
−
y
1
′
x
2
y
2
1
0
0
0
−
x
2
′
x
2
−
x
2
′
y
2
−
x
2
′
0
0
0
x
2
y
2
1
−
y
2
′
x
2
−
y
2
′
y
2
−
y
2
′
⋮
⋮
⋮
]
[
h
1
h
2
h
3
h
4
h
5
h
6
h
7
h
8
h
9
]
=
0
\begin{bmatrix} x_1 & y_1 & 1& 0&0&0 &-x_1'x_1 & -x_1'y_1 &-x_1'\\ 0 & 0& 0 & x_1 &y_1 &1& -y_1'x_1 & -y_1'y_1 & -y_1'\\x_2 & y_2 & 1& 0&0&0 &-x_2'x_2 & -x_2'y_2 &-x_2'\\ 0 & 0& 0 & x_2 &y_2 &1& -y_2'x_2 & -y_2'y_2 & -y_2'\\ & \vdots &&& \vdots &&& \vdots \end{bmatrix} \begin{bmatrix} h_1\\h_2\\h_3\\h_4\\h_5\\h_6\\h_7\\h_8\\h_9 \\ \end{bmatrix}= \color{black}{0}
⎣⎢⎢⎢⎢⎢⎡x10x20y10y20⋮10100x10x20y10y2⋮0101−x1′x1−y1′x1−x2′x2−y2′x2−x1′y1−y1′y1−x2′y2−y2′y2⋮−x1′−y1′−x2′−y2′⎦⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡h1h2h3h4h5h6h7h8h9⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=0
可记为
A
h
=
0
Ah=0
Ah=0,其中
A
A
A是一个具有对应点对二倍数量行数的矩阵。
当方程数量远大于未知量时,认为是超定方程,可以尝试查找最小二乘解。可以使用SVD(Singular Value Decomposition,奇异值分解)算法找到 H H H的最小二乘解。
A t = b At=b At=b,矩阵的最小二乘法为 A T A t = A T b A^TAt=A^Tb ATAt=ATb, t = ( A T A ) − 1 A T b t=(A^TA)^{-1}A^Tb t=(ATA)−1ATb
在Python中有现有的函数来使用svd,即U,S,V=linalg.svd(A)
。最小二乘解即为矩阵SVD分解后所的矩阵V的最后一行,该行经过变形后得到矩阵H,然后对该矩阵进行处理和归一化,返回输出。
用直接线性变换算法计算单应性矩阵:
def H_from_points(fp,tp):
""" Find homography H, such that fp is mapped to tp
using the linear DLT method. Points are conditioned
automatically. """
if fp.shape != tp.shape:
raise RuntimeError('number of points do not match')
# condition points (important for numerical reasons)
# --from points--
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)
# --to points--
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)
# create matrix for linear method, 2 rows for each correspondence pair
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))
# decondition
H = dot(linalg.inv(C2),dot(H,C1))
# normalize and return
return H / H[2,2]
3.1.2 仿射变换
仿射变换( h 7 = h 8 = 0 , h 9 = 1 h_7=h_8=0,h_9=1 h7=h8=0,h9=1)具有6个自由度,需要3个对应点对,6个约束条件,推导方式类似于3.1.1节内容。
用仿射变换算法计算单应性矩阵:
def Haffine_from_points(fp,tp):
""" Find H, affine transformation, such that
tp is affine transf of fp. """
if fp.shape != tp.shape:
raise RuntimeError('number of points do not match')
# condition points
# --from points--
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)
# --to points--
m = mean(tp[:2], axis=1)
C2 = C1.copy() #must use same scaling for both point sets
C2[0][2] = -m[0]/maxstd
C2[1][2] = -m[1]/maxstd
tp_cond = dot(C2,tp)
# conditioned points have mean zero, so translation is zero
A = concatenate((fp_cond[:2],tp_cond[:2]), axis=0)
U,S,V = linalg.svd(A.T)
# create B and C matrices as Hartley-Zisserman (2:nd ed) p 130.
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]))
# decondition
H = dot(linalg.inv(C2),dot(H,C1))
return H / H[2,2]
3.2 图像扭曲
对图像块应用仿射变换,我们将其称之为图像扭曲(或者仿射扭曲)。
from scipy import ndimage
from PIL import Image
from pylab import *
im = array(Image.open(r"C:\Users\13121\Desktop\test.jpg").convert('L'))
H = array([[1.4,0.05,-100],[0.05,1.5,-100],[0,0,1]])
im2 = ndimage.affine_transform(im,H[:2,:2],(H[0,2],H[1,2]))
figure()
gray()
subplot(121)
axis('off')
imshow(im)
subplot(122)
axis('off')
imshow(im2)
show()
3.2.1 图像中的图像
图像扭曲的例子之一。
它能够将图像或者图像的一部分放置在另一幅图像中,使得它们能够和指定区域或者标志物对齐。
def image_in_image(im1,im2,tp):
""" Put im1 in im2 with an affine transformation
such that corners are as close to tp as possible.
tp are homogeneous and counter-clockwise from top left. """
# points to warp from
m,n = im1.shape[:2]
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
# compute affine transform and apply
H = 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
im1 = array(Image.open(r"C:\Users\13121\Desktop\a1.jpg").convert('L'))
im2 = array(Image.open(r"C:\Users\13121\Desktop\a5.jpg").convert('L'))
# set to points
tp = array([[150,280,280,150],[50,50,270,270],[1,1,1,1]])
im3 = image_in_image(im1,im2,tp)
figure()
gray()
subplot(141)
axis('off')
imshow(im1)
subplot(142)
axis('off')
imshow(im2)
subplot(143)
axis('off')
imshow(im3)
show()
坐标值通过查看绘制的图像(在PyLab图像中,鼠标的坐标显示在图像底部附近)手工确定或用PyLab类库中的ginput()函数获得。
ginput()函数的应用:
#定位获取点坐标
import cv2
img = cv2.imread(r"C:\Users\13121\Desktop\a3.jpg")
def on_EVENT_LBUTTONDOWN(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
xy = "%d,%d" % (x, y)
cv2.circle(img, (x, y), 1, (255, 0, 0), thickness=-1)
cv2.putText(img, xy, (x, y), cv2.FONT_HERSHEY_PLAIN,
1.0, (0, 255, 0), thickness=1)
cv2.imshow("image", img)
print(xy)
cv2.namedWindow("image")
cv2.setMouseCallback("image", on_EVENT_LBUTTONDOWN)
cv2.imshow("image", img)
while (True):
try:
cv2.waitKey(100)
except Exception:
cv2.destroyAllWindows()
break
cv2.waitKey(0)
cv2.destroyAllWindows()
利用了仿射变换(Haffine_from_points()函数)计算了单应性矩阵,会返回给对应点对的最优仿射变换,但并不是所有场合都适用,在具有很强透射效应的情况下,使用射影变换输出图像,我们不可能使用同一个仿射变换将全部4个角点变换到他们的目标位置,所以可以选择三个角点将一幅图像进行扭曲,即将图像分成两个三角形,然后对他们分别进行扭曲图像操作。
代码如下(创建两个三角形,为每个三角形创建Alpha图像,然后将所有的图像合并起来):
def alpha_for_triangle(points,m,n):
""" Creates alpha map of size (m,n)
for a triangle with corners defined by points
(given in normalized homogeneous coordinates). """
alpha = zeros((m,n))
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: #all coefficients positive
alpha[i,j] = 1
return alpha
# set from points to corners of im1
m,n = im1.shape[:2]
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
# first triangle
tp2 = tp[:,:3]
fp2 = fp[:,:3]
# compute H
H = Haffine_from_points(tp2,fp2)
im1_t = ndimage.affine_transform(im1,H[:2,:2],
(H[0,2],H[1,2]),im2.shape[:2])
# alpha for triangle
alpha = alpha_for_triangle(tp2,im2.shape[0],im2.shape[1])
im3 = (1-alpha)*im2 + alpha*im1_t
# second triangle
tp2 = tp[:,[0,2,3]]
fp2 = fp[:,[0,2,3]]
# compute H
H = Haffine_from_points(tp2,fp2)
im1_t = ndimage.affine_transform(im1,H[:2,:2],
(H[0,2],H[1,2]),im2.shape[:2])
# alpha for triangle
alpha = alpha_for_triangle(tp2,im2.shape[0],im2.shape[1])
im4 = (1-alpha)*im3 + alpha*im1_t
subplot(144)
imshow(im4)
axis('off')
show()
运行结果如下 :
3.2.2 分段函数扭曲
3.2.1中的仿射扭曲使用三角形图像块来完成角点的精确匹配,本小节使用的是对应点对集合之间的常用扭曲方式,三角化这些点的方法是狄洛克三角剖分方法。
from PIL import Image
from pylab import *
import numpy as np
from scipy.spatial import Delaunay
x,y = array(np.random.standard_normal((2,100)))
tri = Delaunay(np.c_[x,y]).simplices
figure()
gray()
subplot(121)
axis('off')
plot(x,y,'*')
for t in tri:
t_ext = [t[0], t[1], t[2], t[0]] # add first point to end
subplot(122)
plot(x[t_ext],y[t_ext],'r')
plot(x,y,'*')
axis('off')
show()
分段仿射图像扭曲的通用扭曲函数(对每个颜色通道进行扭曲):
(方便起见,把warp.py和homography.py复制到写的代码所在文件里,并添加部分修改:from scipy.spatial import Delaunay
、函数def triangulate_points(x,y)里面的内容改成tri = Delaunay(np.c_[x,y]).simplices
)
import warp
from PIL import Image
from pylab import *
# 打开图像,并将其扭曲
fromim = array(Image.open('a1.jpg').convert('L'))
x, y = meshgrid(range(5), range(6))
x = (fromim.shape[1] / 4) * x.flatten()
y = (fromim.shape[0] / 5) * y.flatten()
# 三角剖分
tri = warp.triangulate_points(x, y)
# 打开图像
im = array(Image.open('a5.jpg').convert('L'))
gray()
imshow(im)
# 手工选取目标点
tp = plt.ginput(30)
for i in range(0, len(tp)):
tp[i] = list(tp[i])
tp[i][0] = int(tp[i][0])
tp[i][1] = int(tp[i][1])
tp = array(tp)
# 将点转换成齐次坐标
fp = vstack((y, x, ones((1, len(x)))))
tp = vstack((tp[:, 1], tp[:, 0], ones((1, len(tp)))))
# 扭曲三角形
im = warp.pw_affine(fromim, im, fp, tp, tri)
# 绘制图像
figure()
imshow(im)
# 绘制三角形
warp.plot_mesh(tp[1], tp[0], tri)
axis('off')
show()
有报错,目前还没有解决。
3.2.3 图像配准
图像配准可对图像进行变换,使得变换后的图像能够在常见的坐标系中对齐,它是图像对比和进行更精细的图像分析的重要前奏。
3.3 创建全景图
全景拼接的基础流程如下:
(1)针对同一场景拍摄系列图像
(2)提取图像的特征和匹配
(3)将匹配转化成齐次坐标点
(4)估计单应性矩阵
(5)拼接图像
随即一致性采样,即RANSAC(RANdom SAmple Consensus)时用来找到正确模型来拟合带有噪声数据的迭代方法,能够在描述正确数据点的同时摒弃噪声点。
稳健的单应性矩阵估计不关心该扭曲例子中的正确点对,仅需该函数的第一个输出(单应性矩阵)
4个点对时计算单应性矩阵所需的最少数目。
在连续图像对间使用SIFT特征寻找匹配对应点对:
图像拼接:
from pylab import *
from numpy import *
from PIL import Image
import numpy as np
olderr = np.seterr(all='ignore')
# If you have PCV installed, these imports should work
import homography, warp
import sift
np.seterr(invalid='ignore')
"""
This is the panorama example from section 3.3.
"""
# 设置数据文件夹的路径
featname = ['C:/Users/13121/Desktop/pics/' + str(i + 1) + '.sift' for i in range(5)]
imname = ['C:/Users/13121/Desktop/pics/' + str(i + 1) + '.jpg' for i in range(5)]
# 提取特征并匹配使用sift算法
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()
fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0] # im1 到im2 的单应性矩阵
fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0] # im0 到im1 的单应性矩阵
tp, fp = convert_points(2) # 注意:点是反序的
H_32 = homography.H_from_ransac(fp, tp, model)[0] # im3 到im2 的单应性矩阵
tp, fp = convert_points(3) # 注意:点是反序的
H_43 = homography.H_from_ransac(fp, tp, model)[0] # im4 到im3 的单应性矩阵
# 扭曲图像
delta = 2000 # 用于填充和平移
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()
课本上的代码运行会出错误:
需要添加代码:
import numpy as np
olderr = np.seterr(all='ignore')
np.seterr(invalid='ignore')
提示:拼接图像耗时很久,请耐心等待…