data_augment.py
yolov6\data\data_augment.py
目录
2.def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
4.def mixup(im, labels, im2, labels2):
6.def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate):
7.def mosaic_augmentation(img_size, imgs, hs, ws, labels, hyp):
1.所需的库和模块
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# This code is based on
# https://github.com/ultralytics/yolov5/blob/master/utils/dataloaders.py
import math
import random
import cv2
import numpy as np
2.def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
# HSV 颜色空间增强。
'''HSV color-space augmentation.'''
if hgain or sgain or vgain:
# np.random.uniform(low=0.0, high=1.0, size=None)
# 函数用于生成指定范围内的均匀分布的随机数。这个范围可以是闭区间 [low, high] ,也可以是半开半闭区间 [low, high) ,具体取决于参数 high 是否包含在内。
# low :生成随机数的下限,默认为0.0。
# high :生成随机数的上限(不包含),默认为1.0。如果要包含上限,可以将 high 设置为 low + 1 的某个小值(例如low + 1e-10)。
# size :输出的形状,可以是一个整数,表示输出的随机数的个数;也可以是一个元组,表示输出的多维数组的形状。如果省略,则返回一个标量。
r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains
# cv2.split(src)
# cv2.split() 方法是 OpenCV 库中的一个函数,它用于将多通道图像分割成单独的单通道图像。
# src :输入的多通道图像。
# 返回一个元组,其中包含输入图像的每个通道作为单独的平面。
# 这个方法通常用于分离彩色图像的 RGB 通道,或者分离其他类型的多通道图像,如 HSV 或 LAB 颜色空间的通道。
# cv2.cvtColor(src, code)
# cv2.cvtColor 是 OpenCV 库中用于转换图像颜色空间的函数。
# src :输入图像,可以是灰度图或彩色图。
# code :转换代码,指定从一种颜色空间转换到另一种颜色空间的类型。 code 参数是一个整数,用于指定转换类型。以下是一些常用的颜色空间转换代码 :
# cv2.COLOR_BGR2BGRA :将 BGR 转换为 BGRA(添加 Alpha 通道)。
# cv2.COLOR_BGR2GRAY :将 BGR 转换为灰度图。
# cv2.COLOR_BGR2HSV :将 BGR 转换为 HSV 颜色空间。
# cv2.COLOR_BGR2LAB :将 BGR 转换为 LAB 颜色空间。
# cv2.COLOR_BGR2RGB :将 BGR 转换为 RGB。
# cv2.COLOR_BGR2YUV :将 BGR 转换为 YUV 颜色空间。
# cv2.COLOR_GRAY2BGR :将灰度图转换为 BGR。
# cv2.COLOR_RGB2BGR :将 RGB 转换为 BGR。
# cv2.COLOR_YUV2BGR :将 YUV 转换为 BGR。
hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))
dtype = im.dtype # uint8
# np.arange([start=None], stop=None, [step=None], dtype=None)
# 生成一个从 start (包含)到 stop (不包含),以 step 为步长的序列。返回一个 ndarray 对象。
# 可以生成整型、浮点型序列,毫无压力。
# 当 step 参数为非整数时(如 step=0.1 ),结果往往不一致。对于这些情况,最好使用“ linspace() ”函数。
# 参数含义:
# start :数值型,可选填。包含此值。默认为0。
# stop :数值型,必填。不包含此值。除非 step 的值不是整数,浮点舍入会影响“ out ”的长度。
# step :数值型,可选填。默认为1,如果步长有指定,则 start 必须给出来。
# dtype :数据类型。输出的 array 数据类型。如果未指定 dtype ,则输出的 array 类型由其它的输入参数决定。
# start 、 stop 、 step 若任一个为浮点型,那么都会生成一个浮点型序列。
x = np.arange(0, 256, dtype=r.dtype)
lut_hue = ((x * r[0]) % 180).astype(dtype)
# np.clip(a, a_min, a_max, out=None, **kwargs)
# a :数组或数据框。
# a_min :下界,区间的最小值, a 中比 a_min 小的数都会强制变成 a_min 。
# a_max :上界,区间的最大值, a 中比 a_max 大的数都会强制变成 a_max 。
# out :可以指定输出矩阵的对象,shape与 a 相同。
# 该函数的作用是将 a 中的所有数限定到 a_min 和 a_max 这个区间中,超出这个区间的值都被截断设置成界限值。
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
# dst = cv2.merge([src1, src2, ..., srcn])
# cv2.merge 是 OpenCV 中的一个函数,它用于将多个单通道数组合并成一个多通道数组。这个函数通常用于将分离的颜色通道(例如,红色、绿色和蓝色通道)合并成一个完整的彩色图像。
# src1, src2, ..., srcn :这些是输入的单通道数组。它们必须具有相同的尺寸。
# dst :这是输出的多通道数组。每个 src 数组可以有不同的数据类型,但它们必须具有相同的尺寸。输出数组 dst 将具有与输入数组相同的尺寸,但其通道数等于输入数组的数量。
# dst = cv2.LUT(src, lut[, dst])
# cv2.LUT 是 OpenCV 库中的一个函数,它执行查找表(Look-Up Table,简称 LUT)转换。这个函数使用一个预先定义好的查找表来替换输入数组中的元素值。
# src :输入数组,它应该是一个 8 位的单通道或多通道数组。
# lut :查找表,它是一个大小为 256 的数组。如果输入数组是多通道的,查找表可以是单通道的(在这种情况下,相同的查找表将用于所有通道),或者它与输入数组具有相同数量的通道。
# dst :输出数组,其大小和通道数与 src 相同,深度与 lut 相同。如果未指定 dst ,则函数会自动分配一个输出数组。
# cv2.LUT 函数的工作原理是将输入数组 src 中的每个元素作为索引,然后在查找表 lut 中查找对应的值,并将其赋值给输出数组 dst 的相应位置。
# 如果输入数组 src 的深度是 CV_8U (8位无符号整数),则索引直接从 src 中获取;如果深度是 CV_8S (8位有符号整数),则索引从 src 中获取的值加上 128 作为索引。
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 无需返回值。
3.def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
# auto=True :自动最小外接矩形调整,保持原始比例且两边留有等宽的 padding。如果设置为 False,则不会自动调整填充。
# scaleup=True :是否允许放大图像。如果设置为 False,则只缩小图像,不放大,这有助于在验证集上提高 mAP(平均精度均值)表现。
# stride=32 :确保最终尺寸是该值的倍数。这通常与模型的 stride(步长)有关,以确保模型可以正确处理图像。
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
# 在满足步幅倍数约束的同时调整图像大小并填充。
'''Resize and pad image while meeting stride-multiple constraints.'''
shape = im.shape[:2] # current shape [height, width] 当前形状 [高度,宽度]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
elif isinstance(new_shape, list) and len(new_shape) == 1:
new_shape = (new_shape[0], new_shape[0])
# Scale ratio (new / old) 缩放比例(新/旧)。
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup: # only scale down, do not scale up (for better val mAP) 只缩小,不扩大(为了获得更好的 val mAP)。
r = min(r, 1.0)
# Compute padding 计算填充。
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding 宽高填充。
if auto: # minimum rectangle 最小矩形。
# y = np.mod(x1, x2[, out])
# 参数定义如下:
# x1 :第一个输入数组。
# x2 :第二个输入数组,其形状必须与 x1 相同,或者能够广播成 x1 的形状。
# out :(可选)用于存放结果的数组,它必须有正确的形状和类型。
# 返回值 :
# y :是一个数组,其形状与输入数组 x1 和 x2 相同,且每个元素是 x1 和 x2 对应元素相除后的余数。
# 需要注意的是, np.mod 的行为与 Python 内置的 % 运算符略有不同,特别是在处理负数时。 np.mod 总是返回一个非负余数,而 % 运算符则返回的余数的符号与被除数相同。
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding 宽高填充。
dw /= 2 # divide padding into 2 sides 将填充分成两边。
dh /= 2
if shape[::-1] != new_unpad: # resize 调整大小
# cv2.resize(src,dsize,dst=None,fx=None,fy=None,interpolation=None)
# 函数返回值 :变换大小后的图像
# src :待变换输入原图,可以为单通道灰度图像,也可以为3通道彩色图像。
# dsize :变换后的图像尺寸,格式:宽高通道;当dsize为0时,它可以通过以下公式计算得出 :dsize = Size(round(fxsrc.cols), round(fysrc.rows))。
# fx - 水平轴上的比例因子。当它为0时,计算公式如下 :(double)(dsize.width/src.cols)。
# fy - 垂直轴上的比例因子。当它为0时,计算公式如下 :(double)(dsize.heifht/src.rows)。
# interpolation - 插值方法。
# interpolation取值 :INTER_NEAREST - 最近邻插值法。
# interpolation取值 :INTER_LINEAR - 双线性插值法(默认)。
# interpolation取值 :INTER_AREA - 基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。
# interpolation取值 :INTER_CUBIC - 基于4x4像素邻域的3次插值法。
# interpolation取值 :INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值。
# 注意 :参数dsize和参数(fx, fy)不能够同时为0。
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
# cv2.copyMakeBorder(src: Mat, top, bottom, left, right, borderType, dts: Mat = ..., value=...)
# 在制作图像数据集时,有时需要调整图片的长和宽。采用 cv2.resize 函数会造成图像扭曲失真,因此需要采取填充图像短边的方法解决这个问题。
# cv2.copyMakeBorder(src: Mat, top, bottom, left, right, borderType, dts: Mat = ..., value=...)
# src :为输入图像。
# top :为图像顶部需要填充的边界宽度,单位为像素。
# bottom :为图像底部需要填充的边界宽度,单位为像素。
# left :为图像左侧需要填充的边界宽度,单位为像素。
# right :为图像右侧需要填充的边界宽度,单位为像素。
# borderType :为填充类型,如cv2.BORDER_CONSTANT,用常数填充;cv2.BORDER_ISOLATED,用黑色像素填充,等。
# dts :为输出图像。
# value :为常量填充的值,如value=(114,114,114),灰度填充。
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
# 1. im :这是经过尺寸调整和填充后的图像。它将被用于后续的目标检测模型输入。
# 2. r :这是一个包含两个元素的元组,表示图像的宽度和高度的缩放比例(ratio)。这个比例是原始图像尺寸与调整后图像尺寸之间的比例。在 YOLO 模型中,这个比例可能用于将检测框的坐标从模型输出的尺寸映射回原始图像尺寸。
# 3. (left, top) :这是一个包含两个元素的元组,表示图像在进行 letterbox 操作时,左和顶部的填充量。这些值同样用于将检测框的坐标映射回原始图像尺寸。
# 具体来说,检测框的坐标在模型输出时是在调整尺寸后的图像上定义的,而 (left, top) 告诉我们需要减去这些填充量,以便将这些坐标转换回原始图像的尺寸。
return im, r, (left, top)
4.def mixup(im, labels, im2, labels2):
def mixup(im, labels, im2, labels2):
# 应用 MixUp 增强 https://arxiv.org/pdf/1710.09412.pdf。
'''Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf.'''
# np.random.beta(a, b[, size])
# numpy.random 模块提供了产生各种分布随机数的API。
# 贝塔分布样本,在 [0, 1]内。
r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0 混合比率,alpha=beta=32.0
# 1. im * r :将第一个图像 im 的每个像素值乘以权重 r 。这个操作会改变 im 图像的透明度, r 越接近 1, im 图像越不透明; r 越接近 0, im 图像越透明。
# 2. im2 * (1 - r) :将第二个图像 im2 的每个像素值乘以 1 - r 。这个操作会改变 im2 图像的透明度, 1 - r 越接近 1, im2 图像越不透明; 1 - r 越接近 0, im2 图像越透明。
# 3. im * r + im2 * (1 - r) :将上述两个操作的结果相加,得到一个新的图像。这个新图像是 im 和 im2 的混合图像,其中 im 图像的透明度由 r 控制,而 im2 图像的透明度由 1 - r 控制。
# 4. .astype(np.uint8) :将混合后的图像数据类型转换为 np.uint8 ,这是图像处理中常用的数据类型,表示每个像素值是一个 0 到 255 之间的整数。
# 最终, im 被更新为混合后的图像。这种混合操作在图像处理和计算机视觉中很常见,例如在图像融合、图像合成、特效制作等领域。
im = (im * r + im2 * (1 - r)).astype(np.uint8)
# np.concatenate((a1, a2, ...), axis=0, out=None)
# np.concatenate函数是用于将两个或多个数组拼接在一起的常用函数。
# (a1, a2, ...) :数组序列,注意要用 () 或者 [] 符号括起来,否则会报错。
# axis :参数指定了拼接的轴向。默认情况下,axis=0表示按行拼接,axis=1表示按列拼接。
labels = np.concatenate((labels, labels2), 0)
return im, labels
def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n)
# 计算候选框: 增强前的box1 , 增强后的box2 , wh_thr(像素) , aspect_ratio_thr , area_ratio 。
'''Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio.'''
w1, h1 = box1[2] - box1[0], box1[3] - box1[1]
w2, h2 = box2[2] - box2[0], box2[3] - box2[1]
ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps)) # aspect ratio 宽高比
# 这个表达式的目的是筛选出满足特定条件的边界框(bounding boxes)。
# w2 > wh_thr :检查边界框的宽度 w2 是否大于某个阈值 wh_thr 。
# h2 > wh_thr :检查边界框的高度 h2 是否大于同一个阈值 wh_thr 。
# w2 * h2 / (w1 * h1 + eps) > area_thr :检查调整后的边界框面积 w2 * h2 与原始边界框面积 w1 * h1 的比值是否大于面积阈值 area_thr 。这里 eps 是一个很小的数,用来防止除以零的情况。
# ar < ar_thr :检查边界框的宽高比 ar (通常计算为 w2 / h2 )是否小于宽高比阈值 ar_thr 。
# 这个表达式返回一个布尔值,只有当所有条件都满足时,整个表达式才为真。 换句话说,它筛选出那些宽度和高度都大于 wh_thr ,调整后的面积与原始面积的比值大于 area_thr ,并且宽高比小于 ar_thr 的边界框。
# 这种类型的条件通常用于 :
# 1. 移除太小的边界框,这些可能不是有效的目标。
# 2. 确保边界框的面积变化在一个合理的范围内,这有助于在目标检测中减少误检。
# 3. 确保边界框的宽高比在一个合理的范围内,这有助于过滤掉形状不符合预期的边界框。
return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr) # candidates 候选框
5.def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10, new_shape=(640, 640)):
# 用于对图像和对应的标签(bounding boxes)应用随机仿射变换。这种变换在数据增强中常用于提高模型的泛化能力。
# 1.img :输入图像。
# 2.labels :图像中目标的标签,每个标签包含边界框的坐标(x1, y1, x2, y2)和类别。
# 3.degrees :旋转角度的范围,以度为单位。
# 4.translate :平移的比例。
# 5.scale :缩放的比例。
# 6.shear :剪切的角度范围,以度为单位。
# 7.new_shape :变换后图像的尺寸。
def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10,
new_shape=(640, 640)):
# 应用随机仿射变换。
'''Applies Random affine transformation.'''
n = len(labels)
height, width = new_shape
# 调用 get_transform_matrix 函数计算仿射变换矩阵 M 和缩放因子 s 。
M, s = get_transform_matrix(img.shape[:2], (height, width), degrees, scale, shear, translate)
# 如果 M 不是单位矩阵,说明图像需要变换,然后使用 cv2.warpAffine 函数应用仿射变换。
if (M != np.eye(3)).any(): # image changed
# cv2.warpAffine(src, M, dsize, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0))
# cv2.warpAffine 是 OpenCV 库中的一个函数,用于对图像应用仿射变换。这个函数通过指定的变换矩阵对图像进行几何变换,如旋转、缩放、平移和剪切等。
# 参数 :
# src :输入图像,可以是灰度图或彩色图。
# M :变换矩阵,是一个 2x3 的数组,表示仿射变换。
# dsize :输出图像的大小(宽度,高度)。可以与输入图像大小不同,以实现缩放效果。
# flags (可选):插值方法,默认为 cv2.INTER_LINEAR ,表示双线性插值。
# 其他选项包括 :
# cv2.INTER_NEAREST :最近邻插值。
# cv2.INTER_CUBIC :双三次插值。
# cv2.INTER_AREA :区域插值,适用于缩放变换。
# borderMode (可选):边界像素的填充方式,默认为 cv2.BORDER_CONSTANT ,表示以常数值填充。其他选项包括:
# cv2.BORDER_REPLICATE :复制边缘像素。
# cv2.BORDER_REFLECT :反射填充。
# cv2.BORDER_WRAP :环绕填充。
# borderValue (可选):用于边界填充的颜色值,默认为黑色(0, 0, 0)。对于灰度图像,只需提供一个值。
# 返回值 :
# 输出图像,其大小和类型由 dsize 和输入图像的深度决定。
img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114))
# 变换标签坐标。
# Transform label coordinates
# 如果有标签,创建新的标签数组 new 。
# 将标签的坐标转换为齐次坐标,并应用变换矩阵 M 。
# 将变换后的坐标重新缩放并裁剪到图像尺寸内。
# 创建新的边界框,并根据变换后的边界框和原始边界框的面积比过滤掉不合适的边界框。
if n:
new = np.zeros((n, 4))
xy = np.ones((n * 4, 3))
xy[:, :2] = labels[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1
xy = xy @ M.T # transform
xy = xy[:, :2].reshape(n, 8) # perspective rescale or affine
# create new boxes
x = xy[:, [0, 2, 4, 6]]
y = xy[:, [1, 3, 5, 7]]
new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
# clip
new[:, [0, 2]] = new[:, [0, 2]].clip(0, width)
new[:, [1, 3]] = new[:, [1, 3]].clip(0, height)
# filter candidates
i = box_candidates(box1=labels[:, 1:5].T * s, box2=new.T, area_thr=0.1)
labels = labels[i]
labels[:, 1:5] = new[i]
# 返回应用了仿射变换的图像和更新后的标签。
return img, labels
6.def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate):
def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate):
new_height, new_width = new_shape
# Center 中心
C = np.eye(3)
C[0, 2] = -img_shape[1] / 2 # x translation (pixels) x 平移(像素)。
C[1, 2] = -img_shape[0] / 2 # y translation (pixels) y 平移(像素)。
# 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)
# cv2.getRotationMatrix2D(center, angle, scale)
# cv2.getRotationMatrix2D 函数是 OpenCV 库中用于计算 2D 旋转的仿射变换矩阵的函数。这个矩阵随后可以用于 cv2.warpAffine 函数来对图像进行旋转。
# center :旋转的中心点,通常是一个 (x, y) 坐标的元组或 Point2f 类型,表示图像中旋转中心的位置。
# angle :旋转角度,以度为单位。正值表示逆时针旋转,负值表示顺时针旋转。
# scale :缩放因子,用于在旋转的同时对图像进行缩放。如果设置为 1 ,则表示图像在旋转时不进行缩放。
# 函数返回一个 2x3 的浮点数矩阵( float32 类型),该矩阵可以用来对图像进行旋转变换。这个矩阵包含了旋转和平移的信息,使得旋转可以围绕任意的中心点进行,而不仅仅是图像的原点。
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) x 剪切 (度)。
S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) y 剪切 (度)。
# Translation 平移。
T = np.eye(3)
T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_width # x translation (pixels) x 平移(像素)。
T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_height # y transla ion (pixels) y 平移(像素)。
# Combined rotation matrix 组合旋转矩阵。
M = T @ S @ R @ C # order of operations (right to left) is IMPORTANT 操作顺序(从右到左)很重要。
# 仿射变换矩阵 M 和缩放因子 s 。
return M, s
7.def mosaic_augmentation(img_size, imgs, hs, ws, labels, hyp):
# 1.img_size :整数,表示输出图像的尺寸。经过 Mosaic 增强后的图像将被调整到这个尺寸。
# 2.imgs :一个包含四个图像的列表或数组。这些图像将被用来创建 Mosaic 图像。通常,这些图像是从数据集中随机选取的。
# 3.hs 、4. ws :分别是与 imgs 中图像对应的高度和宽度数组。这些尺寸信息用于计算在创建 Mosaic 图像时各图像的相对位置和尺寸。
# 5.labels :一个包含四个图像对应的标签的列表或数组。这些标签包含了目标的位置和类别信息,在 Mosaic 增强过程中需要相应地调整以匹配新的图像布局。
# 6.hyp :一个超参数字典,包含了控制数据增强行为的参数,例如 mixup 比例等。
def mosaic_augmentation(img_size, imgs, hs, ws, labels, hyp):
# 应用马赛克增强。
'''Applies Mosaic augmentation.'''
# 目前版本的马赛克增强仅支持4张图片。
assert len(imgs) == 4, "Mosaic augmentation of current version only supports 4 images."
labels4 = []
s = img_size
# s//2 :这是整数除法,表示 s 除以 2 的结果向下取整。这确保了结果是一个整数。
# 3*s//2 :这表示 3 倍的 s 除以 2 的结果向下取整。
# random.uniform(s//2, 3*s//2) 产生一个随机浮点数,然后通过 int() 函数转换为整数。这个表达式在 range(2) 的范围内迭代,生成两个值。
yc, xc = (int(random.uniform(s//2, 3*s//2)) for _ in range(2)) # mosaic center x, y 马赛克中心 x, y。
for i in range(len(imgs)):
# Load image 载入图片。
img, h, w = imgs[i], hs[i], ws[i]
# place img in img4 将 img 放置在 img4 中。
if i == 0: # top left 左上角。
# np.full(shape, fill_value, dtype=None, order='C')
# 函数的基本功能是生成一个具有指定形状、数据类型和填充值的数组。
# shape :参数指定了输出数组的形状,可以是一个整数、元组或列表,表示数组的维度大小。
# fill_value :参数用于指定填充数组的元素值,可以是任何Python数据类型,包括数值、字符串、布尔值等。
# dtype :参数用于指定输出数组的数据类型,如果未指定,则根据 fill_value 的类型自动推断。
# order :参数用于指定数组的存储顺序,默认为 ‘C’,表示按行优先顺序存储。
img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles 包含 4 个图块的基础图像。
x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image)
elif i == 1: # top right 右上角。
x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
elif i == 2: # bottom left 左下角。
x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)
elif i == 3: # bottom right 右上角。
x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax]
padw = x1a - x1b
padh = y1a - y1b
# Labels
labels_per_img = labels[i].copy()
if labels_per_img.size:
boxes = np.copy(labels_per_img[:, 1:])
boxes[:, 0] = w * (labels_per_img[:, 1] - labels_per_img[:, 3] / 2) + padw # top left x
boxes[:, 1] = h * (labels_per_img[:, 2] - labels_per_img[:, 4] / 2) + padh # top left y
boxes[:, 2] = w * (labels_per_img[:, 1] + labels_per_img[:, 3] / 2) + padw # bottom right x
boxes[:, 3] = h * (labels_per_img[:, 2] + labels_per_img[:, 4] / 2) + padh # bottom right y
labels_per_img[:, 1:] = boxes
labels4.append(labels_per_img)
# Concat/clip labels 连接/剪辑标签。
labels4 = np.concatenate(labels4, 0)
for x in (labels4[:, 1:]):
np.clip(x, 0, 2 * s, out=x)
# Augment 增强。
# def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10, new_shape=(640, 640)): -> 应用随机仿射变换。 -> return img, labels
img4, labels4 = random_affine(img4, labels4,
degrees=hyp['degrees'],
translate=hyp['translate'],
scale=hyp['scale'],
shear=hyp['shear'],
new_shape=(img_size, img_size))
return img4, labels4
标签:YOLOv6,labels,4.0,augment,img,cv2,shape,图像,new
From: https://blog.csdn.net/m0_58169876/article/details/143377982