【python】YOLO目标检测txt标签转xml,支持与原xml标签融合

需求:

  1. 利用现有yolo模型权重实现自动标注功能,即使用yolov5源码中的detect.py对待标注图像执行推理任务,并使用--save-txt保存标签文件,然后使用本文python代码直接将detect.py生成的txt标签转为xml标签,最后再使用labelImg标注工具进行人工校正。
  2. 现有存在已标注的数据集需要拓展新的类,例如一个数据集已经标注好了人头部,现在需要把人的手部标注上。可以使用之前已存在的手部yolo目标检测模型权重,detect.py对待标注图像执行推理任务,并使用–save-txt保存标签文件。然后使用本文python代码直接将detect.py生成的txt标签提取出来并融合到原有的xml文件中,从而起动自动标注新增一个或多个目标类的作用。

代码:

txt2xml.py


# -*- coding: utf-8 -*-
# @Time : 2023/10/08 15:49
# @Author : CLW
# @FileName: txt2xml.py
# @Software: PyCharm'''
脚本功能:1. 可以为原图提供自动标注的功能。例如:需要标注10000+的数据,可以人工标注1000+,然后训练yolo模型,再用detect.py用模型对剩下来的原图进行推理,推理中保存txt格式的推理结果。然后可以采用本代码,对剩下来的原图进行自动标注,再人工调整。[参数]org_imgs_path:需要自动标注的图片路径txts_path:输入推理中保存txt文件classes: yolo模型目标检测的类别output_xmls_path:输出的xml文件路径valid_class: 在推理输出的txt文件中,提取指定类别的信息,不再其中的检测框信息会被忽略。(若valid_class置为空,程序默认提取全部类别)2. 通过模型自动标注,可以为已有标签补充其他类别例如:原来的数据集的xml标签文件有[a,b,c]3个类,但是实际需求还需要增加一个d类。然后我们采用另一个检测模型,可以检测[d,e,f]类。使用yolo项目中的detect.py用模型对需要补充类别的数据集原图进行推理,推理中保存txt格式的推理结果。然后可以采用本代码,对txt标签文件中提取出[d]类的检测框信息,然后添加到原来数据集的xml标签中。[参数]org_imgs_path: 需要自动标注的图片路径txts_path: 输入推理中保存txt文件,即案例中用另一个模型推理出包含[d,e,f]3个类的txt结果文件output_xmls_path: 输出的xml文件路径org_xmls_path: 原始的xml标签文件,即案例中只有[a,b,c]3个类的xml标签文件valid_class: 在推理输出的txt文件中,提取指定类别的信息,即案例中的[d]类,而e,f两类的检测框信息会被忽略。(若valid_class置为空,程序默认提取全部类别)'''import os
from xml.dom.minidom import Document
import cv2
import xml.etree.ElementTree as ET
from PIL import Image
import numpy as np'''
####################    输入参数设置(开始)    #################### 
'''# yolo格式txt标签文件相对路径
txts_path = r'D:\dataset\labels'# 存放图片的相对路径
org_imgs_path = r'D:\dataset\pretrain_GW'# 原图片对应的xml路径(当没有原始xml标签时,可以为kong),算法执行后会保留
org_xmls_path = r'D:\dataset\pretrain_GW'# 输出目录转化为voc格式xml标签文件存储的相对路径
output_xmls_path = r'D:\dataset\pretrain_GW'# txt转xml时,yolo模型对应的标签文件
classes = ["hand", "gloves"]
# 当用txt标签文件给已存在的xml标签进行一个检测类别补充时,可以对类别进行一个筛选
valid_class = ["hand", "gloves"]# 图像类别
jpg_types = ["jpg", "png"]'''
####################    输入参数设置(结束)    #################### 
'''
'''<object>        <name>{}</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>{}</xmin><ymin>{}</ymin><xmax>{}</xmax><ymax>{}</ymax></bndbox></object>
'''class YOLO2VOCConvert:def __init__(self, txts_path, out_xmls_path,org_xml_path, imgs_path, classes) :self.txts_path = txts_path   # 标注的yolo格式标签文件路径self.xmls_path = out_xmls_path   # 转化为voc格式标签之后保存路径self.org_xmls_path = org_xml_path   # 原来自带的xml路径self.imgs_path = imgs_path   # 读取读片的路径各图片名字,存储到xml标签文件中self.classes = classes# 从所有的txt文件中提取出所有的类别, yolo格式的标签格式类别为数字 0,1,...# writer为True时,把提取的类别保存到'./Annotations/classes.txt'文件中def search_all_classes(self, writer=False):# 读取每一个txt标签文件,取出每个目标的标注信息all_names = set()txts = os.listdir(self.txts_path)# 使用列表生成式过滤出只有后缀名为txt的标签文件txts = [txt for txt in txts if txt.split('.')[-1] == 'txt']# print(len(txts), txts)# 11 ['0002030.txt', '0002031.txt', ... '0002039.txt', '0002040.txt']for txt in txts:txt_file = os.path.join(self.txts_path, txt)with open(txt_file, 'r') as f:objects = f.readlines()for object in objects:object = object.strip().split(' ')print(object)  # ['2', '0.506667', '0.553333', '0.490667', '0.658667']all_names.add(int(object[0]))# print(objects)  # ['2 0.506667 0.553333 0.490667 0.658667\n', '0 0.496000 0.285333 0.133333 0.096000\n', '8 0.501333 0.412000 0.074667 0.237333\n']print("所有的类别标签:", all_names, "共标注数据集:%d张" % len(txts))return list(all_names)def find_nodes(self,tree, path):return tree.findall(path)def yolo2voc(self):# 创建一个保存xml标签文件的文件夹if not os.path.exists(self.xmls_path):os.mkdir(self.xmls_path)# 把上面的两个循环改写成为一个循环:# imgs = os.listdir(self.imgs_path)txts = os.listdir(self.txts_path)txts = [txt for txt in txts if not txt.split('.')[0] == "classes"]  # 过滤掉classes.txt文件print(txts)for root, dir, files in os.walk(self.txts_path):for file in files:if file.endswith('txt'):img_path = ''for jpg_type in jpg_types:img_name = file[:-3]+jpg_typeimg_path = os.path.join(self.imgs_path, img_name)if not os.path.exists(img_path):continueelse:breakif not os.path.exists(img_path):continue# print("读取图片:", img_path)image = Image.open(img_path)img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)height_img, width_img, depth_img = img.shape# 获取标注文件txt中的标注信息all_objects = []txt_file = os.path.join(root, file)# print('txt_file:', txt_file)with open(txt_file, 'r') as f:objects = f.readlines()for object in objects:object = object.strip().split(' ')all_objects.append(object)self.xml_name = file[:-3]+'xml'self.txt2xml(all_objects, height_img, width_img, depth_img, file)def add_node_by_target_classes(self, nodelist, tree_root):print("len(nodelist)=",len(nodelist))for parent_node in nodelist:children = parent_node.getchildren()# print("children[0].text=",children[0].text)if (parent_node.tag == "object"):# print(children[0].text.lower())tree_root.append(parent_node)def txtAddXml(self, xmlBuilder):new_tree = ET.parse(os.path.join(self.org_xmls_path, self.xml_name))new_root = new_tree.getroot()root_element = ET.fromstring(xmlBuilder.toxml())tree = ET.ElementTree(root_element)old_root = tree.getroot()# new_root = new_tree.getroot()# ET.dump(old_root)# get parent nodesadd_parent_nodes = self.find_nodes(old_root, "./")print(add_parent_nodes)# ET.dump(add_parent_nodes)self.add_node_by_target_classes(add_parent_nodes, new_root)new_tree.write(os.path.join(self.xmls_path, self.xml_name), encoding="utf-8", xml_declaration=True)def txt2xml(self, all_objects, height_img, width_img, depth_img, txt_name):# 创建xml标签文件中的标签xmlBuilder = Document()# 创建annotation标签,也是根标签annotation = xmlBuilder.createElement("annotation")# 给标签annotation添加一个子标签xmlBuilder.appendChild(annotation)# 创建子标签folderfolder = xmlBuilder.createElement("folder")# 给子标签folder中存入内容,folder标签中的内容是存放图片的文件夹,例如:JPEGImagesfolderContent = xmlBuilder.createTextNode(self.imgs_path.split('/')[-1])  # 标签内存folder.appendChild(folderContent)  # 把内容存入标签annotation.appendChild(folder)  # 把存好内容的folder标签放到 annotation根标签下# 创建子标签filenamefilename = xmlBuilder.createElement("filename")# 给子标签filename中存入内容,filename标签中的内容是图片的名字,例如:000250.jpgfilenameContent = xmlBuilder.createTextNode((txt_name.split('.')[0] + '.jpg').encode('utf-8').decode())  # 标签内容filename.appendChild(filenameContent)annotation.appendChild(filename)# 把图片的shape存入xml标签中size = xmlBuilder.createElement("size")# 给size标签创建子标签widthwidth = xmlBuilder.createElement("width")  # size子标签widthwidthContent = xmlBuilder.createTextNode(str(width_img))width.appendChild(widthContent)size.appendChild(width)  # 把width添加为size的子标签# 给size标签创建子标签heightheight = xmlBuilder.createElement("height")  # size子标签heightheightContent = xmlBuilder.createTextNode(str(height_img))  # xml标签中存入的内容都是字符串height.appendChild(heightContent)size.appendChild(height)  # 把width添加为size的子标签# 给size标签创建子标签depthdepth = xmlBuilder.createElement("depth")  # size子标签widthdepthContent = xmlBuilder.createTextNode(str(depth_img))depth.appendChild(depthContent)size.appendChild(depth)  # 把width添加为size的子标签annotation.appendChild(size)  # 把size添加为annotation的子标签# 每一个object中存储的都是['2', '0.506667', '0.553333', '0.490667', '0.658667']一个标注目标for object_info in all_objects:# 先进行类别判断,筛选出有效类别if len(valid_class) > 0:cls_name = self.classes[int(object_info[0])]if cls_name not in valid_class:continue# else:#     print(cls_name)# 开始创建标注目标的label信息的标签object = xmlBuilder.createElement("object")  # 创建object标签# 创建label类别标签# 创建name标签imgName = xmlBuilder.createElement("name")  # 创建name标签imgNameContent = xmlBuilder.createTextNode(self.classes[int(object_info[0])])imgName.appendChild(imgNameContent)object.appendChild(imgName)  # 把name添加为object的子标签# 创建pose标签pose = xmlBuilder.createElement("pose")poseContent = xmlBuilder.createTextNode("Unspecified")pose.appendChild(poseContent)object.appendChild(pose)  # 把pose添加为object的标签# 创建truncated标签truncated = xmlBuilder.createElement("truncated")truncatedContent = xmlBuilder.createTextNode("0")truncated.appendChild(truncatedContent)object.appendChild(truncated)# 创建difficult标签difficult = xmlBuilder.createElement("difficult")difficultContent = xmlBuilder.createTextNode("0")difficult.appendChild(difficultContent)object.appendChild(difficult)# 先转换一下坐标# (objx_center, objy_center, obj_width, obj_height)->(xmin,ymin, xmax,ymax)x_center = float(object_info[1]) * width_img + 1y_center = float(object_info[2]) * height_img + 1xminVal = int(x_center - 0.5 * float(object_info[3]) * width_img)  # object_info列表中的元素都是字符串类型yminVal = int(y_center - 0.5 * float(object_info[4]) * height_img)xmaxVal = int(x_center + 0.5 * float(object_info[3]) * width_img)ymaxVal = int(y_center + 0.5 * float(object_info[4]) * height_img)# 创建bndbox标签(三级标签)bndbox = xmlBuilder.createElement("bndbox")# 在bndbox标签下再创建四个子标签(xmin,ymin, xmax,ymax) 即标注物体的坐标和宽高信息# 在voc格式中,标注信息:左上角坐标(xmin, ymin) (xmax, ymax)右下角坐标# 1、创建xmin标签xmin = xmlBuilder.createElement("xmin")  # 创建xmin标签(四级标签)xminContent = xmlBuilder.createTextNode(str(xminVal))xmin.appendChild(xminContent)bndbox.appendChild(xmin)# 2、创建ymin标签ymin = xmlBuilder.createElement("ymin")  # 创建ymin标签(四级标签)yminContent = xmlBuilder.createTextNode(str(yminVal))ymin.appendChild(yminContent)bndbox.appendChild(ymin)# 3、创建xmax标签xmax = xmlBuilder.createElement("xmax")  # 创建xmax标签(四级标签)xmaxContent = xmlBuilder.createTextNode(str(xmaxVal))xmax.appendChild(xmaxContent)bndbox.appendChild(xmax)# 4、创建ymax标签ymax = xmlBuilder.createElement("ymax")  # 创建ymax标签(四级标签)ymaxContent = xmlBuilder.createTextNode(str(ymaxVal))ymax.appendChild(ymaxContent)bndbox.appendChild(ymax)object.appendChild(bndbox)annotation.appendChild(object)  # 把object添加为annotation的子标签if os.path.exists(os.path.join(self.org_xmls_path, self.xml_name)):# print(xmlBuilder)self.txtAddXml(xmlBuilder)else:f = open(os.path.join(self.xmls_path, txt_name[:-3] + 'xml'), 'w')print(os.path.join(self.xmls_path, txt_name[:-3] + 'xml'))xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')f.close()if __name__ == '__main__':yolo2voc_obj1 = YOLO2VOCConvert(txts_path, output_xmls_path, org_xmls_path, org_imgs_path,classes)labels = yolo2voc_obj1.search_all_classes()print('labels: ', labels)yolo2voc_obj1.yolo2voc()

参数说明:

txts_path:YOLO源码的detect.py推理生成的txt标签目录。
org_imgs_path:存放图片的目录。
org_xmls_path:图片原有的xml标签。如果原本没有xml标签可以置为空,如果原本存在xml标签,则本次txt转换的xml标签信息可以与原有的xml标签信息进行融合。
output_xmls_path:输出转换后的xml标签路径,如果与org_xmls_path路径一致,则可以直接覆盖原有xml标签。
classes:使用的yolo检测模型的类别信息,与该权重模型的训练设置一致,顺序不可颠倒,不可缺失。
valid_class:本次需要转换的标类别名称。如果classes中的类别不在valid_class中,则会被筛掉,不会进行转换操作。
jpg_types:支持的图片类型,可自增,能读入即可。

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

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

相关文章

SAP PP学习笔记14 - MTS(Make-to-Stock) 按库存生产(策略10),以及生产计划的概要

上面讲了SAP里面的基础知识&#xff0c;BOM&#xff0c;作业手顺&#xff08;工艺路线&#xff09;&#xff0c;作业区&#xff08;工作中心&#xff09;&#xff0c;MRP&#xff0c;MPS等概念&#xff0c;现在该到用的时候了。 SAP PP学习笔记07 - 简单BOM&#xff0c;派生BO…

5门编程语言哪个好一点:深入剖析与选择指南

5门编程语言哪个好一点&#xff1a;深入剖析与选择指南 在编程的广阔领域中&#xff0c;各种语言如繁星般璀璨&#xff0c;各有其独特之处。对于初学者或寻求转型的开发者来说&#xff0c;如何选择一门合适的编程语言往往令人头疼。本文将针对五门热门编程语言进行详细剖析&am…

STC8增强型单片机进阶开发--独立按键

知不足而奋进 望远山而前行 文章目录 目录 文章目录 前言 目标 内容 原理图 按键消抖 软件设计 要求 分析 实现单个按钮 实现多个按钮 使用位操作存储状态 总结 前言 本次学习旨在探索按键操作及按键消抖的原理和实现方法。通过学习原理图、按键消抖的三种方法以及软件设计的要…

NXP RT1060学习总结 - 基础CAN功能

1、RT1060-CAN功能简介 这里使用的是RT1060系列的1064芯片进行开发&#xff0c;使用的是官方提供的开发板&#xff1b;提供的CAN外设为CAN2&#xff0c;使用CAN2的好处是IO与CAN3可以互相映射&#xff0c;而CAN3是具备CAN-FD功能。 2、CAN IO初始化 static void can2_gpio_c…

什么是Java泛型?它有什么作用

Java泛型&#xff08;Generics&#xff09;是一种允许在定义类、接口和方法时使用类型参数的机制。泛型提供了一种机制&#xff0c;使得代码可以对多种类型的对象进行操作&#xff0c;而无需进行类型转换。 Java泛型的作用 类型安全&#xff1a;通过在编译时进行类型检查&…

如何选择D类音频放大器(数字功率放大器)

1 简介 多年来&#xff0c;音频内容一直在不断发展。从当地唱片店购买 12 英寸 LP 黑胶唱片的时代已经成为过去&#xff0c;现在我们通过流式传输几乎可即时播放云端的任何内容。虽然一些音频爱好者会为了获得新奇体验而重拾黑胶唱片&#xff0c;但今天绝大多数的音频都是以数…

JVM学习笔记(持续更新)

JDK、JRE、JVM区别&#xff1f; 类加载过程 装载 验证 准备 解析 初始化 类加载器分类 双亲委派模型 如何打破双亲委派模型&#xff1f; 自定义类加载器&#xff0c;集成ClassLoader类重写loadClass,如Tomcat JVM内存模型 JVM 需要使用计算机的内存&#xff0c;Java 程序…

【LeetCode 101】对称二叉树

1. 题目 2. 分析 这道题比较经典。我又一次做错了&#xff0c;这次是花了20min都没有做出来。 最开始我的思想就是&#xff0c;递归比较左根节点的左子树和右根节点的右子树是否对称即可&#xff0c;然后觉得能解决问题了&#xff0c;便动手coding。哪知道&#xff0c;又碰到了…

电源滤波器怎么选用

电源滤波器怎么选用 滤波器应用场景及作用第一步&#xff1a;第二步&#xff1a;第三步&#xff1a;第四步&#xff1a; 滤波器应用场景及作用 可以有效解决EMC测试无法通过、端口防护、滤除干扰、设备保护等问题 主要功能有: 1、降低主电源谐波; 2、保护驱动装置电力电子元件…

算法人生(18):从神经网络的“剪枝策略”看“怎么找回时间”

IT人的工作和生活难平衡这事&#xff0c;到底要怎么解决呢&#xff0c;让我们从神经网络的“剪枝策略”中找点灵感吧&#xff01; 剪枝策略是指训练和优化深度神经网络时采取的一种技术&#xff0c;从名字就知道&#xff0c;它就像修剪树木一样&#xff0c;去除不必要的枝叶&a…

枣庄高防服务器如何确保数据的安全保护?

如何利用枣庄高防服务器确保数据的安全保护&#xff1f; 在当今信息时代&#xff0c;数据安全已经成为企业和个人都需要面对的重要问题。为了保障数据的安全&#xff0c;许多企业选择枣庄高防服务器&#xff0c;其强大的安全防护能力为用户提供了可靠的保障。而为了最大程度地…

stack和queue(2): 模拟实现

一、stack的模拟实现 stack是一个容器适配器&#xff0c;它的底层是通过对某种容器类进行封装来实现&#xff0c;标准容器list和vector&#xff0c;deque都符合这些需求&#xff0c;默认情况下&#xff0c;如果没有为stack指定底层容器就默认是使用deque实现。 我们在模拟实现…

Vuex 是什么?VueX简介

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介Vuex 是什么核心概念1.State&#xff08;状态&#xff09;2. Getter&#xff08;获取器&#xff09;3. Mutation&#xff08;突变&#xff09;4. Action&#xff08;动作&#xff09;5. Module&#xff08;模块&#xff09; 原理解…

使用STS临时访问凭证通过客户端直连OSS对象存储服务器

目录 1、导论 2、客户端直传 3、创建RAM用户以及RAM角色 4、如何实现客户端直传 4.1、跨域访问 4.2、安全授权 5、代码示例 5.1、后端代码实例 5.2、客户端代码实例 1、导论 最近在做项目的过程中使用到了阿里云OSS来存储客户端上传的文件&#xff0c;方法是直接将客…

Keras深度学习框架实战(3):EfficientNet实现stanford dog分类

1、通过EfficientNet进行微调以实现图像分类概述 通过EfficientNet进行微调以实现图像分类&#xff0c;是一个使用EfficientNet作为预训练模型&#xff0c;并通过微调&#xff08;fine-tuning&#xff09;来适应特定图像分类任务的过程。一下是对相关重要术语的解释。 Effici…

Flutter-自定义可展开文本控件

Flutter 在移动开发中&#xff0c;常常需要处理一些长文本显示的场景&#xff0c;如何优雅地展示这些文本并允许用户展开和收起是一个常见的需求。在本文中&#xff0c;我将分享如何使用Flutter实现一个可展开和收起的文本控件。 效果 我们将实现一个可展开和收起的文本控件…

SpringEvent事件发布订阅Demo

文章目录 实现事件定义事件监听器方式一方式二 定义事件发布者方式一方式二 项目测试原理 本文参考&#xff1a; 基础用法&#xff1a;Spring Event事件发布&消费Demo - HumorChen99 - 博客园 (cnblogs.com) 比较全面的用法&#xff1a;Spring Event 事件发布/监听机制 详…

yolov10模块

yolov10模块 1 C2f2 C2fCIB2.1 CIB2.2 RepVGGDW 3 PSA4 SCDown5 v10Detect 论文代码&#xff1a;https://github.com/THU-MIG/yolov10 论文链接&#xff1a;https://arxiv.org/abs/2405.14458 Conv是Conv2dBNSiLU PW是Pointwise Convolution(逐点卷积) DW是Depthwise Convolut…

SpringMvc的工作流程是怎样的

Spring MVC 的工作流程涉及多个组件&#xff0c;它们协同工作来处理HTTP请求并生成响应。下面是一步步说明 Spring MVC 如何处理一个 HTTP 请求&#xff1a; DispatcherServlet 接收请求&#xff1a; 用户发送 HTTP 请求到服务器&#xff0c;请求被前端控制器 DispatcherServle…

python API自动化(Requests库应用)

1.接口自动化的逻辑 接口测试自动化&#xff0c;简单来讲就是功能测试用例脚本化然后执行脚本&#xff0c;产生一份可视化测试报告。不管什么样的测试方式&#xff0c;都是为了验证功能与发现 BUG。那为什么要做接口测试自动化呢&#xff1f;一句话概括就是为了节省人力成本 …