数据增强mosaic实现

news/2024/7/4 23:58:43/文章来源:https://www.cnblogs.com/goldsunshine/p/18279312

image

mosaic 是yolov4中提出的一个数据增强的方式,通过将4张图片拼接在一起送入训练,有效提升了模型的map。mosaic的优点包括如下:

  1. 增加数据多样性,随机选取四张图像进行组合,组合得到图像个数比原图个数要多。
  2. 增强模型鲁棒性,混合四张具有不同语义信息的图片,可以让模型检测超出常规语境的目标。
  3. 加强批归一化层(Batch Normalization)的效果。当模型设置 BN 操作后,训练时会尽可能增大批样本总量(BatchSize),因为 BN 原理为计算每一个特征层的均值和方差,如果批样本总量越大,那么 BN 计算的均值和方差就越接近于整个数据集的均值和方差,效果越好。
  4. Mosaic 数据增强算法有利于提升小目标检测性能。Mosaic 数据增强图像由四张原始图像拼接而成,这样每张图像会有更大概率包含小目标。
  5. 训练的难度增大,有助于模型性能的提

本篇分析mosaic的实现方式。

代码分析

    def load_mosaic(self, index):# YOLOv5 4-mosaic loader. Loads 1 image + 3 random images into a 4-image mosaiclabels4, segments4 = [], []s = self.img_sizeyc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border)  # mosaic center x, yindices = [index] + random.choices(self.indices, k=3)  # 3 additional image indicesrandom.shuffle(indices)for i, index in enumerate(indices):# Load imageimg, _, (h, w) = self.load_image(index)# place img in img4if i == 0:  # top leftimg4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tilesx1a, 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 rightx1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), ycx1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), helif i == 2:  # bottom leftx1a, 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 rightx1a, 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 - x1bpadh = y1a - y1b# Labelslabels, segments = self.labels[index].copy(), self.segments[index].copy()if labels.size:labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh)  # normalized xywh to pixel xyxy formatsegments = [xyn2xy(x, w, h, padw, padh) for x in segments]labels4.append(labels)segments4.extend(segments)# Concat/clip labelslabels4 = np.concatenate(labels4, 0)for x in (labels4[:, 1:], *segments4):np.clip(x, 0, 2 * s, out=x)  # clip when using random_perspective()# img4, labels4 = replicate(img4, labels4)  # replicate# Augmentimg4, labels4, segments4 = copy_paste(img4, labels4, segments4, p=self.hyp['copy_paste'])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 removereturn img4, labels4

马赛克之后的结果如下:

mosaic 的过程可以分为如下几步:

  1. 确定四张图片的中心点
  2. 计算上下左右四张图片的左上右下坐标点
  3. 图片上的标注框做相应的转换
  4. 裁剪标注框多余部分

确定四张图片的中心点

马赛克的第一步就是选择一个中心点,以中心点为基准放置4张图片。中心点选择不是随便的,需要一个范围能够马赛克之后的效果可控。

labels4, segments4 = [], []
s = self.img_size
yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border)  # mosaic center x, y

其中 self.mosaic_border = [-img_size // 2, -img_size // 2],所以 self.mosaic_border = [320, 320]
s = 640,所以 random.uniform(320, 960) 就是用320到960之间选择四张图片的中心点。

计算四张图片的左上右下坐标点

确定了中心点之后就可以将四张图片分别放置在上左,上右,下左,下右四个位置上。

indices = [index] + random.choices(self.indices, k=3)  # 3 additional image indices
random.shuffle(indices)
for i, index in enumerate(indices):# Load imageimg, _, (h, w) = self.load_image(index)# place img in img4if i == 0:  # top leftimg4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tilesx1a, 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 rightx1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), ycx1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), helif i == 2:  # bottom leftx1a, 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 rightx1a, 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]

从剩余的图片中随机抽取三张,包括当前这一张一共四张,打乱顺序。遍历四张图片,分别处理。首先处理上左。

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

首先建立一张空画布,长宽都是两张图片的长宽,1280 * 1280,空白画布如下:

然后画布中上左图片的位置。x1a, y1a是图片在画布中左上角的坐标,x2a, y2a是右下角的坐标。

 x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)

x2a, y2a 可以想象就是中心点,也就是xc, yc。x1a 等于中心点横坐标减去图片宽,y1a等于中心点纵坐标减去图片高。可能会出现中心坐标比较靠左,图片超过画布的情况,x1a,y1a会变成一个负值,所以需要判断,当x1a,y1a变成一个负值时,要等于0。
可能存在的如下两种情况:

x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h

当出现第二种情况时,就需要先裁剪图片再填充到画布中,所以还需要计算出图片裁剪的坐标。
x1b,y1b是裁剪的左上角坐标, x2b, y2b是裁剪的右下角坐标。当然,如果图片没有超出画布,这两个值就刚好等于图片的全部大小。

img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]

最后将图片裁剪之后的像素拷贝到画布中。
剩余三个照片也是同理粘贴。

图片上的标注框做相应的转换

将图片放置在画布上之后,标注框也要放置在画布上。由于图片放在画布上会让标注框产生偏移,所以需要将这些偏移量加到标注框上。

padw = x1a - x1b
padh = y1a - y1b# Labels
labels, segments = self.labels[index].copy(), self.segments[index].copy()
if labels.size:labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh)  # normalized xywh to pixel xyxy formatsegments = [xyn2xy(x, w, h, padw, padh) for x in segments]
labels4.append(labels)
segments4.extend(segments)

假设画布中心点就是正方形中心,那么左上角的图片则刚贴合。标注框相当于图片的左上角而言,现在图片和画布上左完全重合,就不需要处理。但是通常画布的中心点不会那么正,相对于中心点有偏移。图片相对于画布的左上角也有偏移,那么标注框也得加上对应的偏移才能回到图片上的位置。
偏移量的计算如下:
padw = x1a - x1b
padh = y1a - y

在xywhn2xyxy中将xywh格式的标注框转换成xyxy格式,同时加上对应的偏移量。让标注框从以原图为坐标系变成以画布为坐标系。

def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0):# Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-righty = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)y[:, 0] = w * (x[:, 0] - x[:, 2] / 2) + padw  # top left xy[:, 1] = h * (x[:, 1] - x[:, 3] / 2) + padh  # top left yy[:, 2] = w * (x[:, 0] + x[:, 2] / 2) + padw  # bottom right xy[:, 3] = h * (x[:, 1] + x[:, 3] / 2) + padh  # bottom right yreturn y

同理上右也是如此:

裁剪标注框多余部分

将标注框超出画布的部分裁剪掉,下图的蓝框所在区域。

# Concat/clip labels
labels4 = np.concatenate(labels4, 0)
for x in (labels4[:, 1:], *segments4):np.clip(x, 0, 2 * s, out=x)  # clip when using random_perspective()

np.clip 用于将数组中的元素限制在指定的范围内。如果元素的值低于给定的最小值,则会被设置为该最小值;如果元素的值高于给定的最大值,则会被设置为该最大值。

In [22]: arr = np.array([-1, 0.5, 1.5, 2, 3])
In [23]: arr
Out[23]: array([-1. ,  0.5,  1.5,  2. ,  3. ])In [24]: np.clip(arr, 0, 2)
Out[24]: array([0. , 0.5, 1.5, 2. , 2. ])

使用np.clip函数将标签超出的部分都裁剪掉。

将标注框显示在图片中的效果如下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/736178.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot3(cloud 2022.0.0)整合seata1.7.1

一、第一步下载对应版本的seata服务 二、修改conf下的application.yml配置 注意:主要是连接nacos的一些配置:注册中心和服务发现的配置1 # Copyright 1999-2019 Seata.io Group.2 #3 # Licensed under the Apache License, Version 2.0 (the "License");4 # you…

Linux 文件系统扩展

今天发现/usr目录空间不够。需要扩展。之前没有处理过。今天试了一下调整。并且记录下来整个过程。 1 使用fdisk -l 查看磁盘信息2、使用fdisk /dev/sda 对磁盘进行分区操作完后,多了一个/dev/sda34 使用 pvcreate /dev/sda3创建物理卷5、使用vgextend ol /dev/sda3 扩展磁盘 …

华为云技术专家硬核分享,云原生中间件如何加速业务发展

云原生中间件作为云原生架构中的关键组件,在核心能力建设、业务连续性、生态丰富性等诸多方面也面临着一系列挑战。本文分享自华为云社区《云原生中间件,构筑软件安全可信的连接桥梁》,作者: 华为云PaaS服务小智。 近日,在华为云开发者大会2024期间,来自华为云PaaS服务,…

KES数据库实践指南:探索KES数据库的事务隔离级别

本文深入探讨了KES数据库中的并发控制机制和事务隔离级别的重要性及实施方法。我们从并发控制的基本概念出发,详细解释了ACID原则如何通过不同的隔离级别得以实现,以及在串行化与并行执行之间的权衡取舍。通过实际操作和示例,我们展示了不同隔离级别下可能出现的脏读、不可重…

最新扣子(Coze)实战案例:图像流工具之创建一个精美的LOGO,完全免费教程

🧙‍♂️ 大家好,我是斜杠君,手把手教你搭建扣子AI应用。 📜 本教程是《AI应用开发系列教程之扣子(Coze)实战教程》,完全免费学习。 👀 关注公众号:斜杠君,可获取完整版教程。 👍🏻如果想学习AI应用搭建,请关注公众号,及时获取最新免费教程。温馨提示:本文内…

2024年6.23-6.26学习总结

2024年6.23--6.26学习总结 考试 24号考计算机网络,26号考形式语言与自动机。这几天基本都在复习。 形式语言与自动机 dfa极小化 1.先删去不可达状态和陷阱态。 2.假如状态有q1,q2,q3,q4,q5,q6,q7,q8,先将所有终止状态取出分为一组比如{q6,q8},然后在另一组中找到无法区分的状…

大型能源电力集团需要什么样的总部数据下发系统?

能源电力集团的组织结构是一个复杂的系统,包括多个职能部门和子分公司。这些子分公司负责具体的电力生产、销售、运维等业务。这些部门和公司协同工作,确保电力生产的顺利进行,同时关注公司的长期发展、市场拓展、人力资源管理、财务管理和公司治理等方面。由于大型能源电力…

ros slam microros之H电桥电机驱动原理介绍

一、电机驱动原理介绍正式编写代码前,我们先了解下电机驱动的原理,以便于我们了解我们如何才能通过代码控制电机的转速和正反转。1.1 H桥电路让电机动起来只需要通电就行,比如我们用的额定电压为12V 130RPM的电机,当给到12V的电压时可以达到额定转速130转/分,但如果我们给…

ros slam 之移动机器人常用传感器

本节我们对移动机器人底盘结构进行简单的介绍,并着重介绍FishBot基础版的组成结构。 对于一个移动底盘来说,所要提供的核心功能有两个-感知和执行能力,我们分别进行介绍。一、感知-传感器所谓感知即通过给类传感器获取环境信息的能力。在移动机器人中,我们常用的传感器有 距…

VMware ESXi 8.0U3 macOS Unlocker OEM BIOS HPE (慧与) 定制版

VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS HPE (慧与) 定制版VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS HPE (慧与) 定制版 ESXi 8.0U3 标准版,Dell (戴尔)、HPE (慧与)、Lenovo (联想)、Inspur (浪潮)、Cisco (思科)、Hitachi (日立)、Fujitsu (富士通)、NEC…

day33-Django3.2(二)

四、视图 django的视图主要有2种,分别是函数视图和类视图.现在刚开始学习django,我们先学习函数视图(FBV),后面再学习类视图[CBV]. 4.1、请求方式 web项目运行在http协议下,默认肯定也支持用户通过不同的http请求发送数据来。django支持让客户端只能通过指定的Http请求来访问到…

为什么单元测试不是持续交付的唯一答案

残酷的事实是,大多数企业在持续交付的道路上相当落后。为了让持续集成和持续交付(CI/CD)成为现实,企业必须审查其内部流程,并重新思考如何处理软件交付生命周期。过去的清单和评论根本不是前进的方向。残酷的事实是,大多数企业在持续交付的道路上相当落后。对软件交付过程…

蓝桥杯Java组常用知识点

备战蓝桥杯学习笔记基本数据类型 int的取值范围: -2^31 ~ 2^31-1 -2147483648 ~ 2147483647(约等于10的9次方) long long的取值范围: -2^63 ~ (2^63-1) -9223372036854775808 ~ 9223372036854775807(约等于10的18次方) 输入输出 使用文件流对输入输出的重要性:https://…

Linux内存不够了?看看如何开启虚拟内存增加内存使用量

1、为什么要使用虚拟内存 当我们没有多余的钱去购买大内存的云服务器时,但是当前服务器里面的软件和程序运行的比较多导致内存不够用了。这个时候可以通过增加虚拟内存来扩大内存容量。但是在启用虚拟内存时,需要仔细考虑系统的实际需求和硬件配置,以及权衡虚拟内存的优缺点…

camunda数据库表结构详细说明

本文基于Camunda7.19.0版本,介绍Camunda开源工作流引擎的数据库架构和ER模型,Camunda7.19.0共49张表,包括了BPMN流程引擎、DMN规则引擎、CMMN引擎、历史数据、用户身份等方面的表结构定义,以及表与表之间的关联关系。本文基于Camunda7.19.0版本,介绍Camunda开源工作流引擎…

camunda多租户技术架构介绍和测试验证

多租户考虑的是单个 Camunda 安装应该为多个租户提供服务的情况。对于每个租户,应做出一定的隔离保证。例如,一个租户的流程实例不应干扰另一租户的流程实例。 多租户可以通过两种不同的方式实现。一种方法是每个租户使用一个流程引擎。另一种方法是仅使用一个流程引擎并将数…

IEEE 8802-3 以太网标准解读

PHY: CarrierSense 载波侦听 ReceveDataValid 接受数据有效 CollisionDetect 碰撞检测 Transmitting 传输中 TransmitBit 传输比特 SFD 10101011 开始 ReceiveBit 接受比特 Wait 等待1、MA_DATA.request 定义了MAC客户端访问单独实体或者z在组地址的前提下访问多个实体 MA…

在C#中使用RabbitMQ做个简单的发送邮件小项目

在C#中使用RabbitMQ做个简单的发送邮件小项目 前言 好久没有做项目了,这次做一个发送邮件的小项目。发邮件是一个比较耗时的操作,之前在我的个人博客里面回复评论和友链申请是会通过发送邮件来通知对方的,不过当时只是简单的进行了异步操作。 那么这次来使用RabbitMQ去统一发…

【进阶篇】Java 项目中对使用递归的理解分享

笔者在最近的项目开发中,遇到了两个父子关系紧密相关的场景:评论树结构、部门树结构。具体的需求如:找出某条评论下的所有子评论id集合,找出某个部门下所有的子部门id集合。【进阶篇】Java 项目中对使用递归的理解分享 目录【进阶篇】Java 项目中对使用递归的理解分享前言一…

C#/.NET/.NET Core编程技巧练习集(学习,实践干货)

DotNet Exercises介绍 DotNetGuide专栏C#/.NET/.NET Core编程常用语法、算法、技巧、中间件、类库练习集,配套详细的文章教程讲解,助你快速掌握C#/.NET/.NET Core各种编程常用语法、算法、技巧、中间件、类库等等。GitHub开源地址:https://github.com/YSGStudyHards/DotNetE…