目标检测数据集图片及标签同步裁剪

devtools/2024/10/20 16:30:45/

目录

前言

具体方法

使用介绍

完整代码


前言

目标检测任务中,模型的训练依赖于大量高质量的标注数据。然而,获取足够多的标注数据集往往代价高昂,并且某些情况下,数据集中的样本分布不均衡,这会导致模型的泛化能力不足。为此,数据增强成为提升模型性能的常用方法之一。

数据增强的各种方法中,裁剪是一种高效且常用的技术。通过对图片进行随机或特定区域的裁剪,能够生成更多的训练样本,提高模型对不同尺度和位置目标的鲁棒性。此外,裁剪还可以有效解决原始图片尺寸过大的问题,避免对训练性能造成影响。大尺寸图片会增加模型的计算开销,导致训练速度变慢、内存消耗增大。而通过裁剪,图片尺寸得以缩小,训练效率随之提升。

然而,裁剪不仅仅是简单地对图片进行操作,目标检测任务中,每个目标的边界框(标签)必须与图片裁剪过程同步更新,否则裁剪后的标签将失去准确性,影响模型的训练效果。本篇文章将重点介绍如何在进行目标检测数据集图片裁剪时,同时对目标的标签进行精确调整,确保裁剪后的图片和标签保持一致,从而构建高质量的增强数据集,助力模型的准确性与泛化能力提升。

具体方法

本文所使用的裁剪方式为图片的宽度和高度各自从中间切开,将一个图片分为左上,右上,左下,右下四个部分,其中保留含有标签的那部分图片,其余未含有标签的部分则不保留。

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)


http://www.ppmy.cn/devtools/127331.html

相关文章

json路径 [‘a‘].b.c[0].d[‘1‘].f,具体代表什么意思

JSON路径是一种用于从JSON对象中提取数据的表达方式。你给出的路径 [a].b.c.d[1].f 代表了如何逐层访问JSON对象中的数据。让我们逐步解析这个路径&#xff1a; ‌[a]‌&#xff1a; 表示访问JSON对象的根元素中键为 a 的值。使用方括号 [] 通常意味着这个键是一个字符串&#…

信息与计算科学:“数学 + 计算机”,奏响未来科技新乐章

在当今科技飞速发展的时代&#xff0c;有一个专业如同一颗闪耀的新星&#xff0c;散发着独特的魅力&#xff0c;那就是信息与计算科学专业。 一、专业全貌&#xff1a;追根溯源&#xff0c;领略交叉之美 &#xff08;一&#xff09;专业的诞生与发展 1998 年&#xff0c;教育…

导尿管使用注意

现实生活中有一部分老年人得了前列腺增生后是相当的痛苦。当然很大一部分这类病人通过手术后痛苦就解决了。但是在这种疾病中有一部分人是不能通过手术解决的。 因为各种各样的原因一些病人不能做前列腺手术。不能手术又排不出尿&#xff0c;这部分病人就只能通过长期留置尿管…

Python酷玩之旅_数据分析入门(matplotlib)

导览 前言matplotlib入门1. 简介1.1 Pairwise data1.2 Statistical distributions1.3 Gridded data1.4 Irregularly gridded data1.5 3D and volumetric data 2. 实践2.1 安装2.2 示例 结语系列回顾 前言 翻看日历&#xff0c;今年的日子已划到了2024年10月19日&#xff0c;今天…

MusePose模型部署指南

一、模型介绍 MusePose是一个基于扩散和姿势引导的虚拟人视频生成框架。 主要贡献可以概括如下&#xff1a; 发布的模型能够根据给定的姿势序列&#xff0c;生成参考图中人物的舞蹈视频&#xff0c;生成的结果质量超越了同一主题中几乎所有当前开源的模型。发布该 pose alig…

对比损失(Contrastive Loss)详解

对比损失(Contrastive Loss)详解 对比损失(Contrastive Loss)是一种常见的度量学习损失函数,它通过学习样本对之间的相似性和差异性,使得相似样本对在特征空间中的距离更小,而不相似样本对的距离更大。这种方法广泛应用于人脸识别、图像检索等任务中。 核心思想 对比…

[LeetCode] 542. 01矩阵

题目描述&#xff1a; 给定一个由 0 和 1 组成的矩阵 mat &#xff0c;请输出一个大小相同的矩阵&#xff0c;其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1&#xff1a; 输入&#xff1a;mat [[0,0,0],[0,1,0],[0,0,0]] 输出…

自动机器学习(AutoML)

utoML是PAI的提供的自动寻找超参组合的机器学习增强型服务。您在训练模型时&#xff0c;如果超参组合复杂度过高&#xff0c;需大量训练资源和手工调试工作&#xff0c;可以使用AutoML来节省模型调参时间&#xff0c;提升模型调优效率和模型质量。 基础概念 超参数&#xff1a;…