目标检测数据集图片及标签同步旋转角度
前言
在深度学习领域,尤其是目标检测任务中,数据集的质量直接影响模型的性能。为了提升模型的鲁棒性和对各种场景的适应能力,数据增强技术被广泛应用于图像数据集处理。旋转角度是常见的数据增强方法,通过对图像及其对应的标签(边界框)进行同步旋转,可以有效地增加模型对不同方向目标的识别能力。然而,在进行旋转变换时,除了图像本身需要旋转外,与之对应的标签(边界框)也需要进行同步更新。否则,标签位置的偏差将导致模型训练出现误差,甚至无法正确识别目标。
本篇文章将介绍如何在目标检测数据集中的图像和标签(边界框)同步旋转(仍为矩形)。通过同步旋转图像和标签,能够确保数据增强过程中图像与标签的一致性,从而提高模型训练的效果和准确性。
效果如图,倾斜旋转。

代码
import os
import cv2
import math
import shutil
import numpy as np
from lxml import etree, objectifyclass ImageXMLProcessor:def __init__(self, input_img_folder, input_xml_folder, output_img_folder, output_xml_folder, rotation_angle=5, scale=1):self.input_img_folder = input_img_folderself.input_xml_folder = input_xml_folderself.output_img_folder = output_img_folderself.output_xml_folder = output_xml_folderself.rotation_angle = rotation_angleself.scale = scaleos.makedirs(self.output_img_folder, exist_ok=True)os.makedirs(self.output_xml_folder, exist_ok=True)def rotate_img_bbox(self, img, bboxes, angle=5, scale=1.):w = img.shape[1]h = img.shape[0]rangle = np.deg2rad(angle)nw = (abs(np.sin(rangle) * h) + abs(np.cos(rangle) * w)) * scalenh = (abs(np.cos(rangle) * h) + abs(np.sin(rangle) * w)) * scalerot_mat = cv2.getRotationMatrix2D((nw * 0.5, nh * 0.5), angle, scale)rot_move = np.dot(rot_mat, np.array([(nw - w) * 0.5, (nh - h) * 0.5, 0]))rot_mat[0, 2] += rot_move[0]rot_mat[1, 2] += rot_move[1]rot_img = cv2.warpAffine(img, rot_mat, (int(math.ceil(nw)), int(math.ceil(nh))), flags=cv2.INTER_LANCZOS4)rot_bboxes = list()for bbox in bboxes:xmin = bbox[0]ymin = bbox[1]xmax = bbox[2]ymax = bbox[3]point1 = np.dot(rot_mat, np.array([(xmin + xmax) / 2, ymin, 1]))point2 = np.dot(rot_mat, np.array([xmax, (ymin + ymax) / 2, 1]))point3 = np.dot(rot_mat, np.array([(xmin + xmax) / 2, ymax, 1]))point4 = np.dot(rot_mat, np.array([xmin, (ymin + ymax) / 2, 1]))concat = np.vstack((point1, point2, point3, point4))concat = concat.astype(np.int32)rx, ry, rw, rh = cv2.boundingRect(concat)rx_min = rxry_min = ryrx_max = rx + rwry_max = ry + rhrot_bboxes.append([rx_min, ry_min, rx_max, ry_max])return rot_img, rot_bboxesdef save_xml(self, file_name, save_folder, img_info, height, width, channel, bboxs_info):folder_name, img_name = img_info # 得到图片的信息E = objectify.ElementMaker(annotate=False)# 创建 XML 树结构anno_tree = E.annotation(E.folder(folder_name),E.filename(img_name),E.path(os.path.join(folder_name, img_name)),E.source(E.database('Unknown'),),E.size(E.width(width),E.height(height),E.depth(channel)),E.segmented(0),)labels, bboxs = bboxs_info # 得到边框和标签信息for label, box in zip(labels, bboxs):anno_tree.append(E.object(E.name(label),E.pose('Unspecified'),E.truncated('0'),E.difficult('0'),E.bndbox(E.xmin(box[0]),E.ymin(box[1]),E.xmax(box[2]),E.ymax(box[3]))))os.makedirs(save_folder, exist_ok=True)file_path = os.path.join(save_folder, file_name)etree.ElementTree(anno_tree).write(file_path, pretty_print=True, xml_declaration=True, encoding="UTF-8")print(f"XML 文件已保存到: {file_path}")def update_bboxes_in_xml(self, xml_path, bboxes):tree = etree.parse(xml_path)root = tree.getroot()# 找到所有的 <object> 元素并更新对应的 <bndbox> 坐标obj_elements = root.findall("object")for idx, object_element in enumerate(obj_elements):# 获取原始的 bndboxbndbox_element = object_element.find("bndbox")if bndbox_element is not None:# 只有当 bbox 存在时才更新xmin_element = bndbox_element.find("xmin")ymin_element = bndbox_element.find("ymin")xmax_element = bndbox_element.find("xmax")ymax_element = bndbox_element.find("ymax")# 更新坐标if idx < len(bboxes):bbox = bboxes[idx]xmin_element.text = str(bbox[0])ymin_element.text = str(bbox[1])xmax_element.text = str(bbox[2])ymax_element.text = str(bbox[3])# 保存更新后的 XML 文件tree.write(xml_path, pretty_print=True, xml_declaration=True, encoding="UTF-8")print(f"XML 文件已更新并保存到: {xml_path}")def process(self):# 遍历输入文件夹中的所有图片和XML文件for img_file in os.listdir(self.input_img_folder):if img_file.endswith('.jpg') or img_file.endswith('.png'): # 支持jpg/png格式# 获取对应的XML文件xml_file = os.path.splitext(img_file)[0] + '.xml'img_path = os.path.join(self.input_img_folder, img_file)xml_path = os.path.join(self.input_xml_folder, xml_file)if os.path.exists(xml_path):shutil.copy(xml_path, self.output_xml_folder)img = cv2.imread(img_path)height, width, channel = img.shapetree = etree.parse(xml_path)root = tree.getroot()labels = []bboxes = []for obj in root.findall('object'):label = obj.find('name').textxmin = int(obj.find('bndbox/xmin').text)ymin = int(obj.find('bndbox/ymin').text)xmax = int(obj.find('bndbox/xmax').text)ymax = int(obj.find('bndbox/ymax').text)labels.append(label)bboxes.append([xmin, ymin, xmax, ymax])rotated_img, rotated_bboxes = self.rotate_img_bbox(img, bboxes, self.rotation_angle, self.scale)output_img_path = os.path.join(self.output_img_folder, img_file)cv2.imwrite(output_img_path, rotated_img)output_xml_path = os.path.join(self.output_xml_folder, xml_file)self.save_xml(xml_file, self.output_xml_folder, ('folder_name', img_file), height, width, channel, (labels, rotated_bboxes))else:print(f"XML 文件不存在: {xml_path}")if __name__ == "__main__":input_img_folder = r"E:\data\x1" # 输入图片文件夹input_xml_folder = r"E:\data\x2" # 输入 XML 文件夹output_img_folder = r"E:\data\y1" # 输出图片文件夹output_xml_folder = r"E:\data\y2" # 输出 XML 文件夹rotation_angle = 5 # 旋转角度scale = 1 #图片尺寸,默认为1processor = ImageXMLProcessor(input_img_folder, input_xml_folder, output_img_folder, output_xml_folder, rotation_angle, scale)processor.process()
相关文章:
目标检测数据集图片及标签同步旋转角度
前言 在深度学习领域,尤其是目标检测任务中,数据集的质量直接影响模型的性能。为了提升模型的鲁棒性和对各种场景的适应能力,数据增强技术被广泛应用于图像数据集处理。旋转角度是常见的数据增强方法,通过对图像及其对应的标签&am…...
2025前端面试热门题目——计算机网络篇
计算机网络篇——面试 1. 到底什么是 TCP 连接? TCP 连接的定义 TCP(传输控制协议)是一个面向连接的传输层协议。TCP 连接是通过 三次握手 确立的可靠数据通信链路,保证了在不可靠网络(如互联网)上的数据传输的准确…...
LEAST-TO-MOST PROMPTING ENABLES COMPLEX REASONING IN LARGE LANGUAGE MODELS---正文
题目 最少到最多的提示使大型语言模型能够进行复杂的推理 论文地址:https://arxiv.org/abs/2205.10625 摘要 思路链提示在各种自然语言推理任务中表现出色。然而,它在需要解决比提示中显示的示例更难的问题的任务上表现不佳。为了克服这种由易到难的概括…...
Java开发经验——日志治理经验
摘要 本文主要介绍了Java开发中的日志治理经验,包括系统异常日志、接口摘要日志、详细日志和业务摘要日志的定义和目的,以及错误码规范和异常处理规范。强调了日志治理的重要性和如何通过规范化错误码和日志格式来提高系统可观测性和问题排查效率。 1. …...
使用复数类在C#中轻松绘制曼德布洛集分形
示例在 C# 中绘制曼德布洛特集分形解释了如何通过迭代以下方程来绘制曼德布洛特集: 其中 Z(n) 和 C 是复数。程序迭代此方程,直到 Z(n) 的大小至少为 2 或程序执行最大迭代次数。 该示例在单独的变量中跟踪数字的实部和虚部。此示例使用Complex类来更轻松…...
VSCode 启用免费 Copilot
升级VSCode到 1.96版本,就可以使用每个月2000次免费额度了,按照工作日每天近80次免费额度,满足基本需求。前两天一直比较繁忙,今天周六有时间正好体验一下。 引导插件安装GitHub Copilot - Visual Studio Marketplace Extension f…...
常见问题整理
DevOps 和 CICD DevOps 全称Development & Operation 一种实现开发和运维一体化的协同模式,提供快速交付应用和服务的能力 用于协作:开发,部署,质量测试 整体生命周期工作内容,最终实现持续继承,持续部…...
使用Vue创建前后端分离项目的过程(前端部分)
前端使用Vue.js作为前端开发框架,使用Vue CLI3脚手架搭建项目,使用axios作为HTTP库与后端API交互,使用Vue-router实现前端路由的定义、跳转以及参数的传递等,使用vuex进行数据状态管理,后端使用Node.jsexpress…...
【Springboot知识】Redis基础-springboot集成redis相关配置
文章目录 1. 添加依赖2. 配置Redis连接3. 配置RedisTemplate(可选)4. 使用RedisTemplate或StringRedisTemplate5. 测试和验证 集群配置在application.properties中配置在application.yml中配置 主从配置1. 配置Redis服务器使用配置文件使用命令行 2. 配置…...
网络安全概论——身份认证
一、身份证明 身份证明可分为以下两大类 身份验证——“你是否是你所声称的你?”身份识别——“我是否知道你是谁?” 身份证明系统设计的三要素: 安全设备的系统强度用户的可接受性系统的成本 实现身份证明的基本途径 所知:个…...
OpenHarmony-4.HDI 框架
HDI 框架 1.HDI介绍 HDI(Hardware Device Interface,硬件设备接口)是HDF驱动框架为开发者提供的硬件规范化描述性接口,位于基础系统服务层和设备驱动层之间,是连通驱动程序和系统服务进行数据流通的桥梁,是…...
leecode494.目标和
这道题目第一眼感觉就不像是动态规划,可以看出来是回溯问题,但是暴力回溯超时,想要用动态规划得进行一点数学转换 class Solution { public:int findTargetSumWays(vector<int>& nums, int target) {int nnums.size(),bagWeight0,s…...
在Spring中application 的配置属性(详细)
application 的配置属性。 这些属性是否生效取决于对应的组件是否声明为 Spring 应用程序上下文里的 Bean (基本是自动配置 的),为一个不生效的组件设置属性是没有用的。 multipart multipart.enabled 开启上传支持(默认&a…...
jvm符号引用和直接引用
在解析阶段中,符号引用和直接引用是Java类加载和内存管理中的重要概念,它们之间存在显著的区别。以下是对这两个概念的详细解析: 一、定义与特性 符号引用(Symbolic Reference) 定义:符号引用是编译器生成的用于表示类、方法、字段等的引用方式。特性: 独立性:符号引用…...
一文流:JVM精讲(多图提醒⚠️)
一文流系列是作者苦于技术知识学了-忘了,背了-忘了的苦恼,决心把技术知识的要点一笔笔✍️出来,一图图画出来,一句句讲出来,以求刻在🧠里。 该系列文章会把核心要点提炼出来,以求掌握精髓,至于其他细节,写在文章里,留待后续回忆。 目前进度请查看: :::info https:/…...
python 分段拟合笔记
效果图: 源代码: import numpy as np import cv2 import matplotlib.pyplot as plt from numpy.polynomial.polynomial import Polynomialdef nihe(x_points,y_points,p_id):# 按照 p_id 将 points 分成两组group_0_x = []group_0_y = []group_1_x = []group_1_y = []for i, …...
Mysql索引类型总结
按照数据结构维度划分: BTree 索引:MySQL 里默认和最常用的索引类型。只有叶子节点存储 value,非叶子节点只有指针和 key。存储引擎 MyISAM 和 InnoDB 实现 BTree 索引都是使用 BTree,但二者实现方式不一样(前面已经介…...
数据结构——队列的模拟实现
大家好,上一篇博客我带领大家进行了数据结构当中的栈的模拟实现 今天我将带领大家实现一个新的数据结构————队列 一:队列简介 首先来认识一下队列: 队列就像我们上学时的排队一样,有一个队头也有一个队尾。 有人入队的话就…...
在window环境下安装openssl生成钥私、证书和签名,nodejs利用express实现ssl的https访问和测试
在生成我们自己的 SSL 证书之前,让我们创建一个简单的 Express应用程序。 要创建一个新的 Express 项目,让我们创建一个名为node-ssl -server 的目录,用终端cmd中进入node-ssl-server目录。 cd node-ssl-server 然后初始化一个新的 npm 项目…...
Redis 最佳实践
这是以前写下来的文章,发出来备份一下 Redis 在企业中的最佳实践可以帮助提高性能、可用性和数据管理效率。以下是一些推荐的做法: 选择合适的数据结构: 根据需求选择适当的 Redis 数据结构(如 Strings、Lists、Sets、Hashes、So…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
