第5章

几 何 变 换




图像在获取过程中受到多种因素的影响,由于实际获取的图像和预期图像存在差异,因此需要对图像进行调整。例如获取的目标存在偏斜,通过对图像进行透视变换可以矫正图像。当用图像训练模型时,通过对图像进行变换可以获取更多图像特征、样本量。图像的几何变换就是指在不改变图像原有内容的基础上,对图像的像素空间位置进行改变,以达到变换图像中像素位置的目的。图像的几何变换包括图像缩放、翻转、平移、错切、旋转、仿射变换、透视变换。

5.1图像缩放

图像缩放(Image Scaling)是指调整数字图像的尺寸。图像缩放既会改变图像的大小,也会影响图像的清晰度和平滑度。

5.1.1基本原理

图像缩放是对图像像素坐标进行映射,已知sx、sy为缩放系数,原图像素坐标(x0,y0)与缩放后像素坐标(x,y)的关系如下: 
x=sx·x0y=sy·y0(51)
用矩阵表示: 
xy1=sx000sy0001x0y01(52)
图像放缩有多种方法,常用的有最近邻法、双线性插值法。

1. 最近邻

最近邻缩放的原理是根据缩放系数确定新图像每个位置上的像素。已知原图的尺寸为(h,w),缩放后图像的尺寸为(nh,nw),缩放系数为nhh,nww,用新图像坐标除以缩放系数便可得到原图对应的坐标,如图51所示,原图的尺寸为(6,6),把原图宽和高缩小为原来的二分之一,缩放后图像比例为(3,3),缩放系数为36,36,新图像坐标(0,0)对应的原图坐标为03/6,03/6。新图像坐标(3,3)对应原图33/6,33/6坐标的像素。以此类推,得到新图像所有元素的像素,用最近邻放大图像也是同样的原理。



图51最近邻法缩放示例


【例51】最近邻法缩放图像。

解: 

(1) 读取彩色图像。

(2) 最近邻缩放。

(3) 显示图像,代码如下: 



#chapter5_1.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def neighbor_size(img,nh,nw):

'''

最近邻缩放

:param img: 读取彩色图像

:param nh: 新设置的图像高度,new_height

:param nw: 新设置的图像宽度,new_width

:return: 新图像

'''

h,w,c = img.shape

#新图像的尺寸

img_new = np.zeros([nh, nw, c], np.uint8)

#缩放系数

h_scale = nh * 1.0 / h

w_scale = nw * 1.0 / w

#计算新图像上每个位置对应的像素







for i in range(nh):

for j in range(nw):

img_new[i, j] = img[int(i / h_scale), int(j / w_scale)]

return img_new



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 最近邻缩放

#缩小

img1 = neighbor_size(img,nh=40,nw=80)

#放大

img2 = neighbor_size(img,nh=1600,nw=800)

#3. 显示图像

plt.subplot(131)

plt.axis('off')

plt.imshow(img2[..., ::-1])

plt.subplot(132)

plt.axis('off')

plt.imshow(img[..., ::-1])

plt.subplot(133)

plt.axis('off')

plt.imshow(img1[..., ::-1])



运行结果如图52所示。



图52最近邻法缩放图像




图53双线性插值法


2. 双线性插值法

在最近邻法中,根据缩放系数计算新图坐标在原图上的位置可能不是整数。例如缩放系数为2,新图坐标(7,7)对应原图(3.5,3.5)的位置,最近邻法采取取整方法得到(3,3),这种方法简单,但是会损失一部分图像信息,而插值法可以弥补最近邻法的不足。

如图53所示,已知点P00(x1,y2)、P10(x1,y1)、P11(x2,y1)、P01(x2,y2),求函数f在P(x,y)点的值。先在x方向线性插值,根据P10、P11、P00、P01计算f(R0)、f(R1),再在y方向线性插值,根据f(R0)、f(R1)计算f(P)。

在x方向线性插值: 
f(R0)≈x2-xx2-x1f(P10)+x-x1x2-x1f(P11)当R0=(x,y1)(53)
f(R1)≈x2-xx2-x1f(P00)+x-x1x2-x1f(P01)当R1=(x,y2)(54)
在y方向线性插值: 
f(P)≈y2-yy2-y1f(R0)+y-y1y2-y1f(R1)(55)
f(P)=f(x,y)≈y2-yy2-y1×x2-xx2-x1f(P10)+y2-yy2-y1×x-x1x2-x1f(P11)+
y-y1y2-y1×x2-xx2-x1f(P00)+y-y1y2-y1×x-x1x2-x1f(P01)(56)

【例52】双线性插值缩放图像。

解: 

(1) 读取彩色图像。

(2) 双线性插值缩放。

(3) 显示图像,代码如下: 



#chapter5_2.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def insert_size(img, nh, nw):

'''

双线性插值

:param img: 彩色图像

:param nh: 新图像的高度

:param nw: 新图像的宽度

:return: 新图像

'''

#1. 根据原图、新图尺寸[nh, nw]计算缩放系数

#原图尺寸

h, w, c = img.shape

#新图像

img_new = np.zeros([nh, nw, c], np.uint8)

#缩放系数

h_scale = nh * 1.0 / h

w_scale = nw * 1.0 / w

#2. 对新图每个位置进行线性插值






for i in range(nh):

for j in range(nw):

#新图坐标[i,j]在原图上对应的非整数作坐标[i_new,j_new]

i_new = np.round(i / h_scale, 1)    #例如i_new = 0.6

j_new = np.round(j / w_scale, 1)  #例如j_new = 0.8

#把i_new和j_new转换成字符串,获取坐标整数部分和小数部分

i_new1, i_new2 = str(i_new).split('.')  #('0', '6')

j_new1, j_new2 = str(j_new).split('.')  #('0', '8')

i_new1, i_new2 = int(i_new1), int(i_new2) * 0.1  #0, 0.6

j_new1, j_new2 = int(j_new1), int(j_new2) * 0.1  #0, 0.8

#获取目标坐标[i_new,j_new]周围4个整数坐标的像素img_00、img_10、img_01、
#img_11

img_00 = img[i_new1, j_new1]  #img[0,0]    左上角

#处理越界像素

if i_new1 + 1 <= h - 1 and j_new1 + 1 <= w - 1:

img_10 = img[i_new1 + 1, j_new1]  #img[1,0]    左下角

img_01 = img[i_new1, j_new1 + 1]  #img[0,1]    右上角

img_11 = img[i_new1 + 1, j_new1 + 1]  #img[1 1]    右下角

#双线性插值

r0 = img_10 * (1 - j_new2) + img_11 * j_new2

r1 = img_00 * (1 - j_new2) + img_01 * j_new2

r = r1 * (1 - i_new2) + r0 * i_new2

img_new[i, j] = r

else:

img_new[i, j] = img_00

return img_new



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 双线性插值缩放

#缩小

img1 = insert_size(img, nh=40, nw=80)

#放大

img2 = insert_size(img, nh=1600, nw=800)

#3. 显示图像

plt.subplot(131)

plt.axis('off')

plt.imshow(img2[..., ::-1])

plt.subplot(132)

plt.axis('off')

plt.imshow(img[..., ::-1])

plt.subplot(133)

plt.axis('off')

plt.imshow(img1[..., ::-1])



运行结果如图54所示。



图54双线性插值缩放图像


5.1.2语法函数

OpenCV调用函数cv2.resize()实现图像缩放,其语法格式为 

dst=cv2.resize(src,dsize,fx,fy,interpolation)

(1) dst: 输出图像。

(2) src: 输入图像。

(3) dsize: 输入图像尺寸。

(4) fx,fy: 输入图像与输出图像的尺寸比例。dsize与fx、fy设置一项即可。

(5) interpolation: 插值方法,见表51。

表51插值方法



类型注释

cv2.INTER_LINEAR双线性插值
cv2.INTER_NEAREST最邻近插值
cv2.INTER_CUBIC三次样条插值
cv2.INTER_AREA区域插值,根据当前像素周边区域的像素实现当前像素的采样
cv2.INTER_LANCZOS4一种使用8×8 近邻的Lanczos插值方法
cv2.INTER_LINEAR_EXACT位精确双线性插值
cv2.INTER_MAX差值编码掩码
cv2.WARP_FILL_OUTLIERS标志,填补目标图像中的所有像素。如果它们中的一些对应源图像中的奇异点(离群值),则将它们设置为0
cv2.WARP_INVERSE_MAP标志,逆变换


【例53】图像堆叠。

解: 

(1) 读取彩色图像。

(2) 缩放图像并堆叠。

(3) 显示图像,代码如下: 



#chapter5_3.py

import cv2

import matplotlib.pyplot as plt


#1. 读取彩色图像

img = cv2.imread('pictures/L3.png', 1)

#2. 用OpenCV自带函数缩放图像并堆叠

img1 = cv2.resize(img, (120, 120), interpolation=cv2.INTER_LINEAR)

img2 = cv2.resize(img, None, fx=3, fy=3, interpolation=cv2.INTER_NEAREST)

#把缩放后的图像放在一起

img[-120:, -120:] = img1

h, w = img.shape[:2]

img2[-h:, -w:] = img

#3. 显示图像

plt.imshow(img2[..., ::-1])

cv2.imwrite('pictures/p5_3.jpg', img2)



运行结果如图55所示。



图55OpenCV自带函数缩放图像并堆叠


5.2图像翻转

图像翻转是指图像沿轴线进行对称变换,包括上下翻转、左右翻转、上下左右翻转。

5.2.1基本原理

对图像翻转可以视为对多维数组的操作,如图56所示,对图像进行上下翻转,相当于对图像的行进行对称变换,第i行经过翻转后为(height-i)行。对图像进行左右翻转,相当于对图像的列进行对称变换,第i列经过翻转后为(width-i)列。对图像进行上下左右翻转,相当于对图像先进行行对称变换,再对列进行对称变换。



图56图像翻转


已知原图像素坐标为(x0,y0),对应翻转后图像坐标为(x,y),原图宽和高为(h,w)。

1. 上下翻转

对图像进行上下翻转操作,(x0,y0)与(x,y)的关系如下: 
x=x0y=h-y0(57)
用矩阵表示: 
xy1=1000-1h001x0y01(58)
用代码表示: 



#通过处理数组实现翻转

h,w = img.shape[:2]

img_new = img.copy()

for j in range(h):

img_new[j]= img[h-1-j]



#通过仿射实现翻转

mirrorM = np.array([

[1, 0, 0],

[0, -1, h]

], dtype=np.float32)

img4 = cv2.warpAffine(img, mirrorM, dsize=img.shape[:2][::-1])



2. 左右翻转

对图像进行左右翻转操作,(x0,y0)与(x,y)的关系如下: 
x=w-x0y=y0(59)
用矩阵表示: 
xy1=-10w010001x0y01(510)
用代码表示: 



#通过处理数组实现翻转

h,w = img.shape[:2]

img_new = img.copy()

for j in range(h):

img_new[::,j]= img[::,w-1-j]



#通过仿射实现翻转

mirrorM = np.array([

[-1, 0, w],

[0,  1, 0]

], dtype=np.float32)

img4 = cv2.warpAffine(img, mirrorM, dsize=img.shape[:2][::-1])



3. 上下左右翻转

对图像进行上下左右翻转操作,(x0,y0)与(x,y)的关系如下: 
x=w-x0y=h-y0(511)
用矩阵表示: 
xy1=-10w0-1h001x0y01(512)
用代码表示: 



h,w = img.shape[:2]

img_new = img.copy()

img_n = img_new.copy()

for j in range(h):

img_new[j]= img[h-1-j]

for j in range(w):

img_n[::,j] = img_new[::,w-1-j]



#通过仿射实现翻转

mirrorM = np.array([

[-1, 0, h],






[0,  -1, w]

], dtype=np.float32)

img4 = cv2.warpAffine(img, mirrorM, dsize=img.shape[:2][::-1])



【例54】图像翻转源码复现。

解: 

(1) 读取图像。

(2) 图像翻转。

(3) 显示图像,代码如下: 



#chapter5_4.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def my_flip(img, flip=0):

'''

图像翻转

:param img: 原图

:param flip: 0(上下翻转),1(左右翻转),-1(上下左右翻转)

:return: 翻转图像

'''

h, w = img.shape[:2]

img_new = img.copy()

if flip == 0:

for j in range(h):

img_new[j] = img[h - 1 - j]

elif flip == 1:

for j in range(w):

img_new[::, j] = img[::, w - 1 - j]

elif flip == -1:

img_n = img_new.copy()

for j in range(h):

img_new[j] = img[h - 1 - j]

for j in range(w):

img_n[::, j] = img_new[::, w - 1 - j]

img_new = img_n

return img_new



if __name__ == '__main__':

#1. 读取图像

img = cv2.imread('pictures/L1.png', 1)

h, w = img.shape[:2]

#2. 图像翻转

img1 = my_flip(img, -1)  #上下左右翻转

img2 = my_flip(img, 0)  #上下翻转

img3 = my_flip(img, 1)  #左右翻转

#通过仿射实现镜像

mirrorM = np.array([






[-1, 0, w],

[0, 1, 0]

], dtype=np.float32)

img4 = cv2.warpAffine(img, mirrorM, dsize=img.shape[:2][::-1])

#3. 显示图像

re = np.hstack([img, img1, img2, img3, img4])

plt.axis('off')

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_7.jpeg', re)



运行结果如图57所示。



图57图像翻转


5.2.2语法函数

OpenCV调用函数cv2.flip()实现图像的翻转,其语法函数为 

dst=cv2.flip(src,flipCode)

(1) dst: 输出图像。

(2) src: 原始图像。

(3) flipCode: 旋转类型。如果flipCode为0,则表示上下翻转; 如果flipCode为正数,则表示左右翻转; 如果flipCode为负数,则表示上下左右翻转。

【例55】图像翻转。

解: 

(1) 读取图像。

(2) 图像翻转。

(3) 显示图像,代码如下: 



#chapter5_5.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



#1. 读取图像

img = cv2.imread('pictures/L1.png', 1)

#2. 图像翻转






img1 = cv2.flip(img, -1)  #上下左右翻转

img2 = cv2.flip(img, 0)   #上下翻转

img3 = cv2.flip(img, 1)   #左右翻转

#3. 显示图像

re = np.hstack([img, img1, img2, img3])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_8.jpeg', re)



运行结果如图58所示。



图58图像翻转


5.3图像平移

图像平移是指将图像中的所有点按照指定的平移量在水平或垂直方向上移动。

5.3.1基本原理

已知原图像素坐标为(x0,y0),对应翻转后图像坐标为(x,y),原图宽和高为(h,w)。对图像进行平移,水平方向和垂直方向的平移量为(r,c),其中(r,c)可以取正值或负值,平移量绝对值不能大于图像的宽和高。(x0,y0)与(x,y)的关系如下: 
x=x0+ry=y0+c(513)
用矩阵表示: 
xy1=10r01c001x0y01(514)
用代码表示: 



def move(img,r,c):

'''

:param img:

:param r:  水平方向平移量

:param c:  垂直方向平移量






:return: img1  平移后的图像

'''

h, w = img.shape[:2]

img1 = np.ones_like(img, np.uint8)

for i in range(h):

for j in range(w):

l1 = i+c

l2 = j+r

if l1>=0 and l1<h and l2>=0 and l2<w:

img1[l1,l2] = img[i,j]

return img1



【例56】图像平移代码复现。

解: 

(1) 读取彩色图像。

(2) 图像平移。

(3) 显示图像,代码如下: 



#chapter5_6.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def move(img, r, c):

'''

:param img: 原图

:param r:  水平方向平移量

:param c:  垂直方向平移量

:return: img1  平移后的图像

'''

h, w = img.shape[:2]

img1 = np.ones_like(img, np.uint8)

for i in range(h):

for j in range(w):

l1 = i + c

l2 = j + r

if l1 >= 0 and l1 < h and l2 >= 0 and l2 < w:

img1[l1, l2] = img[i, j]

return img1



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 图像平移

img1 = move(img, r=80, c=50)

img2 = move(img, r=80, c=-80)

img3 = move(img, r=-80, c=50)

img4 = move(img, r=-80, c=-80)

#3. 显示图像

re = np.hstack([img, img1, img2, img3, img4])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_9.jpeg', re)



运行结果如图59所示。



图59图像平移


5.3.2语法函数

图像平移是仿射变换的一种,OpenCV调用仿射函数cv2.warpAffine()实现图像平移,其语法格式为 

dst=cv2.warpAffine(src,M,dsize[,flags[,borderMode[,borderValue]]])

(1) dst: 仿射后的输出图像。

(2) src: 仿射的原始图像。

(3) M: 一个 2×3 的平移矩阵。

(4) dsize: 输出图像的尺寸。

(5) flags: 表示插值方法,包括线性插值(cv2.INTER_LINEAR)、最近邻插值(cv2.INTER_NEAREST)、三次样条插值(cv2.INTER_CUBIC)、区域插值(cv2.INTER_AREA)。

(6) borderMode: 边界像素处理模式(可选项),包括常量填充(cv2.BORDER_CONSTANT)、复制边界像素(cv2.BORDER_REPLICATE)、反射边界(cv2.BORDER_REFLECT)、包裹边界(cv2.BORDER_WRAP)。

(7) borderValue: 边界像素填充值,默认值为0。

【例57】图像平移。

解: 

(1) 读取彩色图像。

(2) 图像平移。

(3) 显示图像。



#chapter5_7.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

h, w = img.shape[:2]

#2. 图像平移






#向右向下

m1 = np.float32([[1, 0, 80], [0, 1, 80]])

img1 = cv2.warpAffine(img, m1, (w, h))

#向右向上

m2 = np.float32([[1, 0, 80], [0, 1, -80]])

img2 = cv2.warpAffine(img, m2, (w, h))

#向左向下

m3 = np.float32([[1, 0, -80], [0, 1, 80]])

img3 = cv2.warpAffine(img, m3, (w, h))

#向左向上

m4 = np.float32([[1, 0, -80], [0, 1, -80]])

img4 = cv2.warpAffine(img, m4, (w, h))

#3. 显示图像

re = np.hstack([img1, img2, img3, img4])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_10.jpeg', re)



运行结果如图510所示。



图510图像平移 


5.4图像错切

图像错切(shearing)是对图像在水平或垂直方向按照角度θ偏移一定的距离。图像错切分为垂直错切和水平错切,如图511(a)所示,图像y轴方向坐标不变,x轴方向坐标向右平移一定距离。在图511(b)中,图像x轴方向坐标不变,y轴方向坐标向下平移一定距离。



图511图像错切 


5.4.1基本原理

已知原图像素坐标为(x0,y0),错切后图像坐标为(x,y),原图宽和高为(h,w)。对图像进行水平错切,错切角度为θ,(x0,y0)与(x,y)的关系如下: 
x=x0+tanθ×y0y=y0(515)
用矩阵表示: 
xy1=1tanθ0010001x0y01(516)
用代码表示: 



def h_cut(img,t= 0.8):

'''

水平错切

:param img:彩色图像

:param theta:角度的正切值tan(theate)

:return:水平错切图像

'''

h, w ,c = img.shape

img_n = np.zeros([h, 2*w ,c], dtype=np.uint8)

for i in range(h):

for j in range(w):

#t = tan(theate); x_new = x_0+t*y_0 ;y_new = y_0

ind_r = int(i+t*j)

if ind_r < 2*w:

img_n[i,ind_r] = img[i,j]

return img_n



对图像进行垂直错切,错切角度为θ,(x0,y0)与(x,y)的关系如下: 
x=x0y=y0+tanθ·x0(517)
用矩阵表示: 
xy1=100tanθ10001x0y01(518)
用代码表示: 



def v_cut(img,t = 0.8):

'''

垂直错切

:param img:彩色图像

:param t:角度的正切值tan(theate)

:return:垂直错切图像






'''

h, w ,c = img.shape

img_n = np.zeros([2*h, w ,c], dtype=np.uint8)

for i in range(h):

for j in range(w):

#t = tan(theate); y_new = y_0+t*x_0 ;x_new = x_0

ind_c = int(j+t*i)

if ind_c < 2*h:

img_n[ind_c,j] = img[i,j]

return img_n



【例58】图像错切。

解: 

(1) 读取彩色图像。

(2) 图像错切。

(3) 显示图像,代码如下: 



#chapter5_8.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def h_cut(img, t=0.8):

'''

水平错切

:param img:图像

:param theta:角度的正切值tan(theate)

:return:水平错切

'''

h, w, c = img.shape

img_n = np.zeros([h, 2 * w, c], dtype=np.uint8)

for i in range(h):

for j in range(w):

#t = tan(theate); x_new = x_0+t*y_0 ;y_new = y_0

ind_r = int(i + t * j)

if ind_r < 2 * w:

img_n[i, ind_r] = img[i, j]

return img_n



def v_cut(img, t=0.8):

'''

垂直错切

:param img:图像

:param theta:角度的正切值tan(theate)

:return:垂直错切图像

'''

h, w, c = img.shape

img_n = np.zeros([2 * h, w, c], dtype=np.uint8)

for i in range(h):






for j in range(w):

#t = tan(theate); y_new = y_0+t*x_0 ;x_new = x_0

ind_c = int(j + t * i)

if ind_c < 2 * h:

img_n[ind_c, j] = img[i, j]

return img_n



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 图像错切

#水平错切

img1 = h_cut(img, t=0.8)

#垂直错切

img2 = v_cut(img, t=0.8)

#3. 显示图像

plt.subplot(131)

plt.axis('off')

plt.imshow(img[..., ::-1])

plt.subplot(132)

plt.axis('off')

plt.imshow(img1[..., ::-1])

plt.subplot(133)

plt.axis('off')

plt.imshow(img2[..., ::-1])



运行结果如图512所示。



图512图像错切 


5.4.2语法函数

图像错切是仿射变换的一种,OpenCV调用仿射函数cv2.warpAffine()实现图像错切,参考5.3.2节该函数的语法格式。

【例59】图像错切。

解: 

(1) 读取彩色图像。

(2) 图像错切。设置错切矩阵,根据错切矩阵对原图进行仿射变换。

(3) 显示图像,代码如下: 



#chapter5_9.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 图像错切

tan0 = 0.5

#水平错切

#水平错切矩阵

transM_h = np.array([

[1, tan0, 0],

[0, 1, 0]

], dtype=np.float32)

#仿射变换

img_trans_h = cv2.warpAffine(img, transM_h, dsize=(600, 600))

#垂直错切

#垂直错切矩阵

transM_v = np.array([

[1, 0, 0],

[tan0, 1, 0]

], dtype=np.float32)

#仿射变换

img_trans_v = cv2.warpAffine(img, transM_v, dsize=(600, 600)) #transM:平移矩阵;
#dsize:平移后图像的新的宽和高

#3. 显示图像

re = np.hstack([img_trans_h, img_trans_v])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_13.jpeg', re)



运行结果如图513所示。



图513图像错切


5.5图像旋转

图像旋转是指图像沿着任意点,按顺时针方向把图像的所有像素旋转θ角度。

5.5.1基本原理

如图514所示,点P0是圆心为O半径为r的圆上的一点,点P0(x0,y0)与水平方向的夹角为α,点P0按顺时针方向旋转β角度后得到P(x,y)。


图514坐标旋转 





点P0(x0,y0)用极坐标表示: 
x0=r×cosαy0=r×sinα(519)
点P(x,y)用极坐标表示: 
x=r×cos(α-β)=r×cosα×cosβ+r×sinα×sinβ
y=r×sin(α-β)=r×sinα×cosβ-r×cosα×sinβ(520)
化简得
x=x0×cosβ+y0×sinβy=-x0×sinβ+y0×cosβ(521)
用矩阵表示: 
xy1=cosβsinβ0-sinβcosβ0001x0y01(522)
旋转矩阵R为
R=cosβsinβ0-sinβcosβ0001(523)
上述坐标旋转是以图像原点为中心的,如果指定某点作为旋转中心,则图像先以原点(左上角)为中心旋转,再把旋转后的某点平移到旋转前的位置。例如,如果以C0(x0,y0)为旋转中心,则旋转后的坐标为C(x,y),旋转平移量为CC0=C0-C。用矩阵表示: 
CC0=ΔxΔy=x0-xy0-y=x0×(1-cosβ)-y0×sinβx0×sinβ+y0×(1-cosβ)(524)
则平移矩阵T为
T=10Δx01Δy001(525)
坐标经旋转和平移,对应的矩阵M为
M=TR=10Δx01Δy001cosβsinβ0-sinβcosβ0001
=cosβsinβΔx-sinβcosβΔy001
=cosβsinβx0×(1-cosβ)-y0×sinβ-sinβcosβx0×sinβ+y0×(1-cosβ)001 (526)
以图像原点为中心的旋转代码: 



def rote(img,theta):

'''

旋转

:param img:输入图像

:param theta:旋转角度

:return:旋转后的图像

'''

h, w = img.shape[:2]

#存放旋转后的图像

img1 = np.zeros_like(img, np.uint8)

#把角度转换成弧度

t = theta/180*np.pi

for i in range(h):

for j in range(w):

#r为旋转后图像的行坐标,c为旋转后图像的列坐标

r = int(-np.sin(t)*j+np.cos(t)*i)

c = int(np.cos(t)*j+np.sin(t)*i)

if r>=0 and r+1<h and c >=0 and c+1<w:

img1[r,c] = img1[r+1,c+1] = img[i,j]

return img1



以图像任意点为中心进行旋转,代码如下: 



def rote_center(img,theta,x0,y0):

'''

以任意点为旋转中心

:param img: 输入图像

:param theta:旋转角度

:param x0:旋转中心的横坐标

:param y0:旋转中心的纵坐标

:return:旋转后的图像

'''

h, w = img.shape[:2]

#存放旋转后的图像

img1 = np.zeros_like(img, np.uint8)

#把角度转换成弧度

t = theta/180*np.pi

for i in range(h):

for j in range(w):

#r为旋转后图像的行坐标,c为旋转后图像的列坐标






r = int(-np.sin(t)*j+np.cos(t)*i+ x0*np.sin(t) +y0*(1-np.cos(t)))

c = int(np.cos(t)*j+np.sin(t)*i+x0*(1- np.cos(t)) - y0 * np.sin(t))

if r>=0 and r+1<h and c>=0 and c+1<h:

img1[r,c] = img1[r+1,c+1] = img[i,j]

return img1



5.5.2语法函数

当OpenCV调用函数cv2.warpAffine()对图像进行旋转时,先通过函数cv2.getRotationMatrix2D()获取旋转矩阵,其语法格式为

retval=cv2.getRotationMatrix2D(center,angle,scale)

(1) center: 旋转的中心点。

(2) angle: 旋转角度,正数表示逆时针旋转,负数表示顺时针旋转。

(3) scale: 变换尺度(缩放大小)。

【例510】图像旋转。

解: 

(1) 读取彩色图像。

(2) 图像旋转。设置旋转矩阵,根据旋转矩阵对原图进行仿射变换。

(3) 显示图像,代码如下: 



#chapter5_10.py

import cv2

import numpy as np

import matplotlib.pyplot as plt



def rote(img, theta):

'''

旋转

:param img:输入图像

:param theta:旋转角度

:return:旋转后的图像

'''

h, w = img.shape[:2]

#存放旋转后的图像

img1 = np.zeros_like(img, np.uint8)

#把角度转换成弧度

t = theta / 180 * np.pi

for i in range(h):

for j in range(w):

#r为旋转后图像的行坐标,c为旋转后图像的列坐标

r = int(-np.sin(t) * j + np.cos(t) * i)

c = int(np.cos(t) * j + np.sin(t) * i)

if r >= 0 and r + 1 < h and c >= 0 and c + 1 < w:

img1[r, c] = img1[r + 1, c + 1] = img[i, j]

return img1



def rote_center(img, theta, x0, y0):






'''

以任意点为旋转中心

:param img: 输入图像

:param theta:旋转角度

:param x0:旋转中心的横坐标

:param y0:旋转中心的纵坐标

:return:旋转后的图像

'''

h, w = img.shape[:2]

#存放旋转后的图像

img1 = np.zeros_like(img, np.uint8)

#把角度转换成弧度

t = theta / 180 * np.pi

for i in range(h):

for j in range(w):

#r为旋转后图像的行坐标,c为旋转后图像的列坐标

r = int(-np.sin(t) * j + np.cos(t) * i + x0 * np.sin(t) + y0 * (1 - np.cos(t)))

c = int(np.cos(t) * j + np.sin(t) * i + x0 * (1 - np.cos(t)) - y0 * np.sin(t))

if r >= 0 and r + 1 < h and c >= 0 and c + 1 < w:

img1[r, c] = img1[r + 1, c + 1] = img[i, j]

return img1



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

h, w = img.shape[:2]

#2. 图像旋转

#以原点为旋转中心

img1 = rote(img, theta=30)

#以任意点为旋转中心

img2 = rote_center(img, theta=30, x0=h //2, y0=w //2)

#OpenCV自带函数旋转

rotateM = cv2.getRotationMatrix2D((w //2, h //2), 30, 1)

img_rotate = cv2.warpAffine(img, rotateM, dsize=(w, h))

#3. 显示图像

re = np.hstack([img, img1, img2, img_rotate])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_15.jpeg', re)




运行结果如图515所示。



图515图像旋转


如图515所示,图515(a)为原图,图515(b)为以图像左上角为中心,旋转30°后的图像,图515(c)和图515(d)均为以图像中心为中心,旋转30°后的图像。

5.6仿射变换

仿射变换(Affine Transform)指一个向量空间进行一次线性变换并接上一个平移,变成另一个向量空间。图像中原来的直线、平行线经过仿射变换后还是直线、平行线。仿射变换前一条直线上两条线段的比例,在变换后比例不变。

5.6.1基本原理

仿射变换可以分解为缩放、翻转、旋转和错切的组合。已知原图像素坐标为(x0,y0),仿射变换后图像坐标为(x,y),sx、sy为缩放系数,φ、θ为错切角度,β为旋转角度,r、c为宽和高平移数据,A为仿射变换矩阵。(x0,y0)与(x,y)的关系如下: 

xy1=sx000sy0001·1tanφ0tanθ10001·cosβsinβ0-sinβcosβ0001·10r01c001·x0y01 (527)
其中,
A=sx000sy0001·1tanφ0tanθ10001·cosβsinβ0-sinβcosβ0001·10r01c001=a1a2a3a4a5a6001 (528)
仿射变换化简后可得
xy1=a1a2a3a4a5a6001·x0y01=A·x0y01(529)
其中,A是线性变换(缩放、旋转、错切)和平移变换的叠加。仿射变换需先求出变换矩阵A。变换矩阵A中有6个未知参数,因此需要至少3对不共线的点来求解未知参数。已知原图上的3个不共线的点(x0,y0)、(x1,y1)、(x2,y2)和仿射变换后对应的点(x00,y00)、(x11,y11)、(x22,y22)。

用3对点写出6个方程组: 
a1·x0+a2·y0+a3=x00
a4·x0+a5·y0+a6=y00
a1·x1+a2·y1+a3=x11
a4·x1+a5·y1+a6=y11
a1·x2+a2·y2+a3=x22
a4·x2+a5·y2+a6=y22(530)
仿射变换矩阵用X表示,系数矩阵用M表示,变换后的坐标用向量B表示: 
X=[a1,a2,a3,a4,a5,a6]T(531)
B=[x00,y00,x11,y11,x22,y22]T(532)
M=
x0y01000
000x0y01
x1y11000
000x1y11
x2y21000
000x2y21(533)
M·X=B(534)
直接对M求逆,可以求出仿射变换矩阵: 
X=M-1B(535)
仿射变换的步骤如下: 

(1)  获取仿射变换矩阵。根据原图上的3个不共线的点(x0,y0)、(x1,y1)、(x2,y2)和仿射变换后对应的点(x00,y00)、(x11,y11)、(x22,y22)获取数矩阵M、变换后的坐标B,根据X=M-1B求得参数矩阵X,把仿射变换矩阵X变成仿射变换矩阵A。

(2) 仿射变换。将仿射变换矩阵的逆矩阵与目标图像的坐标相乘,便可得到对应原图的坐标,然后把目标图像坐标的像素用原图对应坐标的像素填充。如果想得到更好的变换效果,则可以用双线性插值法获取坐标的像素,代码如下: 



import cv2

import numpy as np

import matplotlib.pyplot as plt



def get_affine_map(src_points,dst_points):

'''

获取仿射变换矩阵

:param src_points: 数组类型,原图上3个不共线的点。shape:(3, 2)

:param dst_points: 数组类型,原图上3个不共线的点透视后对应的3个点。shape:(3, 2)

:return: 仿射变换矩阵

MX = B;X为仿射变换参数,B为目标图像坐标,M为系数矩阵

'''

#变换后的坐标B

B = dst_points.flatten().reshape([6,1])

#系数矩阵M

M = np.zeros([6,6])

for i in range(3):

p1 = src_points[i]

M[2*i] = [p1[0],p1[1],1,0,0,0]






M[2*i+1] = [0,0,0,p1[0],p1[1],1]

M = np.mat(M)

#X为仿射变换矩阵

X = M.I * B   #X.shape:(6,)

X = X.reshape([2, 3])

return  np.vstack([X,[0,0,1]]) #[2, 3]-->[3,3]



def affine_transform(img,X):

'''

仿射变换,根据目标图像坐标得到其在原图上的坐标,根据坐标把原图像素赋值给目标图像

out_idx = X * img_idx --> img_idx = (X.I) * out_idx

X为仿射变换矩阵,X.I为X的逆矩阵,img_idx为原图像坐标,out_idx为目标图像坐标

:param img: 原图

:param X: 仿射变换矩阵

:return: 经过仿射变换后的图

'''

#仿射变换矩阵的逆矩阵

X_IV = X.I

h, w = img.shape[:2]

#out为目标图像

out = np.zeros_like(img, np.uint8)

#遍历目标图像中的每个像素,找到在原图像上对应的位置

for i in range(h):

for j in range(w):

#目标图像坐标

idx = np.array([j,i,1]).reshape(3,1)

#原图对应的坐标,p.shape:(3, 1)

p = np.array(X_IV*idx)

img_x = int(p[0][0])

img_y = int(p[1][0])

#判断原图像坐标是否越界

if img_y >= 0 and img_y + 1 < h and img_x >= 0 and img_x + 1 < w:

out[i,j] = img[img_y,img_x]

return out



5.6.2语法函数

当OpenCV调用函数cv2.warpAffine()对图像进行仿射时,先通过函数cv2.getAffineTransform()获取仿射变换矩阵,其语法格式为

retval=cv2.getAffineTransform(src,dst)

(1) retval: 仿射变换矩阵。

(2) src: 输入图像的3个点坐标。

(3) dst: 输出图像的3个坐标。

【例511】图像仿射变换。

解: 

(1)  读取彩色图像。

(2)  仿射变换。分别用OpenCV、复现代码实现仿射变换。

(3)  显示图像,代码如下: 



#chapter5_11.py 仿射变换

import cv2

import numpy as np

import matplotlib.pyplot as plt



def get_affine_map(src_points, dst_points):

'''

获取仿射变换矩阵

:param src_points: 数组类型,原图上3个不共线的点。shape:(3, 2)

:param dst_points: 数组类型,原图上3个不共线的点透视后对应的3个点。shape:(3, 2)

:return: 仿射变换矩阵

MX = B;X为仿射变换参数,B为目标图像坐标,M为系数矩阵

'''

#变换后的坐标B

B = dst_points.flatten().reshape([6, 1])

#系数矩阵M

M = np.zeros([6, 6])

for i in range(3):

p1 = src_points[i]

M[2 * i] = [p1[0], p1[1], 1, 0, 0, 0]

M[2 * i + 1] = [0, 0, 0, p1[0], p1[1], 1]

M = np.mat(M)

#X为仿射变换矩阵

X = M.I * B  #X.shape:(6,)

X = X.reshape([2, 3])

return np.vstack([X, [0, 0, 1]])  #[3,3]



def affine_transform(img, X):

'''

仿射变换  MX=B --> M=(X.I)B 目标图像的坐标对应原图上的坐标

X为仿射变换参数,X.I为X的逆矩阵,B为目标图像坐标,M为系数矩阵

:param img: 原图

:param X: 仿射变换矩阵

:return: 经过仿射变换后的图

X.I*np.array([50,50,1]).T

'''

#仿射变换矩阵的逆矩阵

X_IV = X.I

h, w = img.shape[:2]

#out为目标图像

out = np.zeros_like(img, np.uint8)

#遍历目标图像中的每个像素,找到在原图像上对应的位置

for i in range(h):

for j in range(w):

#目标图像坐标

idx = np.array([j, i, 1]).reshape(3, 1)

#原图对应的坐标,p.shape:(3, 1)

p = np.array(X_IV * idx)

img_x = int(p[0][0])

img_y = int(p[1][0])

#判断原图像坐标是否越界






if img_y >= 0 and img_y + 1 < h and img_x >= 0 and img_x + 1 < w:

out[i, j] = img[img_y, img_x]

return out



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png', 1)

#2. 仿射变换

heigh, width, _ = img.shape

#2.1 原图不共线的3个点matSrc,仿射变换后对应的点matDst

src_points = np.float32([[0, 0], [0, heigh - 1], [width - 1, 0]])  
#左上角、左下角及右下角坐标

dst_points = np.float32([[30, 30], [100, heigh - 20], [width - 30, 100]])  
#左上角、左下角及右下角坐标

#2.2.1调用OpenCV实现仿射变换

M = cv2.getAffineTransform(src_points, dst_points)

dst = cv2.warpAffine(img, M, (width, heigh))

#2.2.2 仿射变换代码复现

X = get_affine_map(src_points, dst_points)

out = affine_transform(img, X)

#3. 显示图像

re = np.hstack([img, dst, out])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_16.jpeg', re)

print(f'M:\n{M}')

print(f'X:\n{X}')

#运行结果

'''

M:#代码复现求解的仿射变换矩阵

[[ 0.828985510.15695067 30.]

[ 0.202898550.89013453 30.]]

X:#OpenCV自带函数求解的仿射变换矩阵

[[ 0.828985510.15695067 30.]

[ 0.202898550.89013453 30.]

[ 0.0.1.]]

'''



运行结果如图516所示。



图516图像仿射变换


5.7透视变换

透视变换(Perspective Transformation)把空间三维立体投射到投影面上而得到二维平面的过程。具体做法是先将二维的图像投影到一个三维视平面上,再转换到二维坐标,因此也称为投影映射(Projective Mapping)。

5.7.1基本原理

原图像素坐标(x_0,y_0)从二维空间变换到三维空间(x,y,z),(x,y,z)为透视后的3个坐标,(x′,y′)为原图像素透视后的二维表示,A为透视变换矩阵。透视变换公式表示: 
xyz=a1a2a3a4a5a6a7a81x_0y_01=Ax_0y_01(536)
x、y再通过除以z转换成二维坐标。
x′=xz=a1x_0+a2y_0+a3a7x_0+a8y_0+1
y′=yz=a4x_0+a5y_0+a6a7x_0+a8y_0+1(537)
展开上式: 
a1·x_0+a2·y_0+a3-a7·x_0x′-a8·y·x′=x′(538)
a4·x_0+a5·y_0+a6-a7x_0·y′-a8·y_0·y′=y′(539)
已知原图上的4个点(x0,y0)、(x1,y1)、(x2,y2)、(x3,y3)和透视后图像上的4个(x00,y00)、(x11,y11)、(x22,y22)、(x33,y33),代入线性方程组,得到8个方程,求解8个未知数,用等式表示: 
a1·x0+a2·y0+a3-a7·x0·x00-a8·y0·x00=x00
a4·x0+a5·y0+a6-a7·x0·y00-a8·y0·y00=y00
a1·x1+a2·y1+a3-a7·x1·x11-a8·y1·x11=x11
a4·x1+a5·y1+a6-a7·x1·y11-a8·y1·y11=y11
a1·x2+a2·y2+a3-a7·x2·x22-a8·y2·x22=x22
a4·x2+a5·y2+a6-a7·x2·y22-a8·y2·y22=y22
a1·x3+a2·y3+a3-a7·x3·x33-a8·y3·x33=x33
a4·x3+a5·y3+a6-a7·x3·y33-a8·y3·y33=y33(540)
透视变换矩阵用X表示,系数矩阵用M表示,变换后的坐标用B表示: 
X=[a1,a2,a3,a4,a5,a6,a7,a8]T(541)
B=[x00,y00,x11,y11,x22,y22,x33,y33]T(542)
M=
x0y01000-x0·x00-y0·x00
000x0y01-x0·x00-y0·y00
x1y11000-x1·x11-y1·x11
000x1y11-x1·x11-y1·x11
x2y21000-x2·x22-y2·x22
000x2y21-x2·x22-y2·x22
x3y31000-x3·x33-y3·x33
000x3y31-x3·x33-y3·y33(543)
M·X=B(544)
直接对M求逆,可以求出透视变换矩阵: 
X=M-1B(545)
透视变换的步骤如下: 

(1)  获取透视变换矩阵。根据原图上的4个不共线的点(x0,y0)、(x1,y1)、(x2,y2)、(x3,y3)和透视变换后对应的点(x00,y00)、(x11,y11)、(x22,y22)、(x33,y33)获取系数矩阵M、变换后的坐标B,根据X=M-1B求得矩阵X,把矩阵X变成透视变换矩阵A。

(2) 透视变换。透视变换矩阵的逆矩阵与目标图像的坐标相乘而得到对应原图的坐标,把目标图像坐标的像素用原图对应坐标的像素填充,代码如下: 



import cv2

import numpy as np

import matplotlib.pyplot as plt



def get_projective_map(src_points,dst_points):

'''

获取透视变换矩阵

:param src_points: 数组类型,原图上4个不共线的点。shape:(4, 2)

:param dst_points: 数组类型,原图上4个不共线的点透视后对应的4个点。shape:(4, 2)

:return: 透视变换矩阵

'''

#变换后的坐标B

B = dst_points.flatten().reshape([8,1])

#系数矩阵M

M = np.zeros([8,8])

for i in range(4):

p1 = src_points[i]

p2 = dst_points[i]

M[2*i] = [p1[0],p1[1],1,0,0,0,-p1[0]*p2[0],-p1[1]*p2[0]]

M[2*i+1] =  [0,0,0,p1[0],p1[1],1,-p1[0]*p2[1],-p1[1]*p2[1]]

M = np.mat(M)

#X为透视变换矩阵

X = M.I * B   #X.shape:(8,)







#在X末尾添加1

X = np.vstack([X, [1]]).reshape([3,3]) #X.shape:(3,3)

return X



def projective_transform(img,X):

'''

透视变换  out_idx = X*img_idx --> img_idx = (X.I)*out_idx

X为透视变换矩阵,X.I为X的逆矩阵,img_idx为原图像坐标,out_idx为目标图像坐标

:param img: 原图

:param X: 透视变换矩阵

:return: 目标图像

'''

#透视变换矩阵的逆矩阵

X_IV = X.I

h, w = img.shape[:2]

#out为目标图像

out = np.zeros_like(img, np.uint8)

#遍历目标图像中的每个像素,找到在原图像上对应的位置

for i in range(h):

for j in range(w):

#目标图像坐标

idx = np.array([j,i,1]).reshape(3,1)

#原图对应的坐标,p.shape:(3, 1)

p = np.array(X_IV*idx)

img_x = int(p[0][0])

img_y = int(p[1][0])

#判断原图像坐标是否越界

if img_y >= 0 and img_y + 1 < h and img_x >= 0 and img_x + 1 < w:

out[i,j] = img[img_y,img_x]

return out



5.7.2语法函数

OpenCV先调用函数cv2.getPerspectiveTransform()获取透视变换矩阵,再调用函数 cv2.warpPerspective()实现透视变换。获取透视变换矩阵的语法格式为

retval=cv2.getPerspectiveTransform(src,dst)

(1) retval: 透视变换矩阵。

(2) src: 输入图像的4个顶点的坐标。

(3) dst: 输出图像的4个顶点的坐标。

透视转换的语法格式为

dst=cv2.warpPerspective(src,M,dsize[,flags[,borderMode[,borderValue]]])

(1) dst: 透视变换后的图像。

(2) src: 原图。

(3) M: 透视变换矩阵。

(4) dsize: 图像的尺寸。

(5) flags: 插值方法。

(6) borderMode: 边界填充类型。

(7) borderValue: 边界值。

【例512】透视变换。

解: 

(1) 读取彩色图像。

(2) 透视变换。分别用OpenCV、复现代码实现透视变换。

(3) 显示图像,代码如下: 



#chapter5_12.py 透视变换

import cv2

import numpy as np

import matplotlib.pyplot as plt



def get_projective_map(src_points, dst_points):

'''

获取透视变换矩阵

:param src_points: 数组类型,原图上4个不共线的点。shape:(4, 2)

:param dst_points: 数组类型,原图上4个不共线点透射后对应的4个点。shape:(4, 2)

:return: 透视变换矩阵

'''

#变换后的坐标B

B = dst_points.flatten().reshape([8, 1])

#系数矩阵M

M = np.zeros([8, 8])

for i in range(4):

p1 = src_points[i]

p2 = dst_points[i]

M[2 * i] = [p1[0], p1[1], 1, 0, 0, 0, -p1[0] * p2[0], -p1[1] * p2[0]]

M[2 * i + 1] = [0, 0, 0, p1[0], p1[1], 1, -p1[0] * p2[1], -p1[1] * p2[1]]

M = np.mat(M)

#X为透视变换矩阵

X = M.I * B  #X.shape:(8,)

#在X末尾添加1

X = np.vstack([X, [1]]).reshape([3, 3])  #X.shape:(3,3)

return X



def projective_transform(img, X):

'''

透视变换  out_idx = X*img_idx --> img_idx = (X.I)*out_idx

X为透视变换矩阵,X.I为X的逆矩阵,img_idx为原图像坐标,out_idx为目标图像坐标

:param img: 原图

:param X: 透视变换矩阵

:return: 目标图像

'''

#透视变换矩阵的逆矩阵

X_IV = X.I

h, w = img.shape[:2]

#out为目标图像

out = np.zeros_like(img, np.uint8)

#遍历目标图像中的每个像素,找到在原图像上对应的位置






for i in range(h):

for j in range(w):

#目标图像坐标

idx = np.array([j, i, 1]).reshape(3, 1)

#原图对应的坐标,p.shape:(3, 1)

p = np.array(X_IV * idx)

img_x = int(p[0][0])

img_y = int(p[1][0])

#判断原图像坐标是否越界

if img_y >= 0 and img_y + 1 < h and img_x >= 0 and img_x + 1 < w:

out[i, j] = img[img_y, img_x]

return out



if __name__ == '__main__':

#1. 读取彩色图像

img = cv2.imread('pictures/L1.png')

#2. 获取透视变换矩阵

heigh, width, _ = img.shape

#2.1 原图不共线的4个点matSrc,仿射变换后对应的点matDst

src_points = np.float32([[95, 26], [174, 105], [87, 207], [8, 140]])  
#左上角、左下角、右下角、右上角坐标

dst_points = np.float32([[25, 20], [100, 20], [180, 100], [105, 100]])  
#左上角、左下角,右下角、右上角坐标

#2.2.1 调用OpenCV实现透视变换矩阵

M = cv2.getPerspectiveTransform(src_points, dst_points)

dst = cv2.warpPerspective(img, M, (width, heigh))

#2.2.2 透视变换代码复现

X = get_projective_map(src_points, dst_points)

out = projective_transform(img, X)

#3. 显示图像

re = np.hstack([img, dst, out])

plt.imshow(re[..., ::-1])

cv2.imwrite('pictures/p5_17.jpeg', re)

print(f'M:\n{M}')

print(f'X:\n{X}')

#运行结果

'''

M:#OpenCV自带函数求解的透视变换矩阵

[[1.27430276e-01 7.16830199e-01 -6.44056729e+00]

[-3.62014572e-01 3.46287724e-01 4.48302188e+01]

[-1.07816433e-04 -6.78525945e-04 1.00000000e+00]]

X: #代码复现的透视变换矩阵

[[1.27430276e-01 7.16830199e-01 -6.44056729e+00]

[-3.62014572e-01 3.46287724e-01 4.48302188e+01]

[-1.07816433e-04 -6.78525945e-04 1.00000000e+00]]

'''



运行结果如图517所示。



图517图像透视变换


【例513】车牌摆正。

解: 

(1) 读取彩色图像。

(2) 获取透视变换矩阵。找到原图车牌4个角的坐标,设置透视变换后的坐标,调用OpenCV实现透视变换矩阵。

(3) 显示图像,代码如下: 



#chapter_13 车牌摆正

import cv2

import numpy as np

import matplotlib.pyplot as plt



#1. 读取彩色图像

img = cv2.imread('pictures/che2.png')

#2. 获取透视变换矩阵

heigh, width, _ = img.shape

#2.1 原图不共线的4个点matSrc,仿射变换后对应的点matDst

#左上角、左下角、右下角、右上角坐标

src_points = np.float32([[0, 0], [64, 5], [80, 244], [12, 246]])

dst_points = np.float32([[0, 0], [64, 0], [64, 230], [0, 230]])

#2.2.1 调用OpenCV实现透视变换矩阵

M = cv2.getPerspectiveTransform(src_points, dst_points)

dst = cv2.warpPerspective(img, M, (width + 10, heigh))

#3. 显示图像

re = np.hstack([img, dst])

plt.imshow(re[..., ::-1])

#cv2.imwrite('pictures/p5_18.jpeg', re)



运行结果如图518所示。



图518车牌摆正