1、学习背景
为提高模型的泛化能力,进行了图像增强相关的知识学习,通过了解,发现有训练数据集增强和训练网络数据增强两种,其中:
训练数据集数据增强是在训练数据集上进行的,目的是通过对原始数据进行一系列变换和扩充来增加训练样本的多样性,从而提高模型的泛化能力和鲁棒性。
模型训练网络数据增强是在训练网络的过程中应用的,目的是通过对网络结构和训练策略的改进来提高模型的性能和效果,这种数据增强方法主要关注于网络的架构和训练策略的改进,以提高模型的准确性和效率。
两者的不同意义在于,训练数据集数据增强可以增加训练样本的多样性,提高模型的泛化能力; 而模型训练网络数据增强则可以通过改进网络结构和训练策略来提高模型的性能和效果。两者相辅相成,共同作用于模型的训练过程,以达到更好的检测结果。
以前接触过paddle训练相关内容,其中在训练时会为图像添加其图像增强的阐述(其主要函数为paddle.vision.transforms,具体的处理方法可通过print( paddle.vision.transforms.__all__)查看,其底层封装基本是PIL库或者opencv);在使用yolov5时,在训练相关的设置参数(包含一下12个超参数,参数存储在hyp.scratch.yaml文件中),其具体参数可参考其他大佬的分享。
回到正题,此片主要介绍训练数据集的增强,实现模型数据的扩容,提高训练效果。
2、数据集的增强
处理方法
下面是常见的图像增强的函数及详解( imgaug库方法使用),Imgaug库封装了各种数据增强的方法,仅通过定义一些增强序列即可对图像进行增强,并支持随机顺序组合策略进行数据增强。
变换的选取
顺序增强 (iaa.Sequential())
# 定义一个基本变换序列,顺序增强,其中的每种增强策略都可能会用到,每种策略使用的概率是不同的。
# random_order = True表示每次使用随机的顺序应用其中的变换。
seq = iaa.Sequential([
处理1,
处理2,
处理3
])
部分增强 (iaa.someOf())
# 仅将其中的部分子项变换应用于图像,其中第一个参数控制每次选择变换的个数
如n=(0,5),代表每次从中选择0~5个方法增强图像
seq = iaa.SomeOf((0, 5), [
处理1,
处理2,
处理3,
处理4
])
任选一个增强 (iaa.OneOf())
# 从定义的一系列变换中选择一个应用于图像
seq = iaa.OneOf([
A处理,
B处理,
C处理
])
变换的方法
1、颜色调整:
# 灰度图
iaa.Grayscale(alpha=(0.4, 0.6), from_colorspace='RGB')
# hsv对比度
iaa.WithColorspace(to_colorspace="HSV",from_colorspace="RGB",children=iaa.WithChannels(0, iaa.Add((30, 100))))
# 改变亮度并加上颜色滤镜
iaa.Multiply((0.6, 1.4), per_channel=0.2)
# 饱和度
iaa.AddToSaturation((-100, 100))
# 色相
iaa.AddToHue((-20, 20))
# 饱和度以及色相调整
iaa.AddToHueAndSaturation((-25, 25), per_channel=True)
2、滤波添加:
# 高斯滤波
iaa.GaussianBlur((0, 3.0))
# 均值滤波
iaa.AverageBlur(k=(2, 7))
# 中值滤波
iaa.MedianBlur(k=(3, 11))
# 散焦模糊
iaa.imgcorruptlike.DefocusBlur(severity=1)
3、锐化效果:
# 锐化效果
iaa.Sharpen(alpha=(0, 1.0), lightness=(0.75, 1.5))
# 浮雕效果
iaa.Emboss(alpha=(0, 1.0), strength=(0, 2.0))
4、添加噪声:
# 高斯噪声
iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05 * 255))
# 散粒噪声
iaa.imgcorruptlike.ShotNoise(severity=1)
# 脉冲噪声
iaa.imgcorruptlike.ImpulseNoise(severity=1)
# 斑点噪声
iaa.imgcorruptlike.SpeckleNoise(severity=1)
# 为每个像素乘以一个值
iaa.MultiplyElementwise((0.8, 1.2))
# 为每个像素加上一个值
iaa.AddElementwise((-40, 40))
5、 反转效果
# 镜面反转
iaa.Fliplr(1) # 水平镜像翻转
iaa.Flipud(1) # 垂直镜像翻转
# 中心反转
iaa.Fliplr(1) # 水平镜像翻转
iaa.Flipud(1) # 垂直镜像翻转
6、放射变化
仿射变换:又称仿射映射,是指在几何中,对一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。包含:平移(Translation)、旋转(Rotation)、缩放(Zoom)、错切(Shear)。设置cval,mode两个参数指定变换过程中产生新的像素点的生成方法,设置order参数指定使用的插值方法。
iaa.Affine( scale={"x": (0.8, 1.2), "y": (0.8, 1.2)}, # 图像缩放为80%到120%之间
translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)}, # 平移±20%之间
rotate=(-45, 45), # 旋转±45度之间
shear=(-16, 16), # 剪切变换±16度,(矩形变平行四边形)
order=[0, 1], # 使用最邻近差值或者双线性差值
cval=(0, 255), # 全白全黑填充"constant"
mode="constant" # mode=ia.ALL #定义填充图像外区域的方法
)
7、其他
# 下雨
iaa.Sequential([iaa.Rain(drop_size=(0.025, 0.05), speed=(0.25, 0.05)), # 雨
iaa.imgcorruptlike.Spatter(severity=1), # 溅 123水滴、45泥
iaa.MotionBlur(k=3), # 运动模糊
])
# 下雪
iaa.imgcorruptlike.Snow(severity=1)
# 起雾
iaa.Clouds()
# 扭曲
iaa.PiecewiseAffine(scale=(0.01, 0.05))
# 运动模糊
iaa.MotionBlur(k=4)
# 为每个像素上加浮动40,同时加同一个数
iaa.Add((-40, 40), per_channel=0.5)
# 对图像进行弹性形变,毛玻璃
iaa.ElasticTransformation(alpha=(0, 1.0), sigma=0.25)
# 边缘叠加
iaa.EdgeDetect(alpha=(0.2, 0.4))
# 限制对比度自适应直方图均衡(CLAHE算法),本算法与普通的自适应直方图均衡不同地方在于对比度限幅,图像对比度会更自然。
iaa.CLAHE(clip_limit=(1, 20))
# 不作任何变换
iaa.Noop()
应用对象
增强效果应用时,可直接对整幅图像直接进行增强(作用对象:单图),也可对已经打过标签的数据集增强,后续不需要再打标签(作用对象:有保护框txt,的图像文件)。
巧了,imgaug对影像增强的同时,可对keypoint, bounding box进行相应的变换!
1、读取原影像bounding boxes坐标
读取xml文件并使用ElementTree对xml文件进行解析,找到每个object的坐标值。
# 读取出图像中的目标框
def read_xml_annotation(root, image_id):
in_file = open(os.path.join(root, image_id))
tree = ET.parse(in_file)
root = tree.getroot()
bndboxlist = []
for object in root.findall('object'): # 找到root节点下的所有country节点
bndbox = object.find('bndbox') # 子节点下节点rank的值
xmin = int(bndbox.find('xmin').text)
xmax = int(bndbox.find('xmax').text)
ymin = int(bndbox.find('ymin').text)
ymax = int(bndbox.find('ymax').text)
bndboxlist.append([xmin, ymin, xmax, ymax])
return bndboxlist # 以多维数组的形式保存
2、生成变换后的bounding boxe坐标文件
传入目标变换后的bounding boxe坐标,将原坐标替换成新坐标并生成新的xml文件。
def change_xml_list_annotation(root, image_id, new_target, saveroot, id):
in_file = open(os.path.join(root, str(image_id) + '.xml')) # 读取原来的xml文件
tree = ET.parse(in_file) # 读取xml文件
xmlroot = tree.getroot()
index = 0
# 将bbox中原来的坐标值换成新生成的坐标值
for object in xmlroot.findall('object'): # 找到root节点下的所有country节点
bndbox = object.find('bndbox') # 子节点下节点rank的值
# 注意new_target原本保存为高维数组
new_xmin = new_target[index][0]
new_ymin = new_target[index][1]
new_xmax = new_target[index][2]
new_ymax = new_target[index][3]
xmin = bndbox.find('xmin')
xmin.text = str(new_xmin)
ymin = bndbox.find('ymin')
ymin.text = str(new_ymin)
xmax = bndbox.find('xmax')
xmax.text = str(new_xmax)
ymax = bndbox.find('ymax')
ymax.text = str(new_ymax)
index = index + 1
tree.write(os.path.join(saveroot, str(image_id) + "_aug_" + str(id) + '.xml'))
3、bounding box 变化后坐标计算
先读取该影像对应xml文件,获取所有目标的bounding boxes,然后依次计算每个box变化后的坐标。
bbs = ia.BoundingBoxesOnImage([
ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]),
], shape=img.shape)
bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]
boxes_img_aug_list.append(bbs_aug)
以上便是本人目前整理得内容,希望能对您有帮助,手动鞠躬~