Voc标签文件转Yolo标签文件程序

news/2024/7/11 0:12:17 标签: YOLO, 人工智能, 深度学习, python, 计算机视觉

为了方便,我将代码封装成了桌面程序,GUI部分我就不介绍了,泛泛而谈到时啥都没学会。

一、yolo标签格式

我们看一下yolo标签的格式:

<class index> <x center> <y center> <width> <height> \text{<class index> <x center> <y center> <width> <height>} <class index> <x center> <y center> <width> <height>

上述格式【class_index】是某类别对应的标签索引,为整型;后面4个参数均为比例,而不是实际参数,我们举个例子

1 0.759623 0.749454 0.079443 0.081878

我假设“汽车”这个类别对应的标签索引为1。那么上述例子表示我们标了一个框,这个框的类别索引为1,表示为类别“汽车”。

前两个浮点参数表示这个框的中心坐标分别相对于整个图像宽度和高度的比例,用比例来表示中心点的位置;后两者表示框的宽度和高度分别相对于整个图像的比例,用比例来表达框的大小。

二、载入XML文件

首先我们使用python自带模块【os】来将指定目录(XML文件所在目录)【self.source_path】的所有文件名载入到文件名列表【Files】中。

然后我们使用for训练,遍历文件列表的每一个文件名,如果文件名的扩展名是【.XML】,则表示是标签文件,此时调用Process进行转换

python">def LoadVocTag(self):
        Files = os.listdir(self.source_path)
        # 处理每一个xml文件
        for File in Files:
            if File.endswith('.xml'):
                self.Process(File)

三、获取类别标签对应类别索引字典

代码中【self.classes_file】表示含有对应关系的txt文件路径,现在我们要把它载入,做成字典。我们看一个类别标签和类别索引的对应关系的txt文件内容:

纸类 1
塑料类 2
金属类 3
衣品类 4
其他 5

上述内容,类别标签和类别索引之间用空格隔开了,因此我们打开文件后,逐行读取内容,然后使用空格进行分割成两段。前段是类别标签,后段时类别索引,类别索引读出来是文本,我们转换成整型后添加到字典就好了。

我相信现在看下面的代码就一目了然了:

python">def GetTag(self):
        # 类别字典
        class_dict = {}
        # 打开含有号码和类别对应关系的txt文件
        with open(self.classes_file, 'r', encoding='utf-8') as CF:
            # 获取txt文件的每一行信息
            for line in CF:
                parts = line.strip().split(' ')
                # 每行的第一部分是类别的中文描述,第二部分是相应的数字
                class_dict[parts[0]] = int(parts[1])
        # 返回类别字典
        return class_dict

四、格式转换

这一部分需要解析XML文件,解析XML文件,我呢使用轻量级的【xml.etree.ElementTree】模块,在小文件中,这个模块用来解析是非常方便的。

先获取对应关系的字典,便于后面解析文件得到类别后转换成类别索引:

python"># 获取类别字典,方便后续得到类别对应的号码
Class_Dict = self.GetTag()

for循环用tree.iterfind(‘object’)自动解析XML文件,循环查找 <object></object> \text{<object></object>} <object></object>标记,因为一个object表示我们锚的一个框。

python"># 查找object标签,每一个object标签,代表一个框
for object in tree.iterfind('object'):

VOC标签文档中object标记下第一行是 <name></name> \text{<name></name>} <name></name>标记,表示类别名字,也就是object[0]表示 <name></name> \text{<name></name>} <name></name>标记,其属性text就是标记的内容,该内容就是类别名字。

python"># 获取当前框所对应的类别名字
name = object[0].text

从字典中得到类别索引:

python"># 从类别字典中获取该类别对应的号码
tag = Class_Dict[name]

接下来就是计算转换部分了,下面代码中,【self.image_width】和【self.image_height】是图片尺寸,这个是需要输入的,其他的部分计算原理来自yolo标签的原理,可见第一部分的讲解

python"># 获取边框盒,该标记包含了目标框的4个角的坐标
box = object.find('bndbox')
# 获取4个角坐标(相对于图像左上角)
xmin = float(box.find('xmin').text)
ymin = float(box.find('ymin').text)
xmax = float(box.find('xmax').text)
ymax = float(box.find('ymax').text)

# 计算中心点坐标
center_x = (xmin + xmax) / 2
center_y = (ymin + ymax) / 2
# 计算宽度和高度
width = xmax - xmin
height = ymax - ymin
# 图像的宽度和高度(实际应根据图像尺寸来调整)

# 归一化为相对于图像宽度和高度的比例,YOLO标签以比例表示
normalized_center_x = center_x / self.image_width
normalized_center_y = center_y / self.image_height
normalized_width = width / self.image_width
normalized_height = height / self.image_height

接着我们将计算得到的结果严格按照yolo标签的格式组合在一起,每个参数之间用空格隔开,每个比例保留6位小数。content是一个列表,存储一张图片中包含的所有锚框标签信息。

python"># 格式化将写入txt文件的标签信息
info = f'{tag} {normalized_center_x:.6f} {normalized_center_y:.6f} {normalized_width:.6f} {normalized_height:.6f}'
# 给当前txt文件添加一行,表示处理完一个框
content.append(info)

五、保存txt标签文件

这部分需要将content列表的内容,逐个写入,每个占一行

python">def SaveToYoloTag(self, FileName, content):
        name = os.path.join(self.object_path, FileName.split('.')[0] + '.txt')
        with open(name, 'w') as yolo:
            # 按行写入
            for item in content:
                yolo.write(f'{item}\n')

        # 在滚动窗口中显示输出信息
        showname=name.split("\\")[-1]
        show=f'{showname} has been saved'
        self.output_textedit.append(show)

六、完整程序

python">import os
import xml.etree.ElementTree as ET
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QLineEdit, QTextEdit,QDesktopWidget

class YoloConverterApp(QWidget):
    def __init__(self):
        super().__init__()

        self.source_path = None
        self.object_path = None
        self.classes_file = None
        self.image_width = None
        self.image_height = None

        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        self.source_label = QLabel('VOC标签文件路径:')
        layout.addWidget(self.source_label)

        self.object_label = QLabel('YOLO标签文件路径:')
        layout.addWidget(self.object_label)

        self.classes_label = QLabel('类别指示文件路径:')
        layout.addWidget(self.classes_label)

        self.width_label = QLabel('图像宽度:')
        layout.addWidget(self.width_label)
        self.width_input = QLineEdit()
        layout.addWidget(self.width_input)

        self.height_label = QLabel('图像高度:')
        layout.addWidget(self.height_label)
        self.height_input = QLineEdit()
        layout.addWidget(self.height_input)

        source_button = QPushButton('选择VOC标签文件路径')
        source_button.clicked.connect(self.choose_source_path)
        layout.addWidget(source_button)

        object_button = QPushButton('选择YOLO标签文件路径')
        object_button.clicked.connect(self.choose_object_path)
        layout.addWidget(object_button)

        classes_button = QPushButton('选择类别指示文件路径')
        classes_button.clicked.connect(self.choose_classes_file)
        layout.addWidget(classes_button)

        convert_button = QPushButton('转换')
        convert_button.clicked.connect(self.convert)
        layout.addWidget(convert_button)

        # 添加滚动窗口用于显示输出信息
        self.output_textedit = QTextEdit()
        self.output_textedit.setReadOnly(True)
        layout.addWidget(self.output_textedit)

        self.setLayout(layout)
        self.setWindowTitle('Yolo Converter')

        # 获取桌面尺寸
        desktop = QDesktopWidget()
        screen_rect = desktop.screenGeometry()
        width = screen_rect.width() * 0.5
        height = screen_rect.height() * 0.5

        # 设置窗口大小为桌面大小的50%
        self.setGeometry(100, 100, int(width), int(height))

    def open_file_dialog(self, label):
        # 创建文件对话框的选项
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        # 创建 QFileDialog 实例
        file_dialog = QFileDialog()
        file_dialog.setOptions(options)
        # 将文件模式设置为选择目录
        file_dialog.setFileMode(QFileDialog.Directory)
        # 如果用户选择了目录并点击了确定
        if file_dialog.exec_():
            # 获取所选路径
            selected_path = file_dialog.selectedFiles()[0]
            # 将提供的标签的文本设置为所选路径
            label.setText(selected_path)

    def convert(self):
        self.image_width = int(self.width_input.text())
        self.image_height = int(self.height_input.text())
        self.LoadVocTag()

    def choose_source_path(self):
        self.open_file_dialog(self.source_label)
        self.source_path = self.source_label.text()

    def choose_object_path(self):
        self.open_file_dialog(self.object_label)
        self.object_path = self.object_label.text()

    def choose_classes_file(self):
        # 创建文件对话框的选项
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        # 创建 QFileDialog 实例
        file_dialog = QFileDialog()
        file_dialog.setOptions(options)
        # 如果用户选择了文件并点击了确定
        if file_dialog.exec_():
            # 获取所选文件
            selected_file = file_dialog.selectedFiles()[0]
            # 将类别标签的文本设置为所选文件
            self.classes_label.setText(selected_file)
            # 将类别文件属性设置为所选文件路径
            self.classes_file = selected_file

    def LoadVocTag(self):
        Files = os.listdir(self.source_path)
        # 处理每一个xml文件
        for File in Files:
            if File.endswith('.xml'):
                self.Process(File)

    # 处理单个XML文件
    def Process(self, File):
        # 获取解析树
        tree = ET.parse(os.path.join(self.source_path, File))
        # 获取类别字典,方便后续得到类别对应的号码
        Class_Dict = self.GetTag()
        # 存储要写入txt文件的内容,content的元素个数就是该图片含有的目标框数
        content = []

        # 查找object标签,每一个object标签,代表一个框
        for object in tree.iterfind('object'):
            # 获取当前框所对应的类别名字
            name = object[0].text
            # 从类别字典中获取该类别对应的号码
            tag = Class_Dict[name]
            # 获取边框盒,该标记包含了目标框的4个角的坐标
            box = object.find('bndbox')
            # 获取4个角坐标(相对于图像左上角)
            xmin = float(box.find('xmin').text)
            ymin = float(box.find('ymin').text)
            xmax = float(box.find('xmax').text)
            ymax = float(box.find('ymax').text)

            # 计算中心点坐标
            center_x = (xmin + xmax) / 2
            center_y = (ymin + ymax) / 2
            # 计算宽度和高度
            width = xmax - xmin
            height = ymax - ymin
            # 图像的宽度和高度(实际应根据图像尺寸来调整)

            # 归一化为相对于图像宽度和高度的比例,YOLO标签以比例表示
            normalized_center_x = center_x / self.image_width
            normalized_center_y = center_y / self.image_height
            normalized_width = width / self.image_width
            normalized_height = height / self.image_height

            # 格式化将写入txt文件的标签信息
            info = f'{tag} {normalized_center_x:.6f} {normalized_center_y:.6f} {normalized_width:.6f} {normalized_height:.6f}'
            # 给当前txt文件添加一行,表示处理完一个框
            content.append(info)

        # 保存为yolo格式的标签
        self.SaveToYoloTag(File, content)

    # 获取类别标签对应的字典
    def GetTag(self):
        # 类别字典
        class_dict = {}
        # 打开含有号码和类别对应关系的txt文件
        with open(self.classes_file, 'r', encoding='utf-8') as CF:
            # 获取txt文件的每一行信息
            for line in CF:
                parts = line.strip().split(' ')
                # 每行的第一部分是类别的中文描述,第二部分是相应的数字
                class_dict[parts[0]] = int(parts[1])
        # 返回类别字典
        return class_dict

    # 将处理后的标签信息保存为YOLO格式的文件
    def SaveToYoloTag(self, FileName, content):
        name = os.path.join(self.object_path, FileName.split('.')[0] + '.txt')
        with open(name, 'w') as yolo:
            # 按行写入
            for item in content:
                yolo.write(f'{item}\n')

        # 在滚动窗口中显示输出信息
        showname=name.split("\\")[-1]
        show=f'{showname} has been saved'
        self.output_textedit.append(show)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    converter_app = YoloConverterApp()
    converter_app.show()
    sys.exit(app.exec_())

http://www.niftyadmin.cn/n/5336620.html

相关文章

2018年认证杯SPSSPRO杯数学建模C题(第二阶段)机械零件加工过程中的位置识别全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 基于轮廓提取与图像配准的零件定位问题研究 C题 机械零件加工过程中的位置识别 原题再现&#xff1a; 在工业制造自动生产线中&#xff0c;在装夹、包装等工序中需要根据图像处理利用计算机自动智能识别零件位置&#xff0c;并由机械手将零件…

priority_queue的使用与模拟实现(容器适配器+stack与queue的模拟实现源码)

priority_queue的使用与模拟实现 引言&#xff08;容器适配器&#xff09;priority_queue的介绍与使用priority_queue介绍接口使用默认成员函数 size与emptytoppush与pop priority_queue的模拟实现构造函数size与emptytoppush与pop向上调整建堆与向下调整建堆向上调整建堆向下调…

服务注册流程解析

本文主要介绍服务注册的基本流程 起手式 接上面的继续说&#xff0c;服务注册是一门至高无上的武学&#xff0c;招式千变万化 &#xff0c;九曲十八弯打得你找不到北。可正所谓这顺藤摸瓜&#xff0c;瓜不好找&#xff0c;可是这藤长得地方特别显眼。那么今天&#xff0c;就让…

Linux编写简易shell

思路&#xff1a;​ ​ ​ 所以要写一个shell&#xff0c;需要循环以下过程:​ 获取命令行解析命令行建立一个子进程&#xff08;fork&#xff09;替换子进程&#xff08;execvp&#xff09;父进程等待子进程退出&#xff08;wait&#xff09; 实现代码&#xff1a;​ #inc…

内网安全管理系统(保密管理系统)

在当今信息化的时代&#xff0c;企业的内网已经成为其核心资产的重要组成部分。 随着企业的快速发展和信息化程度的提升&#xff0c;内网安全问题日益凸显&#xff0c;如何保障内网的安全和机密信息的保密性&#xff0c;已经成为企业亟待解决的问题。 内网安全管理系统(保密管…

【Github搭建网站】零基础零成本搭建个人Web网站~

Github网站&#xff1a;https://github.com/ 这是我个人搭建的网站&#xff1a;https://xf2001.github.io/xf/ 大家可以搭建完后发评论区看看&#xff01;&#xff01;&#xff01; 搭建教程&#xff1a;https://www.bilibili.com/video/BV1xc41147Vb/?spm_id_from333.999.0.0…

GPT-4开启人工智能赋能教育的新时代

2022年11月30日&#xff0c;美国 OpenAI公司发布了一款最新研发的生成式人工智能产品——ChatGPT&#xff08;Chat Generative Pre-trained Transformer&#xff09;。ChatGPT的推出&#xff0c;仅用了短短两个月时间就快速占领了人工智能领域的主导地位&#xff0c;成为历史上…

微服务接入skywalking

本地代码修改&#xff1a; 1、引入所需jar包&#xff1b; 2、增加修改内容&#xff08;主要是日志&#xff0c;不修改容易出现skywalking里面不显示日志明细&#xff09;&#xff1b; 除此之外还需要修改skywalking agent的配置项。 在agent.config最后面加上&#xff1a; &am…