2.1简介
几何变换的原理大多数都是相似的,只是变换矩该部分将对基本的几何变换进行学习阵不同,因此,我们以最常用的平移和旋转为例进行学习。在深度学习领域,常用平移、旋转、镜像等操作进行数据增广;在传统CV领域,由于某些拍摄角度的问题,我们需要对图像进行矫正处理,而几何变换正是这个处理过程的基础。
2.2学习目标
1.了解几何变换的概念与应用
2.理解平移、旋转的原理
3.掌握python-opencv下实现平移、旋转操作
2.3算法理论介绍
变换形式:
1.仿射变换的一般形式为:
[
x
y
1
]
=
[
v
w
1
]
∗
T
=
[
v
w
1
]
∗
[
t
11
t
12
0
t
21
t
22
0
t
31
t
32
1
]
\begin{array}{c} \left[ \begin{matrix} x& y& 1 \\ \end{matrix}\right] = \left[ \begin{matrix} v & w & 1 \\ \end{matrix}\right] *T= \left[ \begin{matrix} v & w & 1 \\ \end{matrix}\right] * \left[ \begin{matrix} t_{11} & t_{12} & 0 \\ t_{21} & t_{22} & 0 \\ t_{31}& t_{32} & 1 \end{matrix}\right] \end{array}
[xy1]=[vw1]∗T=[vw1]∗⎣⎡t11t21t31t12t22t32001⎦⎤
其中
T
\mathbf{T}
T是仿射变换矩阵,(
x
x
x,
y
y
y)为变换后的坐标,(
v
v
v,
w
w
w)为原坐标。
2.一些常见的坐标变换矩阵如下图所示:
也就是说,我们根据自己的目的选择不同的变换矩阵就可以了。
坐标系变换
对于缩放,平移可以以图像坐标原点(图像左上角为原点)为中心变换,这不用坐标系变换,直接按照一般形式计算即可。而对于旋转和偏移,一般是以图像中心为原点,要涉及坐标系的转换。
下图是图像坐标系和笛卡尔坐标系的区别:
因此,对于旋转和偏移,就需要3步实现:
1.将输入原图图像坐标转换为笛卡尔坐标系;
2.进行旋转计算,根据表格中的旋转矩阵;
3.将旋转后的图像的笛卡尔坐标转回图像坐标。
图像坐标系与笛卡尔坐标系转换关系:
先看下图:
令图像表示为MxN的矩阵,对于点A而言,两坐标系的坐标分别是(
0
0
0,
0
0
0)和(
−
N
/
2
-N/2
−N/2,
M
/
2
M/2
M/2),则图像某像素点(
x
′
x'
x′,
y
′
y'
y′)转换为笛卡尔坐标(
x
x
x,
y
y
y)转换关系为:
x
=
x
′
−
N
/
2
\begin{array}{l} x=x'-N/2 \end{array}
x=x′−N/2
y
=
−
y
′
+
M
/
2
\begin{array}{l} y=-y'+M/2\end{array}
y=−y′+M/2
逆变换为:
x
‘
=
x
+
N
/
2
\begin{array}{l} x‘=x+N/2 \end{array}
x‘=x+N/2
y
’
=
−
y
+
M
/
2
\begin{array}{l} y’=-y+M/2\end{array}
y’=−y+M/2
x为列,y为行
于是,根据前面说的3个步骤(3次变换),顺时针旋转的变换公式为:
[
x
y
1
]
=
[
x
′
y
′
1
]
∗
T
=
[
x
′
y
′
1
]
∗
[
1
0
0
0
−
1
0
−
0.5
∗
N
0.5
∗
M
1
]
∗
[
c
o
s
(
θ
)
−
s
i
n
(
θ
)
0
s
i
n
(
θ
)
c
o
s
(
θ
)
0
0
0
1
]
∗
[
1
0
0
0
−
1
0
0.5
∗
N
0.5
∗
M
1
]
\begin{array}{c} \left[ \begin{matrix} x& y& 1 \\ \end{matrix}\right] = \left[ \begin{matrix} x' & y' & 1 \\ \end{matrix}\right] *T= \left[ \begin{matrix} x' & y' & 1 \\ \end{matrix}\right] * \left[ \begin{matrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ -0.5*N& 0.5*M & 1 \end{matrix}\right]* \left[ \begin{matrix} cos(\theta) & -sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0& 0 & 1 \end{matrix}\right]* \left[ \begin{matrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0.5*N& 0.5*M & 1 \end{matrix}\right] \end{array}
[xy1]=[x′y′1]∗T=[x′y′1]∗⎣⎡10−0.5∗N0−10.5∗M001⎦⎤∗⎣⎡cos(θ)sin(θ)0−sin(θ)cos(θ)0001⎦⎤∗⎣⎡100.5∗N0−10.5∗M001⎦⎤
反向映射
在冈萨雷斯的《数字图像处理_第三版》中说的很清楚,前向映射就是根据原图用变换公式直接算出输出图像相应像素的空间位置,那么这会导致一个问题:可能会有多个像素坐标映射到输出图像的同一位置,也可能输出图像的某些位置完全没有相应的输入图像像素与它匹配,就是没有被映射到,造成有规律的空洞(黑色的蜂窝状)。更好的一种方式是采用 反向映射(Inverse Mapping):扫描输出图像的位置( x x x, y y y),通过 Image (为 T \mathbf{T} T的逆矩阵)计算输入图像对应的位置 ( v v v, w w w),通过插值方法决定输出图像该位置的灰度值。
插值
在上一节中已经介绍过插值的基本原理,Opencv默认采用双线性插值,我们也就采用双线性插值。
opencv-python实现
1.平移
平移是物体位置的移动。如果您知道在(x,y)方向上的位移,则将其设为( t x t_x tx, t y t_y ty),你可以创建转换矩阵 M \mathbf{M} M,如下所示:
M = [ 1 0 t x 0 1 t y ] M = \begin{bmatrix} 1 & 0 & t_x \ 0 & 1 & t_y \end{bmatrix} M=[10tx 01ty]
import numpy as np
import cv2 as cv
img = cv.imread('D:/Mechine_learning_data/lena512.bmp',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])
dst = cv.warpAffine(img,M,(cols,rows))
cv.imshow('src',img)
cv.imshow('img',dst)
cv.waitKey(0)
cv.destroyAllWindows()
cv.warpAffine()函数的第三个参数是输出图像的大小,其形式为(width,height),其中,width=列数,height
显示结果为:
2.旋转操作
图像旋转角度为 θ θ θ是通过以下形式的变换矩阵实现的:
[ 1 0 0 0 − 1 0 − 0.5 ∗ N 0.5 ∗ M 1 ] ∗ [ c o s ( θ ) − s i n ( θ ) 0 s i n ( θ ) c o s ( θ ) 0 0 0 1 ] ∗ [ 1 0 0 0 − 1 0 0.5 ∗ N 0.5 ∗ M 1 ] \begin{array}{c} \left[ \begin{matrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ -0.5*N& 0.5*M & 1 \end{matrix}\right]* \left[ \begin{matrix} cos(\theta) & -sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0& 0 & 1 \end{matrix}\right]* \left[ \begin{matrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0.5*N& 0.5*M & 1 \end{matrix}\right] \end{array} ⎣⎡10−0.5∗N0−10.5∗M001⎦⎤∗⎣⎡cos(θ)sin(θ)0−sin(θ)cos(θ)0001⎦⎤∗⎣⎡100.5∗N0−10.5∗M001⎦⎤
但是OpenCV提供了可缩放的旋转以及可调整的旋转中心,因此您可以在自己喜欢的任何位置旋转。修改后的变换矩阵为
[ α β ( 1 − α ) ⋅ c e n t e r . x − β ⋅ c e n t e r . y − β α β ⋅ c e n t e r . x + ( 1 − α ) ⋅ c e n t e r . y ] \begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot center.x - \beta \cdot center.y \ - \beta & \alpha & \beta \cdot center.x + (1- \alpha ) \cdot center.y \end{bmatrix} [αβ(1−α)⋅center.x−β⋅center.y −βαβ⋅center.x+(1−α)⋅center.y]
其中:
α = s c a l e ⋅ cos θ , β = s c a l e ⋅ sin θ \begin{array}{l} \alpha = scale \cdot \cos \theta , \ \beta = scale \cdot \sin \theta \end{array} α=scale⋅cosθ, β=scale⋅sinθ
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),45,1)
dst1 = cv.warpAffine(img,M,(cols,rows))
cv.imshow('src',img)
cv.imshow('img',dst1)
cv.waitKey(0)
cv.destroyAllWindows()
为了找到此转换矩阵,OpenCV提供了一个函数cv.getRotationMatrix2D(),其参数含义见相关博客。
输出结果: