当前位置: 首页 > news >正文

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

目录

前言

具体方法

使用介绍

完整代码


前言

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

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

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

具体方法

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

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.lryc.cn/news/466823.html

相关文章:

  • 【设计模式-简单工厂】
  • 多个版本的GCC(GNU编译器集合)可以同时安装并存
  • 量子纠错--shor‘s 码
  • 机器学习2
  • 二分查找_ x 的平方根搜索插入位置山脉数组的峰顶索引
  • 汽车建模用什么软件最好?汽车建模渲染建议!
  • 蘑菇分类识别数据集(猫脸码客 第222期)
  • 长短期记忆网络(Long Short-Term Memory,LSTM)
  • WHAT - 引入第三方组件或项目使用需要注意什么
  • 原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
  • WindTerm配置快捷键Ctrl+C和Ctrl+V
  • AOP学习
  • 【ubuntu18.04】ubuntu18.04升级cmake-3.29.8及还原系统自带cmake操作说明
  • 利用Docker搭建一套Mycat2+MySQL8一主一从、读写分离的最简单集群(保姆教程)
  • 算法——python实现堆排序
  • uniapp-components(封装组件)
  • avue-crud组件,输入框回车搜索问题
  • STM32F407ZGT6定时器相关测试
  • 群晖通过 Docker 安装 GitLab
  • 1.Node.js环境搭建(windows)
  • 链上相遇,节点之间的悸动与牵连
  • 一些简单的编程题(Java与C语言)
  • java计算机毕设课设—愤怒小鸟游戏(附源码、文章、相关截图、部署视频)
  • 【ARM】MDK-Flex服务管理软件使用说明
  • 【H2O2|全栈】WPS/Office系列有哪些好用的快捷方式?
  • 对比学习)
  • 第十六届蓝桥杯嵌入式真题
  • 音频转码常用命令
  • INNER JOIN、LEFT JOIN 和 RIGHT JOIN有什么区别?什么是自连接?
  • 原型模式具体和直接调用构造函数创建实例的区别