导读
在深度学习的数据增强中,我们经常需要对图像进行各种增强操作如平移、缩放、旋转、翻转等,这些其实都是图像的仿射变换。通过本篇文章,你能够知道它们的实现原理以及如何应用它们。本文讲述如何通过仿射变换来实现数据增强。

仿射变换
简单来说,“仿射变换”就是:“线性变换”+“平移”。

先看什么是线性变换?

  • 变换前是直线的,变换后依然是直线
  • 直线比例保持不变
  • 变换前是原点的,变换后依然是原点

仿射变换从几何直观只有两个要点:

  • 变换前是直线的,变换后依然是直线
  • 直线比例保持不变

少了原点保持不变这一条,比如平移

线性变换和仿射变换都是是通过矩阵乘法来实现的。

假设有一个向量空间k:
k = ( x , y ) k=(x,y) k=(x,y)

还有一个向量空间j:
j = ( x ′ , y ′ ) j=(x',y' ) j=(x′,y′)

如果我们想要将向量空间由k变为j,可以通过下面的公式进行变换
j = k ∗ w + b j=k∗w+b j=k∗w+b

将上式进行拆分可得
x ′ = w 00 ∗ x + w 01 ∗ y + b 0 y ′ = w 10 ∗ x + w 11 ∗ y + b 1 x'=w_{00}*x+w_{01}*y+b_0\\ y'=w_{10}*x+w_{11}*y+b_1 x′=w00​∗x+w01​∗y+b0​y′=w10​∗x+w11​∗y+b1​

我们再将上式转换为矩阵的乘法
[ x ′ y ′ ] = [ w 00 w 01 b 0 w 10 w 11 b 1 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} w_{00}&w_{01}&b_0\\w_{10}&w_{11}&b_1 \end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[w00​w10​​w01​w11​​b0​b1​​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

通过参数矩阵M 就可以实现两个向量空间之间的转换,在进行仿射变换的时候我们也只需要一个矩阵M就可以实现平移、缩放、旋转和翻转变换。

接下来,会先介绍原理然后利用OpenCV来实现相应的例子,这里主要利用OpenCV的warpAffine函数来实现仿射变换

warpAffine函数参数:

src:输入的图像数组
M:仿射变换矩阵
dsize:变换后图像的大小
flags:使用的插值算法
borderValue:边界的填充值
图像平移
在平面坐标系有点P ( x , y )和点P ′ ( x ′ , y ′ )
如果我们想要将P点移动到P ′
通过下面的变换就可以实现
x ′ = x + Δ x y ′ = y + Δ y x'=x+\Delta x \\ y' = y + \Delta y x′=x+Δxy′=y+Δy

其中 Δ x \Delta x Δx 和 Δ y \Delta y Δy就是x方向上和y方向上的偏移量,我们将其转换为矩阵的形式
[ x ′ y ′ ] = [ 1 0 Δ x 0 1 Δ y ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} 1&0&\Delta x\\0&1&\Delta y\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[10​01​ΔxΔy​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

上面的矩阵M就是仿射变换的平移参数,接下来我们利用OpenCV中的warpAffine函数来实现

# --*--coding: utf-8 --*...
import cv2
import numpy as np
import matplotlib.pyplot as pltdef show_compare_img(original_img,transform_img):_,axes = plt.subplots(1,2)#显示图像axes[0].imshow(original_img)axes[1].imshow(transform_img)#设置子标题axes[0].set_title("original image")axes[1].set_title("warpAffine transform image")plt.show()def translation_img():# 定义一个图像平移矩阵# x向左平移(负数向左,正数向右)200个像素# y向下平移(负数向上,正数向下)500个像素M = np.array([[1, 0, -200], [0, 1, 500]], dtype=np.float)# 读取需要平移的图像img = cv2.imread("test.jpg")# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义平移后图像的大小,保持和原图大小一致dsize = img.shape[:2][::-1]# 便于大家观察这里采用绿色来填充边界translation_img = cv2.warpAffine(img, M, dsize, borderValue=(0, 255, 0))# 显示图像show_compare_img(img, translation_img)translation_img()

图像翻转
有时候我们我们需要对图像进行水平翻转、垂直翻转、镜像翻转(同时进行水平和垂直翻转),想要实现这个功能并不难,我们可以通过opencv内置的flip方法很容易实现,还可以通过numpy的索引来实现,这里我们主要介绍通过仿射变换矩阵来实现这个功能

上图中的A 、 B 、 C 、 D 表示图像的四个顶点,如果我们需要对图像进行水平翻转,那么我们就需要将A点和B 点进行交换,C 点和D点进行交换,沿着x轴的中线进行对称交换位置,通过下面的式子可以实现水平翻转
x ′ = − x + w x′=−x+w x′=−x+w
上式中的w ww表示图像的宽,同理可得垂直翻转的实现公式
y ′ = − y + h y ′=−y+h y′=−y+h
上式中的h表示的是图像的高

变换矩阵翻转图像
图像翻转的变换矩阵
水 平 翻 转 的 变 换 矩 阵 : M = [ − 1 0 w 0 1 0 ] 水平翻转的变换矩阵: M=\left[ \begin{matrix} -1&0&w\\0&1&0 \end{matrix} \right] 水平翻转的变换矩阵:M=[−10​01​w0​]
垂 直 翻 转 的 变 换 矩 阵 : M = [ 1 0 0 0 − 1 h ] 垂直翻转的变换矩阵: M=\left[ \begin{matrix} 1&0&0\\0&-1&h \end{matrix} \right] 垂直翻转的变换矩阵:M=[10​0−1​0h​]
镜 像 的 变 换 矩 阵 : M = [ − 1 0 w 0 − 1 h ] 镜像的变换矩阵: M=\left[ \begin{matrix} -1&0&w\\0&-1&h \end{matrix} \right] 镜像的变换矩阵:M=[−10​0−1​wh​]

def flip_img(horizontal_flip,vertical_flip,img):#获取输入图片的宽和高height,width = img.shape[:2]#初始化变换矩阵M = np.array([[0, 0, 0], [0, 0, 0]], dtype=np.float)#水平翻转if horizontal_flip:M[0] = [-1,0,width]#垂直翻转if vertical_flip:M[1] = [0,-1,height]# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义缩放后图片的大小img_flip = cv2.warpAffine(img, M, (width,height))# 显示图像show_compare_img(img, img_flip)img = cv2.imread("test.jpg")
flip_img(True,True,img)

  • OpenCV的flip函数翻转图像

flip函数参数:

  • src:输入的图像数组
  • flipCode:图像翻转参数,1表示水平翻转,0表示垂直翻转,-1表示镜像翻转
img = cv2.imread("test.jpg")
#水平翻转
horizontal_flip_img = cv2.flip(img,1)
#垂直翻转
vertical_flip_img = cv2.flip(img,0)
#镜像翻转
mirror_flip_img = cv2.flip(img,-1)
  • numpy的索引翻转图像
img = cv2.imread("test.jpg")
#水平翻转
horizontal_flip_img = img[:,::-1]
#垂直翻转
vertical_flip_img = img[::-1]
#镜像翻转
mirror_flip_img = img[::-1,::-1]

图像缩放
如果我们想要对坐标系的P PP点进行缩放操作,通过下面的公式就可以实现
x ′ = f x ∗ x y ′ = f y ∗ y x′=f_x*x\\ y'=f_y*y x′=fx​∗xy′=fy​∗y
通过,在x xx和y yy前面添加一个缩放系数即可,同样我们将其转换为矩阵形式

[ x ′ y ′ ] = [ f x 0 0 0 f y 0 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} f_x&0&0\\0&f_y&0\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[fx​0​0fy​​00​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​
通过上面的矩阵M 我们就可以实现对图片的缩放

def scale_img():#定义宽缩放的倍数fx = 0.5#定义高缩放的倍数fy = 2#定义一个图像缩放矩阵M = np.array([[fx,0,0],[0,fy,0]],dtype=np.float)#读取图像img = cv2.imread("test.jpg")#获取图片的宽和高height,width = img.shape[:2]#将图片由BGR转为RGBimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#定义缩放后图片的大小scale_img = cv2.warpAffine(img,M,(int(width*fx),int(height*fy)))#显示图像show_compare_img(img,scale_img)scale_img()


这里使用仿射变换实现的图像缩放其实和resize函数的效果是一样的

图像旋转

  • 围绕原点旋转
    我们先来看看一个二维平面上的点在围绕原点是如何旋转的

在这里插入图片描述 上图中点v在围绕原点旋转 θ \theta θ度之后得到了点v ′ ,我们将坐标点用极坐标的形式来表示可以得到 v ( r c o s ϕ , r s i n ϕ ) v(rcos\phi,rsin\phi) v(rcosϕ,rsinϕ),所以 v ′ ( r c o s ( θ + ϕ ) , r s i n ( θ + ϕ ) ) v'(rcos(\theta+\phi),rsin(\theta+\phi)) v′(rcos(θ+ϕ),rsin(θ+ϕ))利用正弦和余弦将其展开可得

对 于 v 点 来 说 : x = r c o s ϕ y = r s i n ϕ 对 于 v ′ 来 说 : x ′ = r c o s ( θ + ϕ ) = r c o s θ ∗ c o s ϕ − r s i n θ ∗ s i n ϕ y ′ = r s i n ( θ + ϕ ) = r s i n θ ∗ c o s ϕ + r c o s θ ∗ s i n ϕ 然 后 将 x 和 用 代 入 上 式 得 : x ′ = x ∗ c o s ϕ − y ∗ s i n ϕ y ′ = y ∗ c o s ϕ + x ∗ s i n ϕ \begin{aligned} 对于v点来说&:\\ &x = rcos\phi \\ &y=rsin\phi \\ 对于v′ 来说&:\\ &x'=rcos(\theta+\phi)=rcos\theta*cos\phi-rsin\theta*sin\phi\\ &y'=rsin(\theta+\phi)=rsin\theta*cos\phi+rcos\theta*sin\phi\\ 然后将x和用代入上式得&:\\ &x'=x*cos\phi-y*sin\phi\\ &y'=y*cos\phi+x*sin\phi\\ \end{aligned} 对于v点来说对于v′来说然后将x和用代入上式得​:x=rcosϕy=rsinϕ:x′=rcos(θ+ϕ)=rcosθ∗cosϕ−rsinθ∗sinϕy′=rsin(θ+ϕ)=rsinθ∗cosϕ+rcosθ∗sinϕ:x′=x∗cosϕ−y∗sinϕy′=y∗cosϕ+x∗sinϕ​
然后再将上式用矩阵M表示,可得
[ x ′ y ′ ] = [ c o s ϕ − s i n ϕ 0 s i n ϕ c o s ϕ 0 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} cos\phi&-sin\phi&0\\sin\phi&cos\phi&0\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[cosϕsinϕ​−sinϕcosϕ​00​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​
特别注意:我们在建立直角坐标系的时候是以左下角为原点建立的,然而对于图像而言是以左上角为原点建立的,所以我们需要对角度 θ \theta θ进行取反,结合三角函数的特性,M 矩阵的表达式如下
M = [ c o s ϕ − s i n ϕ 0 s i n ϕ c o s ϕ 0 ] M= \left[ \begin{matrix} cos\phi&-sin\phi&0\\sin\phi&cos\phi&0\end{matrix} \right] M=[cosϕsinϕ​−sinϕcosϕ​00​]
还需要注意的是这里的角度都是弧度制,所以我们还需要对其进行转换,转换代码如下

#将角度转换为弧度制
radian_theta = theta/180 * np.pi

将图片围绕原点进行逆时针旋转θ \thetaθ度的代码如下

def rotate_img_original(theta):#将角度转换为弧度制radian_theta = theta/180 * np.pi#定义围绕原点旋转的变换矩阵M = np.array([[np.cos(radian_theta),np.sin(radian_theta),0],[-np.sin(radian_theta),np.cos(radian_theta),0]])# 读取图像img = cv2.imread("test.jpg")#定义旋转后图片的宽和高height,width = img.shape[:2]# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#围绕原点逆时针旋转\theta度rotate_img = cv2.warpAffine(img,M,(width,height))#显示图像show_compare_img(img,rotate_img)
rotate_img_original(45)


细心的同学也许已经发现了,上图中围绕图像中心旋转后的图片部分被裁剪掉了,如果我们想让旋转之后的图片仍然是完整,应该如何修改呢?

def rotate_img_point(point_x,point_y,theta,img,is_completed=False):#将角度转换为弧度制radian_theta = theta / 180 * np.pi#定义围绕任意点旋转的变换矩阵M = np.array([[np.cos(radian_theta), np.sin(radian_theta),(1-np.cos(radian_theta))*point_x-point_y*np.sin(radian_theta)],[-np.sin(radian_theta), np.cos(radian_theta),(1-np.cos(radian_theta))*point_y+point_x*np.sin(radian_theta)]])# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义旋转后图片的宽和高height, width = img.shape[:2]#判断旋转之后的图片是否需要保持完整if is_completed:#增大旋转之后图片的宽和高,防止被裁剪掉new_height = height * np.cos(radian_theta) + width * np.sin(radian_theta)new_width = height * np.sin(radian_theta) + width * np.cos(radian_theta)#增大变换矩阵的平移参数M[0, 2] += (new_width - width) * 0.5M[1, 2] += (new_height - height) * 0.5height = int(np.round(new_height))width = int(np.round(new_width))# 围绕原点逆时针旋转\theta度rotate_img = cv2.warpAffine(img, M, (width, height))# 显示图像show_compare_img(img, rotate_img)img = cv2.imread("test.jpg")
height,width = img.shape[:2]
#定义围绕图片的中心旋转
point_x,point_y = int(width/2),int(height/2)
rotate_img_point(point_x,point_y,45,img,True)

参考资料

https://www.zhihu.com/question/20666664
https://xiulian.blog.csdn.net/article/details/103845581

深度学习数据增强方法,利用仿射变换实现图像进行各种操作如平移、缩放、旋转、翻转相关推荐

  1. 二、深度学习数据增强方法汇总

    深度学习模型训练数据增强方法汇总 一.随机裁剪 二.RGB-->BGR通道互换 三.仿射变换(缩放) 三.随机旋转 四.对比度调整 五.随机抠图 六.bound box 中心点随机抠图 七.随机 ...

  2. 深度学习-数据增强与扩充

    数据增强可以说是数据驱动下的深度学习必经之路,掌握数据,相当于掌握当下主流方向的自动驾驶的命脉,是人工智能不可或缺的资源.本文将介绍最新的利用大模型扩充数据的方式! 先看下变色效果: 左褐色背景图 为 ...

  3. 比较全的深度学习数据预处理方法

    当前深度学习的预处理方法 1.中心化/零均值化 程序代码 2.标准化/归一化 程序代码 (1)标准化与归一化的联系和差异 联系 差异 (2)为什么要归一化/标准化 ①某些模型求解需要 ②一些分类器需要 ...

  4. 深度学习数据增强——扩充数据集

    在深度学习过程中经常会因为数据量少而发生过拟合现象,或者模型的泛化能力比较低.基于此,本文讲一下图像的数据增强,就是通过对图像简答你的形变,用来应对因拍照的角度不同而使得图谱变形.tensorflow ...

  5. 【matplotlib + opencv】关于opencv和matplotlib绘制图像时,出现色差色偏的问题探讨,思考,解决。(深度学习数据包plt.imshow绘制的图像底色偏绿蓝偏黄)

    文章目录 一.图像红变蓝,蓝变红的问题 (1)原因分析 (2)代码及结果展示 1)错误代码 2)错误结果 3)正确代码 二.深度学习数据包plt.imshow绘制图像偏蓝黄色 (1)原因分析 1)原理 ...

  6. 深度学习数据增强数据扩增方法

    随机裁剪 对图片随机0.6~1.0比率大小的区域进行裁剪. 然后resize到固定大小. torch.API torchvision.transforms.RandomCrop(size,paddin ...

  7. 深度学习--数据增强

    在深度学习中,为了避免出现过拟合(Overfitting),通常我们需要输入充足的数据量.本页面主要记录下常用的数据增强(Data Augmentation)变换方法. 不同的任务背景下, 我们可以通 ...

  8. 【深度学习数据增强处理】imgaug Augment Polygons 对标注图片和polygons的数据增强

    对于本地化进行图像的增强,大家都是非常好操作的.但是,对于标注信息一起增强,还是稍微有一些难度的,麻烦很多. 我是遇到一个数据集非常少的任务,只有40张图.就直接标记了去训练,发现几乎不拟合,当然这里 ...

  9. 深度学习数据预处理方法及示例

    文章目录 一.中心化/零均值化 二.归一化 三.PCA和白化 数据预处理在构建网络模型时是非常重要的,往往能够决定训练结果.当然对于不同的数据集,预处理的方法都会有或多或少的特殊性和局限性.在这里介绍 ...

最新文章

  1. 第 2 章:初出茅庐【初级篇 - 2.2 贪心算法】
  2. 如何修改 Linkis 依赖的 Spark、Hive 版本?
  3. 分析IBASE save 白屏问题
  4. vue实现多行数据提交_(Vue起步)3.Vue设计模式:MVVM模式
  5. java kotlin lateinit_kotlin - 如何检查“lateinit”变量是否已初始化?
  6. Python编程:从入门到实践 - pygal篇 - Die
  7. PS2021要求计算机配置,Photoshop2021体验:傻瓜操作体验下系统需求大大增加!
  8. 基于Jenkins 快速搭建持续集成环境
  9. 关于NTRIP、RTCM、NMEA的学习
  10. USB2.0 EMC标准设计
  11. 千图成像python_吞了1000瓶老干妈的南山头铁鹅,Python制作千图成像(附上源代码和应用程序)...
  12. android是什么意思
  13. block locality
  14. Enolsoft PDF Converter with OCR Mac(PDF格式转换及OCR识别软件)
  15. STM32CbueMX之USB挂载内存虚拟U盘
  16. 打印100以内的质数
  17. 更改Linux系统的主机名(hostname)两种实用的方法
  18. Eversipn STT-MRAM的MJT细胞
  19. 全球行政区划数据库 地理数据库
  20. 基于Matlab/Simulink的简单三相交流系统扫频仿真

热门文章

  1. 【OCR文字识别】百度文库、个人图书馆中的文字,想复制多少都可以
  2. 2021年危险化学品生产单位安全生产管理人员考试内容及危险化学品生产单位安全生产管理人员考试技巧
  3. istio证书签发流程
  4. html5转PDF软件,HTML to PDF Tools(HTML转PDF工具)
  5. php echo语句,PHP echo和print语句
  6. cesium中添加建筑白模
  7. Deep Reinforcement Learning amidst Lifelong Non-Stationarity
  8. 某大学计算机系主任王教授对新来系里工作的,某大学计算机系的系主任王教授对新来系里工作的刘博士说:“下周一上午我们谈谈,我想请你介绍一下_搜题易...
  9. 孝义职中计算机类升学,孝义职中问题
  10. 虚函数必须定义(纯虚函数除外)