目标检测数据集图片及标签同步裁剪
目录
前言
具体方法
使用介绍
完整代码
前言
在目标检测任务中,模型的训练依赖于大量高质量的标注数据。然而,获取足够多的标注数据集往往代价高昂,并且某些情况下,数据集中的样本分布不均衡,这会导致模型的泛化能力不足。为此,数据增强成为提升模型性能的常用方法之一。
在数据增强的各种方法中,裁剪是一种高效且常用的技术。通过对图片进行随机或特定区域的裁剪,能够生成更多的训练样本,提高模型对不同尺度和位置目标的鲁棒性。此外,裁剪还可以有效解决原始图片尺寸过大的问题,避免对训练性能造成影响。大尺寸图片会增加模型的计算开销,导致训练速度变慢、内存消耗增大。而通过裁剪,图片尺寸得以缩小,训练效率随之提升。
然而,裁剪不仅仅是简单地对图片进行操作,目标检测任务中,每个目标的边界框(标签)必须与图片裁剪过程同步更新,否则裁剪后的标签将失去准确性,影响模型的训练效果。本篇文章将重点介绍如何在进行目标检测数据集图片裁剪时,同时对目标的标签进行精确调整,确保裁剪后的图片和标签保持一致,从而构建高质量的增强数据集,助力模型的准确性与泛化能力提升。
具体方法
本文所使用的裁剪方式为图片的宽度和高度各自从中间切开,将一个图片分为左上,右上,左下,右下四个部分,其中保留含有标签的那部分图片,其余未含有标签的部分则不保留。
def crop_image(image, save_dir, name, suf, boxes):H, W, _ = image.shape# 左上区域if boxes[0] == 1:img_top_left = image[0:H // 2, 0:W // 2]save_path = os.path.join(save_dir, f"{name}_1{suf}")cv.imwrite(save_path, img_top_left)# 右上区域if boxes[1] == 1:img_top_right = image[0:H // 2, W // 2:W]save_path = os.path.join(save_dir, f"{name}_2{suf}")cv.imwrite(save_path, img_top_right)# 左下区域if boxes[2] == 1:img_bottom_left = image[H // 2:H, 0:W // 2]save_path = os.path.join(save_dir, f"{name}_3{suf}")cv.imwrite(save_path, img_bottom_left)# 右下区域if boxes[3] == 1:img_bottom_right = image[H // 2:H, W // 2:W]save_path = os.path.join(save_dir, f"{name}_4{suf}")cv.imwrite(save_path, img_bottom_right)
这部分为裁剪图片的函数定义,可以看到只保留含有标签的部分图像
def split_box(box, shape):W, H = shape[1], shape[0]n, xmin, ymin, xmax, ymax = boxregions = []# 左上区域if xmin < W / 2 and ymin < H / 2:xmin_new = max(0, xmin)ymin_new = max(0, ymin)xmax_new = min(W / 2, xmax)ymax_new = min(H / 2, ymax)regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'left_top'))# 右上区域if xmax > W / 2 and ymin < H / 2:xmin_new = max(W / 2, xmin) - W / 2ymin_new = max(0, ymin)xmax_new = min(W, xmax) - W / 2ymax_new = min(H / 2, ymax)regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'right_top'))# 左下区域if xmin < W / 2 and ymax > H / 2:xmin_new = max(0, xmin)ymin_new = max(H / 2, ymin) - H / 2xmax_new = min(W / 2, xmax)ymax_new = min(H, ymax) - H / 2regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'left_bottom'))# 右下区域if xmax > W / 2 and ymax > H / 2:xmin_new = max(W / 2, xmin) - W / 2ymin_new = max(H / 2, ymin) - H / 2xmax_new = min(W, xmax) - W / 2ymax_new = min(H, ymax) - H / 2regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'right_bottom'))normalized_regions = []for region in regions:box = region[1:5] # 获取 (xmin, ymin, xmax, ymax)normalized_box = xyxy2xywhn((W/2, H/2), box) # 归一化normalized_regions.append((region[0], *normalized_box, region[5])) # 保留标签return normalized_regions
这部分函数为标签裁剪,这里我将每个标签都完整的分割出来,避免因为标签在图片中间交界处而导致的标签发生丢失现象,同时每部分标签加入一定的标志,全部运行完之后返回这部分标志,经过处理后传入图片裁剪中就可以知道哪部分图片需要保留,避免了图片重复写入。
使用介绍
完整代码如下,修改其中路径部分即可使用,需要注意本文仅支持标签文件为yolo格式的txt文件,如果对于转换格式不熟悉的可以去看我下面这篇文章,里面有相关的介绍及方法
VOC格式转YOLO格式,xml文件转txt文件简单通用代码_voc转yolo-CSDN博客文章浏览阅读509次,点赞8次,收藏4次。很多人在进行目标检测训练时习惯将得到的数据标注为XML文件的VOC格式,或者在网上获取的数据集被标注为XML文件,但是不同的标注工具进行的标注会产生不同的标注xml文件,这里我写了一种通用的针对含有最基本图片和标注坐标信息的xml进行转换,在这里简单介绍并分享出来xml文件中最基本需要含有的信息为size,object下的name和bndbox,具体示例如下图(如果xml文件中没有size也就是图片的宽和高则需要单独对每个图片进行读取,感兴趣可以私聊,这里不展开介绍)_voc转yolohttps://blog.csdn.net/qq_67105081/article/details/140000193?spm=1001.2014.3001.5501转换过之后想要再次转换回voc格式的xml文件可以去查看我的下面这篇博客
yolov8等目标检测数据集YOLO格式转VOC格式,txt文件转xml文件,格式转换_yolo格式标签转voc格式-CSDN博客文章浏览阅读255次,点赞3次,收藏8次。YOLO格式的标注文件是归一化之后的数据,每一行有5个数值,从左到右分别是类别,中心坐标x,中心坐标y,宽度w,高度h,这些数据经过计算后即可得到xmin,ymin,xmax,ymax的值,将这些信息相应的填入xml文件中即可。还有一个关键部分是xml文件的构建,如下代码构建,变量确认后替换。_yolo格式标签转voc格式https://blog.csdn.net/qq_67105081/article/details/142915326?spm=1001.2014.3001.5501
完整代码
# 作者:CSDN-笑脸惹桃花 https://blog.csdn.net/qq_67105081?type=blog
# github:peng-xiaobai https://github.com/peng-xiaobai/Data-Augmentor
import os
import numpy as np
import cv2 as cvdef GetFileList(dir):l = []files = os.listdir(dir)for file in files:if file.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tif')):l.append(os.path.join(dir, file))return ldef xywhn2xyxy(x, w, h):y = np.copy(x) if isinstance(x, np.ndarray) else x.copy() if hasattr(x, 'copy') else xy[0] = w * (x[0] - 0.5 * x[2])y[1] = h * (x[1] - 0.5 * x[3])y[2] = w * (x[0] + 0.5 * x[2])y[3] = h * (x[1] + 0.5 * x[3])return ydef xyxy2xywhn(size, box):x_center = (box[0] + box[2]) / 2.0y_center = (box[1] + box[3]) / 2.0w = box[2] - box[0]h = box[3] - box[1]x = x_center / size[0]y = y_center / size[1]w = w / size[0]h = h / size[1]return x, y, w, hdef crop_image(image, save_dir, name, suf, boxes):H, W, _ = image.shape# 左上区域if boxes[0] == 1:img_top_left = image[0:H // 2, 0:W // 2]save_path = os.path.join(save_dir, f"{name}_1{suf}")cv.imwrite(save_path, img_top_left)# 右上区域if boxes[1] == 1:img_top_right = image[0:H // 2, W // 2:W]save_path = os.path.join(save_dir, f"{name}_2{suf}")cv.imwrite(save_path, img_top_right)# 左下区域if boxes[2] == 1:img_bottom_left = image[H // 2:H, 0:W // 2]save_path = os.path.join(save_dir, f"{name}_3{suf}")cv.imwrite(save_path, img_bottom_left)# 右下区域if boxes[3] == 1:img_bottom_right = image[H // 2:H, W // 2:W]save_path = os.path.join(save_dir, f"{name}_4{suf}")cv.imwrite(save_path, img_bottom_right)def split_box(box, shape):W, H = shape[1], shape[0]n, xmin, ymin, xmax, ymax = boxregions = []# 左上区域if xmin < W / 2 and ymin < H / 2:xmin_new = max(0, xmin)ymin_new = max(0, ymin)xmax_new = min(W / 2, xmax)ymax_new = min(H / 2, ymax)regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'left_top'))# 右上区域if xmax > W / 2 and ymin < H / 2:xmin_new = max(W / 2, xmin) - W / 2ymin_new = max(0, ymin)xmax_new = min(W, xmax) - W / 2ymax_new = min(H / 2, ymax)regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'right_top'))# 左下区域if xmin < W / 2 and ymax > H / 2:xmin_new = max(0, xmin)ymin_new = max(H / 2, ymin) - H / 2xmax_new = min(W / 2, xmax)ymax_new = min(H, ymax) - H / 2regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'left_bottom'))# 右下区域if xmax > W / 2 and ymax > H / 2:xmin_new = max(W / 2, xmin) - W / 2ymin_new = max(H / 2, ymin) - H / 2xmax_new = min(W, xmax) - W / 2ymax_new = min(H, ymax) - H / 2regions.append((n, xmin_new, ymin_new, xmax_new, ymax_new, 'right_bottom'))normalized_regions = []for region in regions:box = region[1:5] # 获取 (xmin, ymin, xmax, ymax)normalized_box = xyxy2xywhn((W/2, H/2), box) # 归一化normalized_regions.append((region[0], *normalized_box, region[5])) # 保留标签return normalized_regionsdef save_boxes(label_path, boxes):# 先获取标注文件的基本路径和扩展名base_label_path, ext = os.path.splitext(label_path)for box in boxes:# 获取区域标识(左上、右上、左下、右下)region = box[5]if region == 'left_top':suffix = '_1'elif region == 'right_top':suffix = '_2'elif region == 'left_bottom':suffix = '_3'elif region == 'right_bottom':suffix = '_4'else:continue # 如果区域标识不在预期范围内,跳过specific_label_path = f"{base_label_path}{suffix}{ext}"with open(specific_label_path, 'a') as f:f.write(f"{int(box[0])} " + " ".join(map(str, box[1:5])) + '\n')fileDir = r"E:\peanut_data\j" # 原图片路径
label_path = r"E:\peanut_data\txt" # 原label的路径
list1 = GetFileList(fileDir)image_save_path_head = r"E:\peanut_data\j1" # 分割后有标注图片储存路径
label_save_path_head = r"E:\peanut_data\txt1" # 标签储存路径if not os.path.exists(image_save_path_head):os.makedirs(image_save_path_head)
if not os.path.exists(label_save_path_head):os.makedirs(label_save_path_head)for i in list1:l = [0, 0, 0, 0]img = cv.imread(i)shape = img.shapeseq = 1name, suf = os.path.splitext(os.path.basename(i))labelname = os.path.join(label_path, name) + '.txt' # 找到对应图片的labelpos = []with open(labelname, 'r') as f1:#print(labelname)while True:lines = f1.readline()if lines == '\n':lines = Noneif not lines:breakp_tmp = [float(i) for i in lines.split()]pos.append(p_tmp)pos = np.array(pos)for k in pos:k[1:] = xywhn2xyxy(k[1:], shape[1], shape[0])regions = split_box(k, shape)labelname_new = os.path.join(label_save_path_head, name) + '.txt'save_boxes(labelname_new, regions)for region in regions:region_name = region[-1]if region_name == 'left_top':l[0] = 1print("The region is 'left_top'.")elif region_name == 'right_top':l[1] = 1print("The region is 'right_top'.")elif region_name == 'left_bottom':l[2] = 1print("The region is 'left_bottom'.")elif region_name == 'right_bottom':l[3] = 1print("The region is 'right_bottom'.")f1.close()crop_image(img, image_save_path_head, name, suf, l)
相关文章:
目标检测数据集图片及标签同步裁剪
目录 前言 具体方法 使用介绍 完整代码 前言 在目标检测任务中,模型的训练依赖于大量高质量的标注数据。然而,获取足够多的标注数据集往往代价高昂,并且某些情况下,数据集中的样本分布不均衡,这会导致模型的泛化能…...
【设计模式-简单工厂】
定义 简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,用于通过一个工厂类来创建某个产品类的实例,而不直接在客户端(调用方)中实例化对象。 这种模式的主要思想是将对象的创建逻辑集中在一个…...
多个版本的GCC(GNU编译器集合)可以同时安装并存
在Ubuntu系统中,多个版本的GCC(GNU编译器集合)可以同时安装并存。GCC是编译C、C以及其他编程语言程序的重要工具,不同的项目可能需要不同版本的GCC来确保兼容性。 为什么需要多个GCC版本 项目依赖:不同的软件项目可能…...
量子纠错--shor‘s 码
定理1 (量子纠错的条件) C是一组量子编码,P是映射到C上的投影算子。假设是一个算子元素描述的量子操作,那么基于量子编码C,存在一个能对抗描述的噪声的纠错操作R的充要条件是 对某个复元素厄米矩阵成立。 将算子元素称为导致的错误。如果这样…...
机器学习2
一、模型评估方法 1.1 K折交叉验证法(K-Fold Cross Validation) 1.1.1 定义 K折交叉验证法是一种用于评估模型性能的技术。它将数据集分为K个相等的子集,模型会轮流使用一个子集作为测试集,其余K-1个子集作为训练集。这个过程会…...

二分查找_ x 的平方根搜索插入位置山脉数组的峰顶索引
x 的平方根 在0~X中肯定有数的平方大于X,这是肯定的。我们需要从中找出一个数的平方最接近X且不大于X。0~X递增,它们的平方也是递增的,这样我们就可以用二分查找。 我们找出的数的平方是<或者恰好X,所以把0~X的平方分为<X …...

汽车建模用什么软件最好?汽车建模渲染建议!
在汽车建模和渲染领域,选择合适的软件对于实现精确的设计与高质量的视觉效果至关重要。那么不少的汽车设计师如何选择合适的建模软件与渲染方案呢,一起来简单看看吧! 一、汽车建模用软件推荐 1、Alias Autodesk旗下的Alias系列软件是汽车设…...

蘑菇分类识别数据集(猫脸码客 第222期)
蘑菇分类识别文本/图像数据集 蘑菇,作为一种广泛分布于全球的真菌,隶属于伞菌目伞菌亚门蘑菇科蘑菇属,拥有众多别名,如白蘑菇、洋蘑菇等。其不仅是世界上人工栽培最广泛、产量最高、消费量最大的食用菌品种之一,还在许…...

长短期记忆网络(Long Short-Term Memory,LSTM)
简介:个人学习分享,如有错误,欢迎批评指正。 长短期记忆网络(Long Short-Term Memory,简称LSTM)是一种特殊的循环神经网络(Recurrent Neural Network,简称RNN)架构&#…...
WHAT - 引入第三方组件或项目使用需要注意什么
目录 1. 功能匹配2. 社区与维护3. 兼容性4. 性能5. 易用性6. 安全性7. 授权和许可证8. 国际化支持9. 依赖性10. 未来维护 在前端开发过程中引入第三方组件或项目时,应该从以下几个方面进行考虑,以确保引入的组件能够有效解决问题并适合长期维护ÿ…...

原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
华为于10月22日19:00举办“原生鸿蒙之夜暨华为全场景新品发布会”。此次发布会推出全新的原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)以及nova 13、WATCH Ultimate、MatePad Pro等新品。 据介绍,此前已经发布过的鸿蒙系统,由于系…...

WindTerm配置快捷键Ctrl+C和Ctrl+V
WindTerm配置快捷键CtrlC和CtrlV 平时使用ssh和sftp连接的时候,经常使用windterm, 但是windterm里面找不到相关的快捷键设置, 因为操作习惯,想把CtrlC和CtrlV分别配置为复制和粘贴,其他的快捷键操作可以按照该方法进…...

AOP学习
corol调用serverce不在是直接调用的是调用底层代理对象,由代理对象统一帮我们处理 AOP常见概念 通知类型 切面顺序...

【ubuntu18.04】ubuntu18.04升级cmake-3.29.8及还原系统自带cmake操作说明
参考链接 cmake升级、更新(ubuntu18.04)-CSDN博客 升级cmake操作说明 下载链接 Download CMake 下载版本 下载软件包 cmake-3.30.3-linux-x86_64.tar.gz 拷贝软件包到虚拟机 cp /var/run/vmblock-fuse/blockdir/jrY8KS/cmake-3.29.8-linux-x86_64…...

利用Docker搭建一套Mycat2+MySQL8一主一从、读写分离的最简单集群(保姆教程)
文章目录 1、Mycat介绍1.1、mycat简介1.2、mycat重要概念1.3、Mycat1.x与Mycat2功能对比1.2、主从复制原理 2、前提准备3、集群规划4、安装和配置mysql主从复制4.1、master节点安装mysql8容器4.2、slave节点安装mysql8容器4.2、配置主从复制4.3、测试主从复制配置 5、安装mycat…...

算法——python实现堆排序
文章目录 堆排序二叉树堆堆排序的过程:代码实现python中的heapq模块 堆排序 二叉树 关于二叉树的操作,其实核心就是 父节点找子节点,子节点找父节点 如果要将二叉树存储到队列中,就需要找出 父子节点之间的规律: 父…...

uniapp-components(封装组件)
<myitem></myitem> 在其他类里面这样调用。...
avue-crud组件,输入框回车搜索问题
crud组件,输入框回车搜索问题。 文档是并没有标注,实际上已经具备此功能。 需要在curd的option增加属性 searchEnter: true 即可实现输入内容后回车搜索。 avue的一些踩坑记录 - 前端小小菜 - 博客园...

STM32F407ZGT6定时器相关测试
结论: 20us以下的IO翻转操作,存在误差输出比较定时器使能与禁用功能正常输入捕获定时器使能与禁用功能正常单通道输出比较、输入捕获均正常多通道输出比较波形无干扰,但仍是存在20us以下的IO翻转操作存在误差多通道输入捕获正常 一、单一通…...

群晖通过 Docker 安装 GitLab
Docker 配置容器步骤都是大同小异的,可以参考: 群晖通过 Docker 安装 Gitea-CSDN博客 1. 在 Docker 文件夹中创建 GitLab,并创建子文件夹 2. 设置权限 3. 打开 Docker 应用,并在注册表搜索 gitlab-ce 4. 选择 gitlab-ce 映像运行…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...