一、前言

大家好,我之前做过图像分类或识别时,经常感慨数据集不够大,导致模型的准确度不够高,虽然用过一些图像增强的方法,也见过别人用过一些数据增强的方法,接下来主要统计一些常见的图像增强的方法。
作为一种深度学习中的常用手段,图像增加对模型的泛化性和准确性都有帮助。数据增加的具体使用方式一般有两种:

  • 一种是实时增加,比如在Caffe中加入数据扰动层,每次图像都先经过扰动操作,再去训练,这样训练经过几代(epoch)之后,就等效于数据增加。
  • 一种是更加直接简单一些的,就是在训练之前就通过图像处理手段对数据样本进行扰动和增加

二、图像增强(data augmentation):

1.随机裁剪
2.随机旋转
3.随机颜色/明暗。
4.仿射变换
5. 图像翻转(镜像:x轴,y轴,xy轴)

2.1 随机裁剪

在裁剪的时候考虑图像宽高比的扰动:宽高比扰动相当于对物体的横向和纵向进行了缩放,这样除了物体的位置扰动,又多出了一项扰动。只要变化范围控制合适,目标物体始终在画面内,这种扰动是有助于提升泛化性能的。

第二图的左上角区域内随机采一点作为裁剪区域的左上角,就实现了如图中位置随机,且宽高比也随机的裁剪。

2.2 随机旋转

旋转:旋转中心,旋转角度

cv2.getRotationMatrix2D()
"""第一个参数是旋转中心,第二个参数是逆时针旋转角度,第三个参数是缩放倍数,对于只是旋转的情况下这个值是1,返回值就是做仿射变换的矩阵。"""cv2.warpAffine()
"""旋转之后在缺失区域会出现黑边
"""
2.3 随机的颜色和明暗

随机的颜色以及明暗的方法相对简单很多,就是给HSV空间的每个通道,分别加上一个微小的扰动。
其中对于色调: 从 -δ\deltaδ 到 δ\deltaδ之间按均匀采样,获取一个随机数 δ^\hat \deltaδ^ 作为要扰动的值,然后新的像素值 x′x'x′ 为原始像素值 x+δ^x +\hat \deltax+δ^;对于其他两个空间则是新像素值 x′x'x′ 为原始像素值 xxx 的 (1+δ^)(1+\hat \delta)(1+δ^) 倍,从而实现色调,饱和度和明暗度的扰动。

因为明暗度并不会对图像的直方图相对分布产生大的影响,所以在HSV扰动基础上,考虑再加入一个Gamma扰动,方法是设定一个大于1的Gamma值的上限 γγγ,因为这个值通常会和1是一个量级,再用均匀采样的近似未必合适,所以从−log⁡γ-\logγ−logγ 到 log⁡γ\logγlogγ 之间均匀采样一个值 ααα,然后用 eαe^{\alpha}eα 作为Gamma值进行变换。

"""image_augmentation.py
"""
import numpy as np
import cv2'''
定义裁剪函数,四个参数分别是:
左上角横坐标x0
左上角纵坐标y0
裁剪宽度w
裁剪高度h
'''
crop_image = lambda img, x0, y0, w, h: img[y0:y0+h, x0:x0+w]'''
随机裁剪
area_ratio为裁剪画面占原画面的比例
hw_vari是扰动占原高宽比的比例范围
'''
def random_crop(img, area_ratio, hw_vari):h, w = img.shape[:2]hw_delta = np.random.uniform(-hw_vari, hw_vari)hw_mult = 1 + hw_delta# 下标进行裁剪,宽高必须是正整数w_crop = int(round(w*np.sqrt(area_ratio*hw_mult)))# 裁剪宽度不可超过原图可裁剪宽度if w_crop > w:w_crop = wh_crop = int(round(h*np.sqrt(area_ratio/hw_mult)))if h_crop > h:h_crop = h# 随机生成左上角的位置x0 = np.random.randint(0, w-w_crop+1)y0 = np.random.randint(0, h-h_crop+1)return crop_image(img, x0, y0, w_crop, h_crop)'''
定义旋转函数:
angle是逆时针旋转的角度
crop是个布尔值,表明是否要裁剪去除黑边
'''
def rotate_image(img, angle, crop):h, w = img.shape[:2]# 旋转角度的周期是360°angle %= 360# 用OpenCV内置函数计算仿射矩阵M_rotate = cv2.getRotationMatrix2D((w/2, h/2), angle, 1)# 得到旋转后的图像img_rotated = cv2.warpAffine(img, M_rotate, (w, h))# 如果需要裁剪去除黑边if crop:angle_crop = angle % 180                  # 对于裁剪角度的等效周期是180°if angle_crop > 90:                            # 并且关于90°对称angle_crop = 180 - angle_croptheta = angle_crop * np.pi / 180.0            # 转化角度为弧度hw_ratio = float(h) / float(w)                # 计算高宽比tan_theta = np.tan(theta)                   # 计算裁剪边长系数的分子项numerator = np.cos(theta) + np.sin(theta) * tan_thetar = hw_ratio if h > w else 1 / hw_ratio        # 计算分母项中和宽高比相关的项denominator = r * tan_theta + 1               # 计算分母项crop_mult = numerator / denominator         # 计算最终的边长系数w_crop = int(round(crop_mult*w))            # 得到裁剪区域h_crop = int(round(crop_mult*h))x0 = int((w-w_crop)/2)y0 = int((h-h_crop)/2)img_rotated = crop_image(img_rotated, x0, y0, w_crop, h_crop)return img_rotated'''
随机旋转
angle_vari是旋转角度的范围[-angle_vari, angle_vari)
p_crop是要进行去黑边裁剪的比例
'''
def random_rotate(img, angle_vari, p_crop):angle = np.random.uniform(-angle_vari, angle_vari)crop = False if np.random.random() > p_crop else Truereturn rotate_image(img, angle, crop)'''
定义hsv变换函数:
hue_delta是色调变化比例
sat_delta是饱和度变化比例
val_delta是明度变化比例
'''
def hsv_transform(img, hue_delta, sat_mult, val_mult):img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.float)img_hsv[:, :, 0] = (img_hsv[:, :, 0] + hue_delta) % 180img_hsv[:, :, 1] *= sat_multimg_hsv[:, :, 2] *= val_multimg_hsv[img_hsv > 255] = 255return cv2.cvtColor(np.round(img_hsv).astype(np.uint8), cv2.COLOR_HSV2BGR)'''
随机hsv变换
hue_vari是色调变化比例的范围
sat_vari是饱和度变化比例的范围
val_vari是明度变化比例的范围
'''
def random_hsv_transform(img, hue_vari, sat_vari, val_vari):hue_delta = np.random.randint(-hue_vari, hue_vari)sat_mult = 1 + np.random.uniform(-sat_vari, sat_vari)val_mult = 1 + np.random.uniform(-val_vari, val_vari)return hsv_transform(img, hue_delta, sat_mult, val_mult)'''
定义gamma变换函数:
gamma就是Gamma
'''
def gamma_transform(img, gamma):gamma_table = [np.power(x / 255.0, gamma) * 255.0 for x in range(256)]gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)return cv2.LUT(img, gamma_table)'''
随机gamma变换
gamma_vari是Gamma变化的范围[1/gamma_vari, gamma_vari)
'''
def random_gamma_transform(img, gamma_vari):log_gamma_vari = np.log(gamma_vari)alpha = np.random.uniform(-log_gamma_vari, log_gamma_vari)gamma = np.exp(alpha)return gamma_transform(img, gamma)

主程序里首先定义三个子模块:

  1. 定义一个函数 parse_arg() 通过Python的argparse模块定义了各种输入参数和默认值。需要注意的是这里用argparse来输入所有参数是因为参数总量并不是特别多,如果增加了更多的扰动方法,更合适的参数输入方式可能是通过一个配置文件。
  2. 定义一个生成待处理图像列表的函数 generate_image_list(),根据输入中要增加图片的数量和并行进程的数目尽可能均匀地为每个进程生成了需要处理的任务列表。执行随机扰动的代码定义在augment_images()中,这个函数是每个进程内进行实际处理的函数,执行顺序是镜像→\rightarrow→ 裁剪→\rightarrow→ 旋转→HSV→Gamma\rightarrow HSV\rightarrow Gamma→HSV→Gamma。需要注意的是镜像→\rightarrow→ 裁剪,因为只是个演示例子,这未必是一个合适的顺序。
  3. 定义一个 main 函数进行调用,代码如下:定义一个main函数进行调用,代码如下:
"""run_augmentation.py
"""
import os
import argparse
import random
import math
from multiprocessing import Process
from multiprocessing import cpu_countimport cv2# 导入image_augmentation.py为一个可调用模块
import image_augmentation as ia# 利用Python的argparse模块读取输入输出和各种扰动参数
def parse_args():parser = argparse.ArgumentParser(description='A Simple Image Data Augmentation Tool',formatter_class=argparse.ArgumentDefaultsHelpFormatter)parser.add_argument('input_dir',help='Directory containing images')parser.add_argument('output_dir',help='Directory for augmented images')parser.add_argument('num',help='Number of images to be augmented',type=int)parser.add_argument('--num_procs',help='Number of processes for paralleled augmentation',type=int, default=cpu_count())parser.add_argument('--p_mirror',help='Ratio to mirror an image',type=float, default=0.5)parser.add_argument('--p_crop',help='Ratio to randomly crop an image',type=float, default=1.0)parser.add_argument('--crop_size',help='The ratio of cropped image size to original image size, in area',type=float, default=0.8)parser.add_argument('--crop_hw_vari',help='Variation of h/w ratio',type=float, default=0.1)parser.add_argument('--p_rotate',help='Ratio to randomly rotate an image',type=float, default=1.0)parser.add_argument('--p_rotate_crop',help='Ratio to crop out the empty part in a rotated image',type=float, default=1.0)parser.add_argument('--rotate_angle_vari',help='Variation range of rotate angle',type=float, default=10.0)parser.add_argument('--p_hsv',help='Ratio to randomly change gamma of an image',type=float, default=1.0)parser.add_argument('--hue_vari',help='Variation of hue',type=int, default=10)parser.add_argument('--sat_vari',help='Variation of saturation',type=float, default=0.1)parser.add_argument('--val_vari',help='Variation of value',type=float, default=0.1)parser.add_argument('--p_gamma',help='Ratio to randomly change gamma of an image',type=float, default=1.0)parser.add_argument('--gamma_vari',help='Variation of gamma',type=float, default=2.0)args = parser.parse_args()args.input_dir = args.input_dir.rstrip('/')args.output_dir = args.output_dir.rstrip('/')return args'''
根据进程数和要增加的目标图片数,
生成每个进程要处理的文件列表和每个文件要增加的数目
'''
def generate_image_list(args):# 获取所有文件名和文件总数filenames = os.listdir(args.input_dir)num_imgs = len(filenames)# 计算平均处理的数目并向下取整num_ave_aug = int(math.floor(args.num/num_imgs))# 剩下的部分不足平均分配到每一个文件,所以做成一个随机幸运列表# 对于幸运的文件就多增加一个,凑够指定的数目rem = args.num - num_ave_aug*num_imgslucky_seq = [True]*rem + [False]*(num_imgs-rem)random.shuffle(lucky_seq)# 根据平均分配和幸运表策略,# 生成每个文件的全路径和对应要增加的数目并放到一个list里img_list = [(os.sep.join([args.input_dir, filename]), num_ave_aug+1 if lucky else num_ave_aug)for filename, lucky in zip(filenames, lucky_seq)]# 文件可能大小不一,处理时间也不一样,# 所以随机打乱,尽可能保证处理时间均匀random.shuffle(img_list)# 生成每个进程的文件列表,# 尽可能均匀地划分每个进程要处理的数目length = float(num_imgs) / float(args.num_procs)indices = [int(round(i * length)) for i in range(args.num_procs + 1)]return [img_list[indices[i]:indices[i + 1]] for i in range(args.num_procs)]# 每个进程内调用图像处理函数进行扰动的函数
def augment_images(filelist, args):# 遍历所有列表内的文件for filepath, n in filelist:img = cv2.imread(filepath)filename = filepath.split(os.sep)[-1]dot_pos = filename.rfind('.')# 获取文件名和后缀名imgname = filename[:dot_pos]ext = filename[dot_pos:]print('Augmenting {} ...'.format(filename))for i in range(n):img_varied = img.copy()# 扰动后文件名的前缀varied_imgname = '{}_{:0>3d}_'.format(imgname, i)# 按照比例随机对图像进行镜像if random.random() < args.p_mirror:# 利用numpy.fliplr(img_varied)也能实现img_varied = cv2.flip(img_varied, 1)varied_imgname += 'm'# 按照比例随机对图像进行裁剪if random.random() < args.p_crop:img_varied = ia.random_crop(img_varied,args.crop_size,args.crop_hw_vari)varied_imgname += 'c'# 按照比例随机对图像进行旋转if random.random() < args.p_rotate:img_varied = ia.random_rotate(img_varied,args.rotate_angle_vari,args.p_rotate_crop)varied_imgname += 'r'# 按照比例随机对图像进行HSV扰动if random.random() < args.p_hsv:img_varied = ia.random_hsv_transform(img_varied,args.hue_vari,args.sat_vari,args.val_vari)varied_imgname += 'h'# 按照比例随机对图像进行Gamma扰动if random.random() < args.p_gamma:img_varied = ia.random_gamma_transform(img_varied,args.gamma_vari)varied_imgname += 'g'# 生成扰动后的文件名并保存在指定的路径output_filepath = os.sep.join([args.output_dir,'{}{}'.format(varied_imgname, ext)])cv2.imwrite(output_filepath, img_varied)# 主函数
def main():# 获取输入输出和变换选项args = parse_args()params_str = str(args)[10:-1]# 如果输出文件夹不存在,则建立文件夹if not os.path.exists(args.output_dir):os.mkdir(args.output_dir)print('Starting image data augmentation for {}\n''with\n{}\n'.format(args.input_dir, params_str))# 生成每个进程要处理的列表sublists = generate_image_list(args)# 创建进程processes = [Process(target=augment_images, args=(x, args, )) for x in sublists]# 并行多进程处理for p in processes:p.start()for p in processes:p.join()print('\nDone!')if __name__ == '__main__':main()

终端运行: python run_augmentation.py -h

python run_augmentation.py imagenet_samples more_samples 1000 --rotate_angle_vari 180 --p_rotate_crop 0.5

其中imagenet_samples为一些从imagenet图片url中随机下载的一些图片,–rotate_angle_vari设为180方便测试全方向的旋转,–p_rotate_crop设置为0.5,让旋转裁剪对一半图片生效。扰动增加后的1000张图片在more_samples文件夹下

机器视觉 OpenCV—python 数据增强相关推荐

  1. Python 数据增强 -- PIL模块

    Python 数据增强 – PIL模块 一.导入模块.读取图片 # 图像操作模块 from PIL import Image# 图像颜色增强模块 from PIL import ImageEnhanc ...

  2. python数据增强三种方法

    程序1: # -*- coding:utf-8 -*- """数据增强1. 色彩抖动 color jittering2. 噪声扰动 noise3. 旋转变换/反射变换 R ...

  3. python 数据增强

    https://www.cnblogs.com/dxscode/p/11733311.html 还有这个: https://blog.csdn.net/xunan003/article/details ...

  4. 机器视觉 OpenCV—python 基于LSTM网络的OCR文本检测与识别

    文章目录 一.背景与环境搭建 二.文本检测与识别 一.背景与环境搭建 OpenCV的文本识别流程: OpenCV EAST 文本检测器执行文本检测, 我们提取出每个文本 ROI 并将其输入 Tesse ...

  5. 机器视觉 OpenCV—python 多目标跟踪与视频分析

    在上一篇中,我们了解目标跟踪(光流),这次我们在视频监控与分析中,视频前后景分析.多目标检测.目标跟踪等算法进行协同工作.该算法实现了匈牙利算法与卡尔曼滤波的多目标跟踪. 贡献者:俄罗斯的Andrey ...

  6. 机器视觉 OpenCV—python目标跟踪(光流)

    文章目录 一.运动检测 1.1 检测思路 1.2 代码 二.运动方向预测 2.1 关键点(角点)追踪 goodFeaturesToTrack() 2.2 光流法 一.运动检测 1.1 检测思路 目标跟 ...

  7. 使用 Python 和 OpenCV 进行数据增广

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 数据扩充是一种增加数据集多样性的技术,无需收集更多真实数据,但仍有 ...

  8. 【Tool】Augmentor和imgaug——python图像数据增强库

    Augmentor和imgaug--python图像数据增强库 Tags: ComputerVision Python 介绍两个图像增强库:Augmentor和imgaug,Augmentor使用比较 ...

  9. python图像增强_【Tool】Augmentor和imgaug——python图像数据增强库

    Augmentor和imgaug--python图像数据增强库 Tags: ComputerVision Python 介绍两个图像增强库:Augmentor和imgaug,Augmentor使用比较 ...

最新文章

  1. Linux对用户态的动态内存管理
  2. echarts geo地图示例_干货|Pyecharts绘制好看的交互式地图教程
  3. 如何写一个Python万能装饰器,既可以装饰有参数的方法,也可以装饰无参数方法,或者有无返回值都可以装饰
  4. 网易云信再被列入Gartner最新发布的两份CPaaS市场报告
  5. OpenCV HDF创建,写入和读取数据集
  6. 这个只有1.5M的软件,能让你的网速快3倍
  7. 20210912模拟
  8. 由一个activity跳转到另一个activity
  9. 系统集成项目管理工程师的含金量
  10. Ubuntu的HBase2.2.2安装
  11. 用mysql生成工资条,巧用各种方法可以实现工资条一键生成
  12. node 简繁体转换_简繁体转换
  13. 【项目管理/PMP/PMBOK第六版/新考纲】计算题! 假设情景分析/类比估算/处理变更/结束采购/高层级风险/组织过程资产
  14. 云原生爱好者周刊:KubeSphere 3.3.0 Alpha 版发布
  15. 2020年上半年我国互联网网络安全监测数据分析报告
  16. VMware 将虚拟磁盘多个文件合并为单个文件
  17. ios动态效果实现翻页_iOS动画--翻页动画
  18. opencv学习(三十九)之反向投影calcBackProject()
  19. Google天气预报API
  20. component-kv设计与实现

热门文章

  1. 易语言防破解防爆暗桩源码
  2. 05UEc++【打飞艇:飞艇的运动与生成】
  3. 以ITK-SNAP为例,解决所有因为windows系统中文名导致的软件问题
  4. 附件上传总结-模板文件服务器路径无法识别,aspose的maven无法下载,无法找到jar包问题,打包异常问题
  5. Android项目:基于Android图书馆借阅系统app毕业设计(计算机毕业设计)
  6. 2021 CV NLP CTR 多模态深度学习 超越SOTA新模型整理
  7. 电子科技大学 计算机图形学 考研,电子科技大学机械工程考研
  8. 使用CSS将图像进行模糊处理
  9. 从单片机到嵌入式开发——(8) μC/OS-II嵌入式实时操作系统概述
  10. Tomcat三个端口的作用