jjzjj

深度学习炼丹-数据增强

嵌入式视觉 2023-03-28 原文

一,数据增强概述

数据增强(也叫数据扩增)的目的是为了扩充数据和提升模型的泛化能力。有效的数据扩充不仅能扩充训练样本数量,还能增加训练样本的多样性,一方面可避免过拟合,另一方面又会带来模型性能的提升。

数据增强几种常用方法有: 图像水平/竖直翻转、随机抠取、尺度变换和旋转。其中尺度变换(scaling)、旋转(rotating)等方法用来增加卷积卷积神经网络对物体尺度和方向上的鲁棒性。

在此基础上,对原图或已变换的图像(或图像块)进行色彩抖动(color jittering)也是一种常用的数据扩充手段,即改变图像颜色的四个方面: 亮度、对比度、饱和度和色调。色彩抖动是在 RGB 颜色空间对原有 RGB 色彩分布进行轻微的扰动,也可在 HSV 颜色空间尝试随机改变图像原有的饱和度和明度(即改变 SV 通道的值)或对色调进行微调(小范围改变 该通道的值)。

HSV 表达彩色图像的方式由三个部分组成: Hue(色调、色相) Saturation(饱和度、色彩纯净度) Value(明度)

在机器学习管道(pipeline)框架中,我们需要在送入模型之前,进行数据增强,一般有两种处理方式:

  • 线下增强(offline augmentation): 适用于较小的数据集(smaller dataset)。
  • 线上增强(online augmentation): 适用于较大的数据集(larger datasets)。

二,opencv 图像增强-几何变换

OpenCV 提供的几何变换函数如下所示:

1,拓展缩放: 拓展缩放,改变图像的尺寸大小

cv2.resize(): 。常用的参数有设定图像尺寸、缩放因子和插值方法。

2,平移: 将对象换一个位置。

cv2.warpAffine(): 函数第一个参数是原图像,第二个参数是移动矩阵,第三个参数是输出图像大小 (width,height)。举例,如果要沿 $(x,y)$ 方向移动,移动的距离是 $(tx ,ty)$,则以下面的方式构建移动矩阵: $$ \begin{bmatrix} 1 & 0 & t_x\ 0 & 1 & t_y \end{bmatrix} $$

3,旋转: 对一个图像旋转角度 $\theta$。

先使用 cv2.getRotationMatrix2D 函数构建旋转矩阵 $M$,再使用 cv2.warpAffine() 函数将对象移动位置。

getRotationMatrix2D 函数第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子

4,放射变换(也叫平面变换/径向变换): 在仿射变换中,原图中所有的平行线在结果图像中依旧平行。

为了找到变换矩阵,我们需要从输入图像中得到三个点,以及它们在输出图像中的对应位置。然后使用 cv2. getAffineTransform 先构建一个 2x3 变换矩阵,最后该矩阵将传递给 cv2.warpAffine 函数。

5,透视变换(也叫空间变换): 转换之后,直线仍是直线。

原理: 透视变换(Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。-来源百度百科。

对于透视变换,需要先构建一个 $3\times 3$ 变换矩阵。要找到此变换矩阵,需要在输入图像上找 4 个点,以及它们在输出图像中的对应位置。在这 4 个点中,其中任意 3 个不共线。然后可以通过函数 cv2.getPerspectiveTransform 找到变换矩阵,将 cv2.warpPerspective 应用于此 $3\times 3$ 变换矩阵。

图像几何变换的实例代码如下:

import cv2 import matplotlib.pyplot as plt from PIL import Image import numpy as np def show_images(imgs, num_rows, num_cols, titles=None, scale=8.5): """Plot a list of images. Defined in :numref:`sec_utils`""" figsize = (num_cols * scale, num_rows * scale) _, axes = plt.subplots(num_rows, num_cols, figsize=figsize) axes = axes.flatten() for i, (ax, img) in enumerate(zip(axes, imgs)): try: img = np.array(img) except: pass ax.imshow(img) ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) if titles: ax.set_title(titles[i]) return axes # Some Geometric Transformation of Images class GeometricTransAug(object): def __init__(self, image_path): img = Image.open(image_path) # load the image self.img_np = np.array(img) # convert PIL image to numpy array self.rows, self.cols, self.ch = self.img_np.shape self.geometry_trans_aug_visual(self.img_np) def resize_aug(self, img_np): # 直接设置了缩放因子, 缩放原大小的2倍 res = cv2.resize(img_np, None, fx=2, fy=2, interpolation = cv2.INTER_CUBIC) return res def warpAffine_aug(self, img_np): # 先构建转换矩阵, 将图像像素点整体进行(100,50)位移: M = np.float32([[1,0,100],[0,1,50]]) res = cv2.warpAffine(img_np, M,(self.cols, self.rows)) return res def rotation_aug(self, img_np): rows, cols, ch = img_np.shape # 先构建转换矩阵,图像相对于中心旋转90度而不进行任何缩放。 M = cv2.getRotationMatrix2D((self.cols/2, self.rows/2), 90, 1) res = cv2.warpAffine(img_np, M, (self.cols, self.rows)) return res def radial_trans_aug(self, img_np): # 仿射变换需要从原图像中找到三个点以及他们在输出图像中的位置 pts1 = np.float32([[50,50],[200,50],[50,200]]) pts2 = np.float32([[10,100],[200,50],[100,250]]) # 通过 getAffineTransform 创建一个 2x3 的转换矩阵 M = cv2.getAffineTransform(pts1,pts2) res = cv2.warpAffine(img_np, M, dsize = (self.cols, self.rows)) return res def perspective_trans_aug(self, img_np): # 透视变换需要一个 3x3 变换矩阵 pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]]) pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]]) M = cv2.getPerspectiveTransform(pts1,pts2) # dsize: size of the output image. res = cv2.warpPerspective(img_np, M, dsize = (300,300)) return res def geometry_trans_aug_visual(self, img_np): res1 = self.resize_aug(img_np) res2 = self.warpAffine_aug(img_np) res3 = self.rotation_aug(img_np) res4 = self.radial_trans_aug(img_np) res5 = self.perspective_trans_aug(img_np) imgs = [res1, res2, res3, res4, res5] aug_titles = ["resize_aug", "warpAffine_aug", "rotation_aug", "radial_trans_aug", "perspective_trans_aug"] # show_images 函数前文已经给出,这里不再复制过来 axes = show_images(imgs, 2, 3, titles=aug_titles, scale=8.5) if __name__ == '__main__': img_path = 'Koalainputimage.jpeg' geometry_trans_aug = GeometricTransAug(img_path) img_np2 = geometry_trans_aug.img_np print(img_np2.shape) 程序运行后输出的几何变换增强效果如下所示:

三,pytorch 图像增强

pytorch 框架中,transforms 类提供了 22 个数据增强方法,对应代码在 transforms.py 文件中,它们既可以对 PIL Image 也能对 torch.*Tensor 数据类型进行增强。

api 的详细介绍可以参考官网文档-Transforming and augmenting images。本章只对 transforms 的 22 个方法进行简要介绍和总结。

总的来说 transforms.py 中的各个预处理方法可以归纳为四大类:

1,裁剪-Crop

  • 中心裁剪: transforms.CenterCrop
  • 随机裁剪: transforms.RandomCrop
  • 随机长宽比裁剪: transforms.RandomResizedCrop
  • 上下左右中心裁剪: transforms.FiveCrop
  • 上下左右中心裁剪后翻转: transforms.TenCrop
2,翻转和变换-Flip and Rotations

  • 依概率 p 水平翻转:transforms.RandomHorizontalFlip(p=0.5)
  • 依概率 p 垂直翻转:transforms.RandomVerticalFlip(p=0.5)
  • 随机旋转:transforms.RandomRotation
3,图像变换

  • resize: transforms.Resize
  • min-max Normalization: 对应 torchvision.transforms.ToTensor() 方法
  • zero-mean Normalization: 对应 torchvision.transforms.Normalize() 方法
  • 填充: transforms.Pad
  • 修改亮度、对比度和饱和度:transforms.ColorJitter
  • 转灰度图: transforms.Grayscale
  • 线性变换: transforms.LinearTransformation()
  • 仿射变换: transforms.RandomAffine
  • 依概率 p 转为灰度图: transforms.RandomGrayscale
  • 将数据转换为 PILImage: transforms.ToPILImage
  • transforms.Lambda: Apply a user-defined lambda as a transform.
4,对 transforms 操作,使数据增强更灵活

  • transforms.RandomChoice(transforms): 从给定的一系列 transforms 中选一个进行操作
  • transforms.RandomApply(transforms, p=0.5): 给一个 transform 加上概率,依概率进行操作
  • transforms.RandomOrder: 将 transforms 中的操作随机打乱
这里 resize 图像增强方法为例,可视化其调整输入图像大小的效果。

# 为了节省空间,这里不再列出导入相应库的代码和show_images函数 img_PIL = Image.open('astronaut.jpeg') print(img_PIL.size) # if you change the seed, make sure that the randomly-applied transforms # properly show that the image can be both transformed and *not* transformed! torch.manual_seed(0) # size 参数: desired output size. resized_imgs = [transforms.Resize(size=size)(orig_img) for size in (30, 50, 100, orig_img.size)] show_images(resized_imgs, 1, 4) 程序运行后的输出图如下。

四,imgaug 图像增强

imgaug 是一个用于机器学习实验中图像增强的库。 它支持广泛的增强技术,允许轻松组合这些技术并以随机顺序或在多个 CPU 内核上执行它们,具有简单而强大的随机接口,不仅可以增强图像,还可以增强关键点/地标、边界框、 热图和分割图。

单个输入图像的示例增强如下所示。

imgaug 的图像增强方法如下所示。

  • Basics
  • Keypoints
  • Bounding Boxes
  • Heatmaps
  • Segmentation Maps and Masks
  • Stochastic Parameters: 随机参数
  • Blending/Overlaying images: 混合/叠加图像
  • Augmenters: 增强器概述
各个方法的使用请参考 imaug 官网

参考资料

  1. 《解析卷积神经网络-第5、6章》
  2. 《OpenCV-Python-Toturial-中文版》

有关深度学习炼丹-数据增强的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  3. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  6. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  7. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  8. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  9. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  10. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

随机推荐