1. 图像的几何变换
1.1 图像缩放
缩放是针对图像进行大小调整,即图像放大或缩小 APIcv2.resize(src,dsize,fx=0,fy=0,interpolation=cv2.INTER_LINEAR)
参数:
- src:输入的图像
- dsize:绝对尺寸,直接指定调整后图像的大小,为一二维元组(cols, rows),注:图像的shape前两维为(rows,cols)
- fx,fy为相对尺寸,注需将dsize设置为None,fx和fy为比例因子
- interpolation:插值方法
- cv2.INTER_NEAREST - 最邻近插值
- cv2.INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
- cv2.INTER_AREA
- cv2.INTER_CUBIC - 4x4像素邻域内的双立方插值
- cv2.INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值
注意事项:
- dsize和fx/fy不能同时为0
- 插值方法,正常情况下使用默认的双线性插值就够用了。
- 几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值;但是效率和效果成反比,所以根据自己的情况酌情使用。
- 通常情况下,dst(输出图像)的大小和类型是未知的,类型是有src(原图像)继承而来的,大小是根据参数计算而来的。但加入已知dst的大小,可采用如下方式
dst = cv2.resize(src, dst.size(), 0, 0, interpolation)
import cv2 as cv
img1 = cv.imread("./image/dog.jpeg")
rows,cols = img1.shape[:2]
res = cv.resize(img1,(2*cols,2*rows),interpolation=cv.INTER_CUBIC)
res1 = cv.resize(img1,None,fx=0.5,fy=0.5)
cv.imshow("orignal",img1)
cv.imshow("enlarge",res)
cv.imshow("shrink)",res1)
cv.waitKey(0)
fig,axes=plt.subplots(nrows=1,ncols=3,figsize=(10,8),dpi=100)
axes[0].imshow(res[:,:,::-1])
axes[0].set_title("绝对尺度(放大)")
axes[1].imshow(img1[:,:,::-1])
axes[1].set_title("原图")
axes[2].imshow(res1[:,:,::-1])
axes[2].set_title("相对尺度(缩小)")
plt.show()
1.2 图像平移
将图像按照指定方向和距离,移动到相应的位置 API:cv2.warpAffine(img, M, dsize) 参数:
- img:输入图像
- M:为一2×3的矩阵,例如对于
(
x
,
y
)
(x,y)
(x,y)处的像素点,把它移动到
(
x
+
t
x
,
y
+
t
y
)
(x+t_x,y+t_y)
(x+tx?,y+ty?)处时,M矩阵应设置为:
M
=
∣
1
0
t
x
0
1
t
y
∣
M=\begin{vmatrix} 1 & 0 &t_x \\ 0 & 1 & t_y \end{vmatrix}
M=∣∣∣∣?10?01?tx?ty??∣∣∣∣? - dsize:输出图像的大小,(cols, rows)
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread("./image/image2.jpg")
rows,cols = img1.shape[:2]
M = M = np.float32([[1,0,100],[0,1,50]])
dst = cv.warpAffine(img1,M,(cols,rows))
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("平移后结果")
plt.show()
1.3 图像旋转
将图像依据某个位置并按照一定角度进行转动的过程,旋转过程中图像仍然保持原本尺寸。 图像旋转后图像的水平对称轴、垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换。 图像如何进行旋转: 假设图像逆时针旋转
θ
\theta
θ,则根据坐标转变可得旋转转换为:
{
x
′
=
r
c
o
s
(
α
?
θ
)
y
′
=
r
s
i
n
(
α
?
θ
)
\begin{cases} x^{'}=rcos(\alpha-\theta) \\ y^{'}=rsin(\alpha-\theta) \end{cases}
{x′=rcos(α?θ)y′=rsin(α?θ)? 其中:
r
=
x
2
+
y
2
,
s
i
n
α
=
y
x
2
+
y
2
,
c
o
s
α
=
x
x
2
+
y
2
r=\sqrt{x^{2}+y^{2}} ,sin\alpha=\frac{y}{\sqrt{x^{2}+y^{2}}},cos\alpha=\frac{x}{\sqrt{x^{2}+y^{2}}}
r=x2+y2
?,sinα=x2+y2
?y?,cosα=x2+y2
?x? 则会有:
[
x
′
y
′
1
]
=
[
x
y
1
]
∣
c
o
s
θ
?
s
i
n
θ
0
s
i
n
θ
c
o
s
θ
0
0
0
1
∣
[x^{'}\quad y^{'}\quad 1]= [x\quad y\quad 1]\begin{vmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta &0\\ 0 & 0 & 1 \end{vmatrix}
[x′y′1]=[xy1]∣∣∣∣∣∣?cosθsinθ0??sinθcosθ0?001?∣∣∣∣∣∣? 即:
{
x
′
=
x
c
o
s
θ
+
y
s
i
n
θ
y
′
=
?
x
s
i
n
θ
+
y
c
o
s
θ
\begin{cases} x^{'} = xcos\theta+ysin\theta \\ y^{'} = -xsin\theta+ycos\theta \end{cases}
{x′=xcosθ+ysinθy′=?xsinθ+ycosθ? 图像在旋转完成后,还需要修正原点的位置,因为原图像中的坐标原点在图像的左上角,经旋转后的图像的大小会有所变换,需要进行原点修正。
假设在旋转时,是以旋转中心为坐标原点的,那么旋转结束后还需要将坐标原点移动到图像左上角,即还需要进行一次变换。
[
x
′
′
y
′
′
1
]
=
[
x
′
y
′
1
]
∣
1
0
0
0
?
1
0
l
e
f
t
t
o
p
1
∣
=
[
x
y
1
]
∣
c
o
s
θ
?
s
i
n
θ
0
s
i
n
θ
c
o
s
θ
0
0
0
1
∣
∣
1
0
0
0
?
1
0
l
e
f
t
t
o
p
1
∣
[x^{''}\quad y^{''}\quad 1] = [x^{'}\quad y^{'}\quad 1]\begin{vmatrix} 1 & 0 & 0 \\ 0 & -1 &0\\ left & top & 1 \end{vmatrix}= [x\quad y\quad 1]\begin{vmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta &0\\ 0 & 0 & 1 \end{vmatrix}\begin{vmatrix} 1 & 0 & 0 \\ 0 & -1 &0\\ left & top & 1 \end{vmatrix}
[x′′y′′1]=[x′y′1]∣∣∣∣∣∣?10left?0?1top?001?∣∣∣∣∣∣?=[xy1]∣∣∣∣∣∣?cosθsinθ0??sinθcosθ0?001?∣∣∣∣∣∣?∣∣∣∣∣∣?10left?0?1top?001?∣∣∣∣∣∣? opencv中图像旋转首先根据旋转角度和旋转中心获取旋转矩阵,然后根据旋转矩阵进行变换,即可实现任意角度和任意中心的旋转效果。 获取旋转矩阵API:cv2.getRotarionMatrix2D(center, angle, scale) 参数:
- center:旋转中心
- angle:旋转角度
- scale:缩放比列
返回值为 M:旋转矩阵 进行旋转变换API:cv.warpAffine(img,M,(cols,rows)) 参数: - img:要旋转的图像
- M:旋转矩阵
- (cols,rows):平移后图像尺寸
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("./image/image2.jpg")
rows,cols = img.shape[:2]
M = cv.getRotationMatrix2D((cols/2,rows/2),90,1)
dst = cv.warpAffine(img,M,(cols,rows))
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("旋转后结果")
plt.show()
1.4 仿射变换
图像的仿射变换设计到图像的形状、位置和角度的变换,是深度学习预处理常用的功能,仿射变换主要是对图像的缩放、旋转、翻转和平移等操作的组合。 opencv中,仿射变换矩阵是一个2×3的矩阵:
M
=
[
A
B
]
=
∣
a
00
a
01
b
0
a
10
a
11
b
1
∣
M=[A \quad B]=\begin{vmatrix} a_{00} & a_{01} & b_0 \\ a_{10} & a_{11} & b_1 \end{vmatrix}
M=[AB]=∣∣∣∣?a00?a10??a01?a11??b0?b1??∣∣∣∣? 其中左边的2×2子矩阵
A
A
A是线性变换矩阵,右边的2×1子矩阵
B
B
B是平移项:
A
=
∣
a
00
a
01
a
10
a
11
∣
,
B
=
∣
b
0
b
1
∣
A=\begin{vmatrix} a_{00} & a_{01} \\ a_{10} & a_{11} \end{vmatrix},\quad B=\begin{vmatrix} b_0 \\ b_1 \end{vmatrix}
A=∣∣∣∣?a00?a10??a01?a11??∣∣∣∣?,B=∣∣∣∣?b0?b1??∣∣∣∣? 对于图像上的任一位置
(
x
,
y
)
(x, y)
(x,y),仿射变换执行的是如下的操作:
T
a
f
f
i
n
e
=
A
∣
x
y
∣
+
B
=
M
∣
x
y
1
∣
T_{affine} = A \begin{vmatrix} x \\ y \end{vmatrix} + B = M \begin{vmatrix} x \\ y \\ 1 \end{vmatrix}
Taffine?=A∣∣∣∣?xy?∣∣∣∣?+B=M∣∣∣∣∣∣?xy1?∣∣∣∣∣∣? 注:对于图像而言,宽度方向是x,高度方向是y,坐标的顺序和图像像素对应下标一致。那么原点的位置不是左下角而是右上角,y的方向也不是向上而是向下。 在仿射变换中,原图中所有的平行线在结果图像中同样平行。为得到仿射变换矩阵M,需要从原图像中找到三个点以及他们在输出图像中的位置,然后利用API:cv2.getAffineTransform(pts1, pts2) 参数pts1和pts2分别是原图和输出图的对应点(pts1仿射变换后即为pts2) 仿射变换API:cv2.warpAffine(img, M, (cols, rows))
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("./image/image2.jpg")
rows,cols = img.shape[:2]
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[100,100],[200,50],[100,250]])
M = cv.getAffineTransform(pts1,pts2)
dst = cv.warpAffine(img,M,(cols,rows))
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("仿射后结果")
plt.show()
1.5 透射变换
透射变换是视角变换的结构,是指利用透视中心、像点、目标点三点共线的条件,按照透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。 其本质为将图像投影到一个新的视平面,其通用变换公式为:
[
x
′
y
′
z
′
]
=
[
u
v
w
]
∣
a
00
a
01
a
02
a
10
a
11
a
12
a
20
a
21
a
22
∣
[x_{'} \quad y_{'} \quad z_{'}] = [u \quad v \quad w] \begin{vmatrix} a_{00} & a_{01} & a_{02} \\ a_{10} & a_{11} & a_{12} \\ a_{20} & a_{21} & a_{22} \end{vmatrix}
[x′?y′?z′?]=[uvw]∣∣∣∣∣∣?a00?a10?a20??a01?a11?a21??a02?a12?a22??∣∣∣∣∣∣? 其中,
(
u
,
v
)
(u, v)
(u,v)是原始的图像像素坐标,
w
w
w取值为1.
(
x
=
x
′
/
z
′
,
y
=
y
′
/
z
′
)
(x = x^{'} / z^{'}, y = y^{'} / z^{'})
(x=x′/z′,y=y′/z′)是透射变换后的结果。后面的矩阵称为透视变换矩阵,一般情况下将其分为三部分:
T
=
∣
a
00
a
01
a
02
a
10
a
11
a
12
a
20
a
21
a
22
∣
=
∣
T
1
T
2
T
3
a
22
∣
T = \begin{vmatrix} a_{00} & a_{01} & a_{02} \\ a_{10} & a_{11} & a_{12} \\ a_{20} & a_{21} & a_{22} \end{vmatrix} = \begin{vmatrix} T_1 & T_2 \\ T_3 & a_{22} \\ \end{vmatrix}
T=∣∣∣∣∣∣?a00?a10?a20??a01?a11?a21??a02?a12?a22??∣∣∣∣∣∣?=∣∣∣∣?T1?T3??T2?a22??∣∣∣∣? 其中,
T
1
T_1
T1?表示对图像进行线性变换,
T
2
T_2
T2?对图像进行平移,
T
3
T_3
T3?表示对图像进行投射变换,
a
22
a_{22}
a22?一般设为1 在opencv中,需要找到四个点,其中任意三个不共线,之后通过APIcv.getPerspectiveTransform 找到变换矩阵T,透射变换APIcv.warpPerspective
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("./image/image2.jpg")
rows,cols = img.shape[:2]
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[100,145],[300,100],[80,290],[310,300]])
T = cv.getPerspectiveTransform(pts1,pts2)
dst = cv.warpPerspective(img,T,(cols,rows))
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("透射后结果")
plt.show()
1.6 图像金字塔
图像金字塔是图像多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。 图像金字塔用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低且来源同一张原始图像的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。 金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似,层级越高,图像越小,分辨率越低。 上采样API:cv2.pytUp(img) ;下采样API:cv2.pyrDown(img)
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("./image/image2.jpg")
up_img = cv.pyrUp(img)
img_1 = cv.pyrDown(img)
cv.imshow('enlarge', up_img)
cv.imshow('original', img)
cv.imshow('shrink', img_1)
cv.waitKey(0)
cv.destroyAllWindows()
|