YOLOV5-6.x 解说] 数据增强方法介绍 + 代码实现数据增强方法介绍 + 代码实现
最编程
2024-03-26 22:54:09
...
主干目录:
【YOLOV5-6.x 版本讲解】整体项目代码注释导航
现在YOLOV5已经更新到6.X版本,现在网上很多还停留在5.X的源码注释上,因此特开一贴传承开源精神!5.X版本的可以看其他大佬的帖子本文章主要从6.X版本出发,主要解决6.X版本的项目注释与代码分析!......
https://blog.****.net/qq_39237205/article/details/125729662
以下内容为本栏目的一部分,更多关注以上链接目录,查找YOLOV5的更多信息
祝福你朋友早日发表sci!
1 数据增强的作用
- 分割需要在像素级别进行标签标注
- 一些专业领域的图像标注,依赖于专业人士的知识素养
- 在数据集规模很小的情况,如何提高模型的表现力
- 迁移学习:使得具有大量标注数据的源域帮助提升模型的训练效果
- 数据增强 学习到空间的不变形,像素级别的不变形特征都有限,利用平移,缩放,旋转,改变色调值等方法,让模型见过各种类型的数据,提高模型在测试数据上的判别力
2 YOLO数据增强的方法
2.1 rectangular
2.1.1 含义
同个batch里做rectangle宽高等比变换, 加快训练 ,对于多余的黑边做到最小,实现降低计算量。
2.1.2 图解
网络异常,图片无法展示
|
2.1.3 代码
# 文件位置:utils/datasets.py # 6、为Rectangular Training作准备:即减少大小不同图片处理时,对于多余的黑边做到最小,实现降低计算量 # 这里主要是注意shapes的生成 这一步很重要 因为如果采样矩形训练那么整个batch的形状要一样 就要计算这个符合整个batch的shape # 而且还要对数据集按照高宽比进行排序 这样才能保证同一个batch的图片的形状差不多相同 再选择一个共同的shape代价也比较小 if self.rect: # 所有训练图片的shape s = self.shapes # wh # 计算高宽比 ar = s[:, 1] / s[:, 0] # aspect ratio irect = ar.argsort() # 根据高宽比排序 self.img_files = [self.img_files[i] for i in irect] # 获取排序后的img_files self.label_files = [self.label_files[i] for i in irect] # 获取排序后的label_files self.labels = [self.labels[i] for i in irect] # 获取排序后的labels self.shapes = s[irect] # 获取排序后的wh ar = ar[irect] # 获取排序后的wh # 计算每个batch采用的统一尺度 Set training image shapes shapes = [[1, 1]] * nb for i in range(nb): # 同一个batch的图片提取出来 ari = ar[bi == i] mini, maxi = ari.min(), ari.max() # 获取第i个batch中,最小和最大高宽比 if maxi < 1: # [H,W]如果高/宽小于1(w > h),宽大于高,矮胖型,(img_size*maxi,img_size)(保证原图像尺度不变进行缩放) shapes[i] = [maxi, 1] elif mini > 1: # [H,W]如果高/宽大于1(w < h),宽小于高,瘦高型,(img_size,img_size *(1/mini))(保证原图像尺度不变进行缩放) shapes[i] = [1, 1 / mini] # 计算每个batch输入网络的shape值(向上设置为32的整数倍) # 要求每个batch_shapes的高宽都是32的整数倍,所以要先除以32,取整再乘以32(不过img_size如果是32倍数这里就没必要了) self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(np.int) * stride
2.2 HSV变换
2.2.1 含义
- HSV-Hue augmentation (fraction), 色调
- HSV-Saturation augmentation (fraction), 饱和度
- HSV-Value augmentation (fraction), 曝光度
2.2.2 图解效果
2.2.3 代码
# 调用函数的文件位置:文件位置:utils/datasets.py # 色域空间增强Augment colorspace:H色调、S饱和度、V亮度 # 通过一些随机值改变hsv,实现数据增强 augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v']) # 被调用的函数位置:utils/augmentations.py def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5): # HSV color-space augmentation if hgain or sgain or vgain: r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV)) dtype = im.dtype # uint8 x = np.arange(0, 256, dtype=r.dtype) lut_hue = ((x * r[0]) % 180).astype(dtype) lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) lut_val = np.clip(x * r[2], 0, 255).astype(dtype) im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))) cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im) # no return needed
2.3 随机旋转、平移、缩放、裁剪,错切/非垂直投影 、透视变换(从0开始)
2.3.1.1 旋转+缩放
分析:
- src为左边图片,dst为右边旋转缩放变换后的图片, xy为横纵坐标, M为旋转缩放矩阵
- 旋转参数主要是M[0,1], M[1, 0]起作用, 且M[0,1], M[1, 0]互为相反数
- 缩放参数主要是M[0,0], M[1,1]起作用
2.3.1.2 平移
- src为左边图片, dst为右边旋转缩放变换后的图片, xy为横纵坐标, M为平移矩阵
- x轴平移参数主要是M[0,2]起作用
- y轴平移参数主要是M[1,2]起作用
2.3.1.3 错切/非垂直投影
- 错切的类似于固定图片一边, 对另外平行一边施加一个推力形成的变形
- src为左边图片, dst为右边错切变换后的图片, xy为横纵坐标, M为错切矩阵
- 错切参数主要是M[0,1], M[1, 0]起作用
2.3.1.4 透视变换
- src为左边图片, dst为右边透视变换后的图片, xy为横纵坐标, M为变换矩阵
- 变换参数主要是M[2,0], M[2,1]起作用
2.3.2 代码实现
# 调用函数地址:utils/datasets.py # Augment # random_perspective Augment 随机透视变换 [1280, 1280, 3] => [640, 640, 3] # 对mosaic整合后的图片进行随机旋转、平移、缩放、裁剪,透视变换,并resize为输入大小img_size img4, labels4 = random_perspective(img4, labels4, segments4, degrees=self.hyp['degrees'], # 旋转 translate=self.hyp['translate'], # 平移 scale=self.hyp['scale'], # 缩放 shear=self.hyp['shear'], # 错切/非垂直投影 perspective=self.hyp['perspective'], # 透视变换 border=self.mosaic_border) # border to remove # 被调用的函数地址:utils/augmentations.py def random_perspective(im, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0, border=(0, 0)): # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(0.1, 0.1), scale=(0.9, 1.1), shear=(-10, 10)) # targets = [cls, xyxy] height = im.shape[0] + border[0] * 2 # shape(h,w,c) width = im.shape[1] + border[1] * 2 # Center C = np.eye(3) C[0, 2] = -im.shape[1] / 2 # x translation (pixels) C[1, 2] = -im.shape[0] / 2 # y translation (pixels) # Perspective # 透视变换 P = np.eye(3) P[2, 0] = random.uniform(-perspective, perspective) # x perspective (about y) P[2, 1] = random.uniform(-perspective, perspective) # y perspective (about x) # Rotation and Scale 旋转+缩放 R = np.eye(3) a = random.uniform(-degrees, degrees) # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations s = random.uniform(1 - scale, 1 + scale) # s = 2 ** random.uniform(-scale, scale) R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) # Shear 错切/非垂直投影 S = np.eye(3) S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg) S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) # Translation 平移 T = np.eye(3) T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width # x translation (pixels) T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height # y translation (pixels) <
推荐阅读
-
一种结构设计模式,允许在对象中动态添加新行为。它通过创建一个封装器来实现这一目的,即把对象放入一个装饰器类中,然后把这个装饰器类放入另一个装饰器类中,以此类推,形成一个封装器链。这样,我们就可以在不改变原始对象的情况下动态添加新行为或修改原始行为。 在 Java 中,实现装饰器设计模式的步骤如下: 定义一个接口或抽象类作为被装饰对象的基类。 公共接口 Component { void operation; } } 在本例中,我们定义了一个名为 Component 的接口,该接口包含一个名为 operation 的抽象方法,该方法定义了被装饰对象的基本行为。 定义一个实现基类方法的具体装饰对象。 公共类 ConcreteComponent 实现 Component { public class ConcreteComponent implements Component { @Override public void operation { System.out.println("ConcreteComponent is doing something...") ; } } 定义一个抽象装饰器类,该类继承于基类,并将装饰对象作为一个属性。 公共抽象类装饰器实现组件 { protected Component 组件 public Decorator(Component component) { this.component = component; } } @Override public void operation { component.operation; } } } 在这个示例中,我们定义了一个名为 Decorator 的抽象类,它继承了 Component 接口,并将被装饰对象作为一个属性。在操作方法中,我们调用了被装饰对象上的同名方法。 定义一个具体的装饰器类,继承自抽象装饰器类并实现增强逻辑。 公共类 ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component 组件) { super(component); } } public void operation { super.operation System.out.println("ConcreteDecoratorA 正在添加新行为......") ; } } 在本例中,我们定义了一个名为 ConcreteDecoratorA 的具体装饰器类,它继承自装饰器抽象类,并实现了操作方法的增强逻辑。在操作方法中,我们首先调用被装饰对象上的同名方法,然后添加新行为。 使用装饰器增强被装饰对象。 公共类 Main { public static void main(String args) { Component 组件 = new ConcreteComponent; component = new ConcreteDecoratorA(component); 组件操作 } } 在这个示例中,我们首先创建了一个被装饰对象 ConcreteComponent,然后通过 ConcreteDecoratorA 类创建了一个装饰器,并将被装饰对象作为参数传递。最后,调用装饰器的操作方法,实现对被装饰对象的增强。 使用场景 在 Java 中,装饰器模式被广泛使用,尤其是在 I/O 中。Java 中的 I/O 库使用装饰器模式实现了不同数据流之间的转换和增强。 让我们打开文件 a.txt,从中读取数据。InputStream 是一个抽象类,FileInputStream 是专门用于读取文件流的子类。BufferedInputStream 是一个支持缓存的数据读取类,可以提高数据读取的效率,具体代码如下: @Test public void testIO throws Exception { InputStream inputStream = new FileInputStream("C:/bbb/a.txt"); // 实现包装 inputStream = new BufferedInputStream(inputStream); byte bytes = new byte[1024]; int len; while((len = inputStream.read(bytes)) != -1){ System.out.println(new String(bytes, 0, len)); } } } } 其中 BufferedInputStream 对读取数据进行了增强。 这样看来,装饰器设计模式和代理模式似乎有点相似,接下来让我们讨论一下它们之间的区别。 第三,与代理模式的区别: 代理模式的目的是控制对对象的访问,它在对象外部提供一个代理对象来控制对原对象的访问。代理对象和原始对象通常实现相同的接口或继承相同的类,以确保两者可以相互替换。 装饰器模式的目的是动态增强对象的功能,而这是通过对象内部的包装器来实现的。在装饰器模式中,装饰器类和被装饰对象通常实现相同的接口或继承自相同的类,以确保两者可以相互替代。装饰器模式也被称为封装器模式。 在代理模式中,代理类附加了与原类无关的功能。
-
[姿势估计] 实践记录:使用 Dlib 和 mediapipe 进行人脸姿势估计 - 本文重点介绍方法 2):方法 1:基于深度学习的方法:。 基于深度学习的方法:基于深度学习的方法利用深度学习模型,如卷积神经网络(CNN)或递归神经网络(RNN),直接从人脸图像中学习姿势估计。这些方法能够学习更复杂的特征表征,并在大规模数据集上取得优异的性能。方法二:基于二维校准信息估计三维姿态信息(计算机视觉 PnP 问题)。 特征点定位:人脸姿态估计的第一步是通过特征点定位来检测和定位人脸的关键点,如眼睛、鼻子和嘴巴。这些关键点提供了人脸的局部结构信息,可用于后续的姿势估计。 旋转表示:常见的旋转表示方法包括欧拉角和旋转矩阵。欧拉角通过三个旋转角度(通常是俯仰、偏航和滚动)描述头部的旋转姿态。旋转矩阵是一个 3x3 矩阵,表示头部从一个坐标系到另一个坐标系的变换。 三维模型重建:根据特征点的定位结果,三维人脸模型可用于姿势估计。通过将人脸的二维图像映射到三维模型上,可以估算出人脸的旋转和平移信息。这就需要建立人脸的三维模型,然后通过优化方法将模型与特征点对齐,从而获得姿势估计结果。 特征点定位 特征点定位是用于检测人脸关键部位的五官基础部分,还有其他更多的特征点表示方法,大家可以参考我上一篇文章中介绍的特征点检测方案实践:人脸校正二次定位操作来解决人脸校正的问题,客户在检测关键点的代码上略有修改,坐标转换部分客户见上图 def get_face_info(image). img_copy = image.copy image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_detection.process(image) # 在图像上绘制人脸检测注释。 image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) box_info, facial = None, None if results.detections: for detection in results. for detection in results.detections: mp_drawing.Drawing.detection = 无 mp_drawing.draw_detection(image, detection) 面部 = detection.location_data.relative_keypoints 返回面部 在上述代码中,返回的数据是五官(6 个关键点的坐标),这是用 mediapipe 库实现的,下面我们可以尝试用另一个库:dlib 来实现。 使用 dlib 使用 Dlib 库在 Python 中实现人脸关键点检测的步骤如下: 确保已安装 Dlib 库,可使用以下命令: pip install dlib 导入必要的库: 加载 Dlib 的人脸检测器和关键点检测器模型: 读取图像并将其灰度化: 使用人脸检测器检测图像中的人脸: 对检测到的人脸进行遍历,并使用关键点检测器检测人脸关键点: 显示绘制了关键点的图像: 以下代码将参数 landmarks_part 添加到要返回的关键点坐标中。
-
YOLOV5-6.x 解说] 数据增强方法介绍 + 代码实现数据增强方法介绍 + 代码实现