数据增广:旋转,缩放,平移以及错切

本文介绍了深度学习中如何使用OpenCV进行图像的旋转、缩放、平移及错切操作,通过实例展示仿射变换矩阵的计算和应用,包括了基本原理、opencv函数以及错切变换的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在深度学习(图像领域)中,为了提升训练样本数量数据增广是非常常见的手段。比如:

  • 随机水平翻转
  • 随机色调(H)、饱和度(S)、明度(V)调整
  • 随机旋转,缩放,平移以及错切
  • 还有近几年常用的mixup,mosaic等等。

今天简单讲讲随机旋转,缩放,平移以及错切方法,因为在之前yolov3 spp项目的数据读取部分有涉及到相关知识。本文会结合opencv来进行演示。



仿射变换

仿射变换的原理不在这里赘述,变换前后满足平直性(变换前是直线变换后还是直线)和平行性(变换前平行的线变换后依旧平行),参考博文。在opencv中可以通过仿射变换来实现旋转,缩放,平移以及错切等一系列操作。仿射变换的矩阵乘法形式如下,其中 x , y x,y x,y是变换前的坐标, x ′ , y ′ {x}',{y}' x,y是变换后的坐标。其中 m 11 , m 12 , m 21 , m 22 m_{11},m_{12},m_{21},m_{22} m11,m12,m21,m22为线性变换参数, m 13 , m 23 m_{13},m_{23} m13,m23为平移参数。如果仿射矩阵是对角矩阵,相当于不做任何操作。看到公式不要怕,后面会针对具体示例进行解释
[ x ′ y ′ 1 ] = [ m 11    m 12    m 13 m 21    m 22    m 23 0        0        1 ] [ x y 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} m_{11}\ \ m_{12}\ \ m_{13} \\ m_{21}\ \ m_{22}\ \ m_{23} \\ 0\ \ \ \ \ \ 0\ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=m11  m12  m13m21  m22  m230      0      1xy1


旋转、平移与缩放

首先再次强调下图像处理中的坐标系,水平向右为x轴正方向,竖直向下为y轴正方向

对于图像的旋转,缩放,平移都可以直接通过使用opencv提供的getRotationMatrix2D方法来求得仿射矩阵,需要传入旋转中心center,旋转角度angle(逆时针为正),以及缩放因子scale,假设以图片中心为旋转中心,顺时针旋转30度(opencv里是以逆时针为正,所以angle=-30),并缩放0.5倍:

import cv2


img = cv2.imread("1.png")
h, w = img.shape[0], img.shape[1]
m = cv2.getRotationMatrix2D(center=(w // 2, h // 2), angle=-30, scale=0.5)
print(m)

得到的旋转矩阵参数如下:

[[0.433    -0.25     198.14]
 [0.25      0.433     16.25]]

接着使用opencv中的cv2.warpAffine的方法利用求得的仿射矩阵做仿射变换,其中src为原图像,M为仿射矩阵,dsize为输出图像的大小,borderValue为边界填充颜色(注意是BGR顺序, ( 0 , 0 , 0 ) (0,0,0) (0,0,0)代表黑色):

import cv2


img = cv2.imread("1.png")
h, w = img.shape[0], img.shape[1]
m = cv2.getRotationMatrix2D(center=(w // 2, h // 2), angle=-30, scale=0.5)
r_img = cv2.warpAffine(src=img, M=m, dsize=(w, h), borderValue=(0, 0, 0))
cv2.imshow("origin", img)
cv2.imshow("rotation_scale_trans", r_img)
cv2.waitKey(0)

如图,左边是原图,右边是旋转、缩放、平移后的图片(注意,旋转后如果有超出指定范围dsize的像素都会被截去)。

rotation

接着结合上面的仿射变换公式来讲(不想看理论的可以跳过)。其中 m 11 , m 12 , m 21 , m 22 m_{11},m_{12},m_{21},m_{22} m11,m12,m21,m22为线性变换参数(沿坐标原点旋转就是一个简单的线性变换), m 13 , m 23 m_{13},m_{23} m13,m23为平移参数(分别对应x轴方向平移和y轴方向平移)。
[ m 11    m 12    m 13 m 21    m 22    m 23 0        0        1 ] \begin{bmatrix} m_{11}\ \ m_{12}\ \ m_{13} \\ m_{21}\ \ m_{22}\ \ m_{23} \\ 0\ \ \ \ \ \ 0\ \ \ \ \ \ 1 \end{bmatrix} m11  m12  m13m21  m22  m230      0      1

上面的操作其实可以分解成三步,第一步沿坐标原点旋转,第二步缩放图片,第三步平移图片

在这里插入图片描述

对于第一步的原点旋转对应的仿射矩阵为(通过cv2.getRotationMatrix2D(center=(0, 0), angle=-30, scale=1.0)求得):
[ 0.866    − 0.5    0 0.5        0.866     0 0            0           1 ] \begin{bmatrix} 0.866\ \ -0.5\ \ 0 \\ 0.5\ \ \ \ \ \ 0.866\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ 1 \end{bmatrix} 0.866  0.5  00.5      0.866   00          0         1
当然这里也可以直接使用旋转矩阵模板来计算,但需要注意的是模板里的旋转矩阵默认y轴是竖直向上的,但图像处理中y轴是竖直向下的,且都是以逆时针旋转为正,所以这里的 θ = 30 ° \theta=30\degree θ=30°
[ c o s ( θ )    − s i n ( θ )    0 s i n ( θ )        c o s ( θ )     0 0                0              1 ] \begin{bmatrix} cos(\theta)\ \ -sin(\theta)\ \ 0 \\ sin(\theta)\ \ \ \ \ \ cos(\theta)\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} cos(θ)  sin(θ)  0sin(θ)      cos(θ)   00              0            1

对于第二步图片的缩放,直接使用如下缩放矩阵:
[ S x      0      0 0      S y      0 0      0      1 ] \begin{bmatrix} S_x\ \ \ \ 0 \ \ \ \ 0 \\ 0\ \ \ \ S_y\ \ \ \ 0 \\ 0\ \ \ \ 0\ \ \ \ 1 \end{bmatrix} Sx    0    00    Sy    00    0    1
假设要将图片缩放0.5倍,那么缩放矩阵为:
[ 0.5      0       0 0       0.5      0 0        0        1 ] \begin{bmatrix} 0.5\ \ \ \ 0 \ \ \ \ \ 0 \\ 0\ \ \ \ \ 0.5\ \ \ \ 0 \\ 0\ \ \ \ \ \ 0\ \ \ \ \ \ 1 \end{bmatrix} 0.5    0     00     0.5    00      0      1
将旋转和缩放结合起来就是(注意顺序),由于矩阵乘法满足结合律所以可以将两个仿射矩阵相乘:
[ x ′ y ′ 1 ] = [ 0.5      0       0 0       0.5      0 0        0        1 ] ( [ 0.866    − 0.5    0 0.5        0.866     0 0            0           1 ] [ x y 1 ] )   = [ 0.433    − 0.25    0 0.25        0.433     0 0             0            1 ] [ x y 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} 0.5\ \ \ \ 0 \ \ \ \ \ 0 \\ 0\ \ \ \ \ 0.5\ \ \ \ 0 \\ 0\ \ \ \ \ \ 0\ \ \ \ \ \ 1 \end{bmatrix} \left ( \begin{bmatrix} 0.866\ \ -0.5\ \ 0 \\ 0.5\ \ \ \ \ \ 0.866\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \right ) \\ \ \\ = \begin{bmatrix} 0.433\ \ -0.25\ \ 0 \\ 0.25\ \ \ \ \ \ 0.433\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=0.5    0     00     0.5    00      0      10.866  0.5  00.5      0.866   00          0         1xy1 =0.433  0.25  00.25      0.433   00           0          1xy1

对于第三步图片的平移,即将旋转、缩放后的图像中心移至原图像中心。这里示例中的图片 w = 564 , h = 307 w=564,h=307 w=564,h=307,故原图片中心点坐标是 ( 282 , 153 ) (282, 153) (282,153),旋转后的中心点坐标是 ( 83.86 , 136.75 ) (83.86, 136.75) (83.86,136.75)
[ 83.86 136.75 1 ] = [ 0.433    − 0.25    0 0.25        0.433     0 0             0            1 ] [ 282 153 1 ] \begin{bmatrix} 83.86 \\ 136.75\\ 1 \end{bmatrix} = \begin{bmatrix} 0.433\ \ -0.25\ \ 0 \\ 0.25\ \ \ \ \ \ 0.433\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} 282\\ 153\\ 1 \end{bmatrix} 83.86136.751=0.433  0.25  00.25      0.433   00           0          12821531

所以需要向x轴正方向平移 282 − 83.73 = 198.14 282-83.73=198.14 28283.73=198.14,向y轴正方向平移 153 − 136.75 = 16.25 153-136.75=16.25 153136.75=16.25,刚上面说了 m 13 , m 23 m_{13},m_{23} m13,m23为平移参数(分别对应x轴方向平移和y轴方向平移)所以 m 13 = 198.14 , m 23 = − 16.25 m_{13}=198.14, m_{23}=-16.25 m13=198.14,m23=16.25(在对角矩阵的基础上设置 m 13 , m 23 m_{13},m_{23} m13,m23即可)
[ 1      0       198.14 0      1        16.25 0      0               1 ] \begin{bmatrix} 1\ \ \ \ 0 \ \ \ \ \ 198.14 \\ 0\ \ \ \ 1\ \ \ \ \ \ 16.25 \\ 0\ \ \ \ 0\ \ \ \ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} 1    0     198.140    1      16.250    0             1
和旋转缩放后的仿射矩阵进一步结合起来:
[ x ′ y ′ 1 ] = [ 1      0       198.14 0      1        16.25 0      0               1 ] ( [ 0.433    − 0.25    0 0.25        0.433     0 0             0            1 ] [ x y 1 ] )   = [ 0.433    − 0.25    198.14 0.25        0.433     16.25 0             0            1 ] [ x y 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} 1\ \ \ \ 0 \ \ \ \ \ 198.14 \\ 0\ \ \ \ 1\ \ \ \ \ \ 16.25 \\ 0\ \ \ \ 0\ \ \ \ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \left ( \begin{bmatrix} 0.433\ \ -0.25\ \ 0 \\ 0.25\ \ \ \ \ \ 0.433\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \right ) \\ \ \\ = \begin{bmatrix} 0.433\ \ -0.25\ \ 198.14 \\ 0.25\ \ \ \ \ \ 0.433\ \ \ 16.25 \\ 0\ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=1    0     198.140    1      16.250    0             10.433  0.25  00.25      0.433   00           0          1xy1 =0.433  0.25  198.140.25      0.433   16.250           0          1xy1

这个矩阵刚好和前面使用opencv方法cv2.getRotationMatrix2D(center=(w // 2, h // 2), angle=-30, scale=0.5)得到的矩阵是一样的(前两行)。注意处理的顺序,先旋转,在缩放,最后平移(顺序不一样结果不同)。opencv中得到的矩阵是 2 × 3 2\times3 2×3的,我们自己刚刚算的是 3 × 3 3\times3 3×3的仿射矩阵,最后使用cv2.warpAffine时只用传入前两行就行了。这是使用 3 × 3 3\times3 3×3的矩阵是为了方便将多个仿射矩阵进行相乘操作。


错切

图像的错切变换实际上是平面景物在投影平面上的非垂直投影效果。图像错切变换也称为图像剪切、错位或错移变换,参考博文。一般错切分为横向错切,纵向错切,当然也可以两个方向同时进行错切。下图分别展示了沿x轴方向错切,y轴方向错切,以及同时沿x,y轴两个方向错切的效果。

在这里插入图片描述

在opencv中并没有直接针对错切生成仿射矩阵的方法,所以我们自己可以构建错切对应的仿射矩阵,然后利用cv2.warpAffine进行仿射变换即可。下面是错切对应的仿射矩阵,其中 θ \theta θ表示错切角度, t a n ( θ 1 ) tan(\theta_{1}) tan(θ1)是x轴方向的错切参数, t a n ( θ 2 ) tan(\theta_{2}) tan(θ2)是y轴方向的错切参数。
[ x ′ y ′ 1 ] = [ 1             t a n ( θ 1 )       0 t a n ( θ 2 )            1        0 0                     0        1 ] [ x y 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} 1\ \ \ \ \ \ \ \ \ \ \ tan(\theta_{1}) \ \ \ \ \ 0 \\ tan(\theta_{2})\ \ \ \ \ \ \ \ \ \ 1\ \ \ \ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 0 \ \ \ \ \ \ 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=1           tan(θ1)     0tan(θ2)          1      00                   0      1xy1

假设要沿水平方向错切 30 ° 30\degree 30°,那么仿射矩阵为:
[ 1        0.577       0 0             1         0 0             0         1 ] \begin{bmatrix} 1\ \ \ \ \ \ 0.577 \ \ \ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ 1\ \ \ \ \ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ 0 \ \ \ \ \ \ \ 1 \end{bmatrix} 1      0.577     00           1       00           0       1

import math
import cv2
import numpy as np


img = cv2.imread("1.png")
cv2.imshow("origin", img)
h, w = img.shape[0], img.shape[1]
origin_coord = np.array([[0, 0, 1], [w, 0, 1], [w, h, 1], [0, h, 1]])

theta = 30  # shear角度
tan = math.tan(math.radians(theta))

# x方向错切
m = np.eye(3)
m[0, 1] = tan
shear_coord = (m @ origin_coord.T).T.astype(np.int)
shear_img = cv2.warpAffine(src=img, M=m[:2],
                           dsize=(np.max(shear_coord[:, 0]), np.max(shear_coord[:, 1])),
                           borderValue=(0, 0, 0))
cv2.imshow("shear_x", shear_x)
cv2.waitKey(0)
### 数据增广方法及其应用场景 #### 方法概述 数据增广是一种用于扩展有限训练样本的技术,旨在通过各种手段生成额外的训练实例,以改善机器学习模型的学习效果和泛化能力。具体而言,该过程涉及对现有数据施加一系列转换操作,使得产生的新数据既保留原有信息的核心特性,又能提供足够的多样性来帮助算法更好地理解输入空间的整体结构[^3]。 对于图像领域来说,常见的做法是对原始图片实施几何变换(如翻转、旋转)、颜色调整(亮度调节、对比度改变)或者局部扰动(遮挡部分区域),以此模拟更多样的视觉条件并促使网络学会忽略不必要的细节而聚焦于真正重要的模式特征[^4]。 而在其他类型的媒体资料上,则可以根据具体情况采用相应的策略——例如,在自然语言处理任务里可以通过同义词替换、语序重排等方式创造变体文本;针对时间序列预测问题则能利用插值法填补缺失值或是随机抽样片段作为补充素材[^2]。 #### 应用场景分析 在实际项目中合理运用数据增广能够带来诸多好处: - **解决标注成本高昂的问题**:特别是在医疗影像识别等领域,高质量的手工标记往往耗费巨大资源,此时借助自动化工具批量生产相似却有所区别的案例有助于缓解这一困境; - **克服特定环境下的局限性**:假设某项计算机视觉方案需应对复杂多变的实际工作场所,提前准备好涵盖广泛光照强度、角度位置等因素影响后的合成图库便显得尤为重要,这不仅有利于增强系统的稳定性和可靠性,还能减少后期维护调试的工作量; - **防止过拟合现象的发生**:当可用的历史记录相对较少时,容易造成所建立起来的关系过于贴合当前观测到的现象而非普遍规律,适当增加一些经过巧妙设计的变化形式可以帮助引导参数朝着更通用的方向收敛,进而取得更好的测试成绩[^5]。 ```python import numpy as np from keras.preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator( rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest' ) for batch in datagen.flow(x_train, y_train, batch_size=32): model.fit(batch[0], batch[1]) ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳花的小绿豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值