数据增广:旋转,缩放,平移以及错切
在深度学习(图像领域)中,为了提升训练样本数量数据增广是非常常见的手段。比如:
- 随机水平翻转
- 随机色调(H)、饱和度(S)、明度(V)调整
- 随机旋转,缩放,平移以及错切
- 还有近几年常用的mixup,mosaic等等。
今天简单讲讲随机旋转,缩放,平移以及错切方法,因为在之前yolov3 spp
项目的数据读取部分有涉及到相关知识。本文会结合opencv来进行演示。
文章目录
- 仿射变换
- 旋转、平移与缩放
- 错切
仿射变换
仿射变换的原理不在这里赘述,变换前后满足平直性(变换前是直线变换后还是直线)和平行性(变换前平行的线变换后依旧平行),参考博文。在opencv中可以通过仿射变换来实现旋转,缩放,平移以及错切等一系列操作。仿射变换的矩阵乘法形式如下,其中x,yx,yx,y是变换前的坐标,x′,y′{x}',{y}'x′,y′是变换后的坐标。其中m11,m12,m21,m22m_{11},m_{12},m_{21},m_{22}m11,m12,m21,m22为线性变换参数,m13,m23m_{13},m_{23}m13,m23为平移参数。如果仿射矩阵是对角矩阵,相当于不做任何操作。看到公式不要怕,后面会针对具体示例进行解释:
[x′y′1]=[m11m12m13m21m22m23001][xy1]\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} ⎣⎡x′y′1⎦⎤=⎣⎡m11 m12 m13m21 m22 m230 0 1⎦⎤⎣⎡xy1⎦⎤
旋转、平移与缩放
首先再次强调下图像处理中的坐标系,水平向右为x轴正方向,竖直向下为y轴正方向。
对于图像的旋转,缩放,平移都可以直接通过使用opencv提供的getRotationMatrix2D
方法来求得仿射矩阵,需要传入旋转中心center
,旋转角度angle
(逆时针为正),以及缩放因子scale
,假设以图片中心为旋转中心,顺时针旋转30度(opencv里是以逆时针为正,所以angle=-30
),并缩放0.5倍:
import cv2img = 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 cv2img = 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
的像素都会被截去)。
接着结合上面的仿射变换公式来讲(不想看理论的可以跳过)。其中m11,m12,m21,m22m_{11},m_{12},m_{21},m_{22}m11,m12,m21,m22为线性变换参数(沿坐标原点旋转就是一个简单的线性变换),m13,m23m_{13},m_{23}m13,m23为平移参数(分别对应x轴方向平移和y轴方向平移)。
[m11m12m13m21m22m23001]\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.500.50.8660001]\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°:
[cos(θ)−sin(θ)0sin(θ)cos(θ)0001]\begin{bmatrix} cos(\theta)\ \ -sin(\theta)\ \ 0 \\ sin(\theta)\ \ \ \ \ \ cos(\theta)\ \ \ 0 \\ 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ 0\ \ \ \ \ \ \ \ \ \ \ \ 1 \end{bmatrix} ⎣⎡cos(θ) −sin(θ) 0sin(θ) cos(θ) 00 0 1⎦⎤
对于第二步图片的缩放,直接使用如下缩放矩阵:
[Sx000Sy0001]\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.50000.50001]\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.50000.50001]([0.866−0.500.50.8660001][xy1])=[0.433−0.2500.250.4330001][xy1]\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} ⎣⎡x′y′1⎦⎤=⎣⎡0.5 0 00 0.5 00 0 1⎦⎤⎝⎛⎣⎡0.866 −0.5 00.5 0.866 00 0 1⎦⎤⎣⎡xy1⎦⎤⎠⎞ =⎣⎡0.433 −0.25 00.25 0.433 00 0 1⎦⎤⎣⎡xy1⎦⎤
对于第三步图片的平移,即将旋转、缩放后的图像中心移至原图像中心。这里示例中的图片w=564,h=307w=564,h=307w=564,h=307,故原图片中心点坐标是(282,153)(282, 153)(282,153),旋转后的中心点坐标是(83.86,136.75)(83.86, 136.75)(83.86,136.75),
[83.86136.751]=[0.433−0.2500.250.4330001][2821531]\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 1⎦⎤⎣⎡2821531⎦⎤
所以需要向x轴正方向平移282−83.73=198.14282-83.73=198.14282−83.73=198.14,向y轴正方向平移153−136.75=16.25153-136.75=16.25153−136.75=16.25,刚上面说了m13,m23m_{13},m_{23}m13,m23为平移参数(分别对应x轴方向平移和y轴方向平移)所以m13=198.14,m23=−16.25m_{13}=198.14, m_{23}=-16.25m13=198.14,m23=−16.25(在对角矩阵的基础上设置m13,m23m_{13},m_{23}m13,m23即可)
[10198.140116.25001]\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]=[10198.140116.25001]([0.433−0.2500.250.4330001][xy1])=[0.433−0.25198.140.250.43316.25001][xy1]\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} ⎣⎡x′y′1⎦⎤=⎣⎡1 0 198.140 1 16.250 0 1⎦⎤⎝⎛⎣⎡0.433 −0.25 00.25 0.433 00 0 1⎦⎤⎣⎡xy1⎦⎤⎠⎞ =⎣⎡0.433 −0.25 198.140.25 0.433 16.250 0 1⎦⎤⎣⎡xy1⎦⎤
这个矩阵刚好和前面使用opencv方法cv2.getRotationMatrix2D(center=(w // 2, h // 2), angle=-30, scale=0.5)
得到的矩阵是一样的(前两行)。注意处理的顺序,先旋转,在缩放,最后平移(顺序不一样结果不同)。opencv中得到的矩阵是2×32\times32×3的,我们自己刚刚算的是3×33\times33×3的仿射矩阵,最后使用cv2.warpAffine
时只用传入前两行就行了。这是使用3×33\times33×3的矩阵是为了方便将多个仿射矩阵进行相乘操作。
错切
图像的错切变换实际上是平面景物在投影平面上的非垂直投影效果。图像错切变换也称为图像剪切、错位或错移变换,参考博文。一般错切分为横向错切,纵向错切,当然也可以两个方向同时进行错切。下图分别展示了沿x轴方向错切,y轴方向错切,以及同时沿x,y轴两个方向错切的效果。
在opencv中并没有直接针对错切生成仿射矩阵的方法,所以我们自己可以构建错切对应的仿射矩阵,然后利用cv2.warpAffine
进行仿射变换即可。下面是错切对应的仿射矩阵,其中θ\thetaθ表示错切角度,tan(θ1)tan(\theta_{1})tan(θ1)是x轴方向的错切参数,tan(θ2)tan(\theta_{2})tan(θ2)是y轴方向的错切参数。
[x′y′1]=[1tan(θ1)0tan(θ2)10001][xy1]\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} ⎣⎡x′y′1⎦⎤=⎣⎡1 tan(θ1) 0tan(θ2) 1 00 0 1⎦⎤⎣⎡xy1⎦⎤
假设要沿水平方向错切30°30\degree30°,那么仿射矩阵为:
[10.5770010001]\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 npimg = 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)
数据增广:旋转,缩放,平移以及错切相关推荐
- 编程实现多边形的平移、比例(缩放)、旋转、对称和错切等二维仿射变换(大有门道)
这是计算机图形学基础的一个课后题,其实完全可以直接用OpenGL提供的几何变换的函数轻松的实现,但是毕竟学就要学明白,仔细写这个题一是为了回顾一下各种变换对应的变换矩阵和数学规律,二是加深一下对交互的 ...
- 目标检测训练数据旋转python代码——数据增广(一)
转载请在首行附上原文链接!有帮助的话记得点ge赞. 针对目标检测任务,对训练数据做旋转进而达到数据增广的python2代码,网上没找到,自己写了一份. 另外附上一份,检查旋转后效果的Python代码( ...
- 深度学习 之 数据增广(包含源码及注释文件更改)
数据增广:平移,水平/垂直翻转,旋转,缩放,剪切,对比度,色彩抖动,噪声 #coding=utf-8 ################################################ ...
- 炼丹手册——数据增广
数据增广这个知识点放在炼丹手册里说,总感觉不太搭,不过看到bag of freebies这个词,心想也算是提高训练精度的一种手段,那就和其他knowledge躺在一起吧. 数据增广的目的是增加训练样本 ...
- 常用数据增广方法,解决数据单一问题
Datawhale干货 作者:陈信达,Datawhale优秀学习者 寄语:本文将对传统图像算法的数据增广方式进行学习,以最常用的平移和旋转为例,帮助大家梳理几何变换的概念和应用,并对其在OpenCV的 ...
- 深度学习训练中关于数据处理方式--原始样本采集以及数据增广
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/SMF0504/article/details/78695908 好久没有写博客,一直想重新调整自己的 ...
- 【工大SCIR笔记】自然语言处理领域的数据增广方法
点击上方,选择星标或置顶,每天给你送干货! 作者:李博涵 来自:哈工大SCIR 1.摘要 本文介绍自然语言处理领域的数据增广方法.数据增广(Data Augmentation,也有人将Data Aug ...
- 嵌入式AI —— 6. 为糖葫芦加糖,浅谈深度学习中的数据增广
没有读过本系列前几期文章的朋友,需要先回顾下已发表的文章: 开篇大吉 集成AI模块到系统中 模型的部署 CMSIS-NN介绍 从穿糖葫芦到织深度神经网络 又和大家见面了,上次本程序猿介绍了CMSIS- ...
- 自然语言处理领域的数据增广方法
1.摘要 本文介绍自然语言处理领域的数据增广方法.数据增广(Data Augmentation,也有人将Data Augmentation翻译为"数据增强",然而"数据增 ...
最新文章
- elinput内容过长显示悬浮框_element ui el-table 表头自定义,内容超出省略,悬浮时显示...
- golang defer简介 goland 警告提示 possible resource leak,difer is called in a for loop 原因
- java启动子线程过多导致卡死_java线程基础巩固---多Product多Consumer之间的通讯导致出现程序假死的原因分析...
- java.library.path属性在代码中设置不生效问题
- 恢复qsecofr密码
- SpringMVC的请求-获得请求参数-获得集合类型参数2
- 「ROI 2017 Day 2」反物质(单调队列优化dp)
- 阿里科学家再获世界级荣誉,平头哥首席科学家谢源当选AAASFellow
- SQL锁机制和事务隔离级别
- 用“五心”寻找政务云的“答案”
- Kafka配置1--Windows环境安装和配置Kafka
- 管理感悟:需要什么样的技术文档
- 在线API 工具之SosoApi
- 2021-0(C++)输入一个字符串,判断其是否是回文字符串(回文字符串就是正序与反序是相同的字符串)5-27
- 工程电磁场复习基本知识点
- 神经网络可以用来预测吗,神经网络预测的优点
- 计算机组装与维修的前言,计算机组装与维修论文大纲模板 计算机组装与维修论文提纲如何写...
- java集合优秀率怎么算,优秀率怎么算(及格率和优秀率公式)
- 嵌入式Linux misc 设备驱动
- Mac的VIM中delete键失效的原因和解决方案
热门文章
- 利用R语言irr包计算ICC值(组内相关系数)
- Swift 使用NSRange 查找字符多次出现的位置处理
- IDS--入侵检测系统的学习
- Scrapy vs BeautifulSoup
- HTTP/1.1、HTTP/2
- VMware创建虚拟机时出现 network bot from intel e1000
- linux内核代码研读与实战,Linux内核源码研读与实战演练
- 一文读懂SIMD指令集 目前最全SSE/AVX介绍
- 【WIN10】如何关闭右下角输入法的“拼”字
- jsf<h:outpytText>实现换行