Yolo V5 学习 ( 从数据集中获取图像和标签 __getitem__ )


1.引入库

代码如下(示例):

index = self.indices[index]  # 获取数据集中的索引
hyp = self.hyp	# 超参数
mosaic = self.mosaic and random.random() < hyp['mosaic']	# 是否使用马赛克增强

  self.indices 是一个列表,包含数据集中的索引
  hyp 是一个字典,包含了各种超参数,用于存储数据增强方法的概率和参数
例如,hyp[‘mosaic’] 表示使用马赛克增强的概率,hyp[‘mixup’] 表示使用 MixUp 增强的概率,hyp[‘hsv_h’]、hyp[‘hsv_s’] 和 hyp[‘hsv_v’] 分别表示 HSV 颜色空间增强的色调、饱和度和明度增益。
  mosaic 通过检查 self.mosaic 和 random.random() < hyp[‘mosaic’] 来实现的。如果这两个条件都为真,则将 mosaic 变量设置为 True。

2. 马赛克

2.1 使用马赛克 mosaic == True

代码如下(示例):

if mosaic:
    img, labels = self.load_mosaic(index)	# 加载马赛克图像和标签,返回一个包含图像和标签的元组
    shapes = None
    if random.random() < hyp['mixup']:	# 是否使用 MixUp 增强
        img, labels = mixup(img, labels, *self.load_mosaic(random.randint(0, self.n - 1)))	# 返回一个包含混合后的图像和标签的元组

  MixUp 是一种数据增强技术,它通过将两个训练样本的特征和标签混合在一起来创建新的虚拟训练样本。使用公式实现
new_x = lambda * x1 + (1 - lambda) * x2 new_y = lambda * y1 + (1 - lambda) * y2
  其中 x1 和 x2 是图像,y1 和 y2 是标签,lambda 是一个在 [0, 1] 范围内的值,从 Beta 分布中采样得到。
  MixUp 可以帮助神经网络避免记忆错误的标签,因为它通过将不同的特征和标签混合在一起,使网络不会对特征和标签之间的关系过于自信。

2.1.1 加载马赛克图像和标签 (load_mosaic)

  load_mosaic 方法用于加载马赛克图像和标签。马赛克增强是一种数据增强技术,它通过将四个训练图像拼接在一起来创建新的虚拟训练图像。

labels4, segments4 = [], []		# 用于存储马赛克图像的标签和分段
s = self.img_size	# 获取图像的大小
# random.uniform() 用于在给定范围内生成随机数
yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border)  # 计算马赛克图像的中心坐标	
indices = [index] + random.choices(self.indices, k=3)  # 从数据集中随机选择三个索引 
random.shuffle(indices)		# 然后打乱索引的顺序
for i, index in enumerate(indices):		#遍历索引
	
	# 加载图像 返回图像,形状,(尺寸) 形状和尺寸分别表示图像的原始形状和调整后的尺寸
    img, _, (h, w) = self.load_image(index)		
	
	# 根据索引在列表中的位置,计算图像在马赛克图像中的位置
    if i == 0:  # top left	
    	# 1.初始化大图;2.计算当前图片放在大图中什么位置;3.计算在小图中取哪一部分放到大图中
        img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles
        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)

	# 使用 NumPy 索引将源图像的一部分复制到目标图像中
    img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]
    # 计算 左上角 起始偏移量
    padw = x1a - x1b
    padh = y1a - y1b

    # 获取图像对应的标签和分段
    labels, segments = self.labels[index].copy(), self.segments[index].copy()
	# 检查标签是否存在 即labels.size是否大于零
	if labels.size:
		# 调用 xywhn2xyxy 函数将标签从归一化的 xywhn 格式转换为像素 xyxy 格式
		# 接受标签、图像宽度、图像高度和填充值作为输入,并返回转换后的标签
        labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh)  
        
        # 使用xyn2xy 函数将分段从归一化的 xyn 格式转换为像素 xy 格式
        # 接受分段、图像宽度、图像高度和填充值作为输入,并返回转换后的分段
        segments = [xyn2xy(x, w, h, padw, padh) for x in segments]
        
    # 使用.append() 将标签添加到 labels4 列表
    labels4.append(labels)
    
    # 使用.extend() 将分段添加到 segments4 列表
    segments4.extend(segments)

  self.mosaic_border 是一个包含两个元素的元组,表示马赛克边界的 x 和 y 值。
  random.uniform(-x, 2 * s + x) 函数用于在给定范围内生成随机数。

   对于第一个位置(即左上角),代码首先创建一个填充值为 114 的全新图像

 img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)

然后,它计算源图像和目标图像的坐标:

x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc
x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h

其中 x1a, y1a, x2a, y2a 是目标图像(即马赛克图像)中要复制到的区域的坐标,而 x1b, y1b, x2b, y2b 是源图像中要复制的区域的坐标。

# 使用np.concatenate() 将labels4 列表中的标签连接起来
labels4 = np.concatenate(labels4, 0)
# 坐标计算后可能越界,调整坐标值,使其都在大图中
for x in (labels4[:, 1:], *segments4):
	# 使用 np.clip() 对标签和分段进行裁剪
	# 确保它们的值在 [0, 2 * s] 范围内
    np.clip(x, 0, 2 * s, out=x)  

# 使用copy_paste() 增强复制粘贴
# 接受马赛克图像、标签、分段和复制粘贴概率作为输入,并返回增强后的图像、标签和分段
img4, labels4, segments4 = copy_paste(img4, labels4, segments4, p=self.hyp['copy_paste'])

# 使用random_perspective() 做随机透视变换增强。
# 接受马赛克图像、标签、分段和各种超参数作为输入,并返回增强后的图像和标签。
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 
# 返回增强后的马赛克图像和标签
return img4, labels4

2.2 不使用马赛克 mosaic == False

代码如下(示例):

 img, (h0, w0), (h, w) = self.load_image(index)	# 加载图像
 shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size # final letterboxed shape
 img, ratio, pad = letterbox(img, shape, auto=False, scaleup=self.augment)
 shapes = (h0, w0), ((h / h0, w / w0), pad) # 计算图像的最终字母框形状 	

 labels = self.labels[index].copy()	
 if labels.size: 	# 获取图像对应的标签
 	# 从归一化的 xywh 格式转换为像素 xyxy 格式.通过调用 xywhn2xyxy 函数并使用缩放比例和填充值来实现
 	labels[:, 1:] = xywhn2xyxy(labels[:, 1:], ratio[0] * w, ratio[1] * h, padw=pad[0], padh=pad[1])

 if self.augment:	# 是否数据增强
 	img, labels = random_perspective(img,
	    labels,
	    degrees=hyp['degrees'],
	    translate=hyp['translate'],
	    scale=hyp['scale'],
	    shear=hyp['shear'],
	    perspective=hyp['perspective'])

  使用 self.load_image(index) 方法加载图像,并获取图像的原始高度和宽度(h0 和 w0)以及调整后的高度和宽度(h 和 w)
   字母框形状(letterbox shape)是指将图像调整为特定形状时,通过在图像周围添加填充来保持图像的纵横比。这种方法类似于在电视上观看宽屏电影时,在画面的顶部和底部添加黑色条纹。
  使用 letterbox 函数将图像调整为该形状,letterbox 函数返回调整后的图像、缩放比例和填充值。
  random_perspective 函数来应用随机透视变换。此函数接受图像、标签和各种超参数作为输入,并返回增强后的图像和标签。


总结

  使用__getitem__可以快速的将数据经行预处理,并拿到处理后的图像和标签