从VOC格式到YOLO格式:图像标注数据的转换过程

转载:从VOC格式到YOLO格式:图像标注数据的转换过程_voc转yolo-CSDN博客

随着深度学习在目标检测领域的广泛应用,各类数据格式的处理和转换显得尤为重要。其中,PASCAL VOC (Visual Object Classes) 格式和YOLO (You Only Look Once) 格式是两种广泛使用的图像标注格式。本文将详细介绍如何将VOC格式的图像标注数据转换为YOLO格式。

一、VOC格式简介

PASCAL VOC格式是一种流行的目标检测数据集格式,它包含XML文件记录每张图片中目标的位置(矩形框坐标)、类别等信息。每个XML文件对应一张图片,描述了图片中的所有物体实例。

二、YOLO格式简介

YOLO格式则更为简洁直接,它通常将标注信息存储在一个文本文件中,每一行对应一张图片的数据。YOLO标注格式一般包括图片路径、每个目标的中心坐标(相对于整图宽高的比例)、目标的宽度和高度(同样以比例形式表示)以及目标的类别索引。

三、VOC转YOLO格式的具体步骤

1. **读取VOC数据**:首先需要读取VOC格式的XML标注文件,提取出每张图片的文件名、目标类别以及对应的矩形框坐标。

2. **坐标转换**:VOC格式使用的是绝对坐标(像素值),而YOLO格式使用相对坐标(比例)。因此,需要将矩形框的左上角坐标(xmin, ymin)和右下角坐标(xmax, ymax)转换为YOLO格式下的中心点坐标(x, y)和宽高(w, h),它们均是以图像宽度和高度的比例表示。

3. **格式化输出**:按照YOLO的格式要求,将每张图片的文件名、每个目标的类别索引(根据YOLO的类别字典进行映射)、以及转换后的中心坐标、宽高信息整理成一行文本,写入到YOLO格式的标注文件中。

通过以上步骤,我们就成功地将原本的VOC格式图像标注数据转换为了YOLO格式,以便于在基于YOLO的目标检测模型训练时使用。值得注意的是,在实际操作过程中可能需要编写相应的脚本或程序来自动化完成这一系列数据处理工作。

import xml.etree.ElementTree as ET
import sys
import os.path
import cv2
 
 
class XmlParse:
    def __init__(self, file_path):
        # 初始化成员变量:self.tree 和 self.root 分别用于存储XML文件解析后的ElementTree对象和根节点;self.xml_file_path 存储传入的XML文件路径。
        self.tree = None
        self.root = None
        self.xml_file_path = file_path
 
    # 使用 try...except...finally 结构处理可能出现的异常情况。
    def ReadXml(self):  # 该方法用于读取XML文件并解析为ElementTree对象。
        try:
            self.tree = ET.parse(self.xml_file_path)  # 使用 xml.etree.ElementTree.parse() 方法解析XML文件,将结果赋值给 self.tree
            self.root = self.tree.getroot()  # 获取XML文件的根节点并赋值给 self.root。
        except Exception as e:  # 在 except Exception as e 块内,捕获并打印解析失败的错误信息,并通过 sys.exit() 终止程序执行。
            print("parse xml faild!")
            sys.exit()
        else:
            pass
        finally:  # finally 块会在不论是否出现异常的情况下都会被执行,这里返回解析好的 self.tree。
            return self.tree
 
    def WriteXml(self, destfile):
        dses_xml_file = os.path.abspath(destfile)
        self.tree.write(dses_xml_file, encoding="utf-8", xml_declaration=True)
 
 
def xml2txt(xml, labels, name_list, img_path):
    for i, j in zip(os.listdir(xml), os.listdir(img_path)):
        p = os.path.join(xml + '/' + i)  # xml路径
        xml_file = os.path.abspath(p)  # 绝对路径
        parse = XmlParse(xml_file)
        tree = parse.ReadXml()  # xml树
        root = tree.getroot()  # 根节点
 
        W = float(root.find('size').find('width').text)
        H = float(root.find('size').find('height').text)
 
        fil_name = root.find('filename').text[:-4]
        if not os.path.exists(labels):  # 如果路径不存在则创建
            os.mkdir(labels)
        out = open(labels + './' + fil_name + '.txt', 'w+')
        for obj in root.iter('object'):
 
            x_min = float(obj.find('bndbox').find('xmin').text)
            x_max = float(obj.find('bndbox').find('xmax').text)
            y_min = float(obj.find('bndbox').find('ymin').text)
            y_max = float(obj.find('bndbox').find('ymax').text)
            print(f'------------------------{i}-----------------------')
            print('W:', W, 'H:', H)
            # 计算公式
            xcenter = x_min + (x_max - x_min) / 2
            ycenter = y_min + (y_max - y_min) / 2
            w = x_max - x_min
            h = y_max - y_min
            # 目标框的中心点 宽高
            print('center_X: ', xcenter)
            print('center_Y: ', ycenter)
            print('target box_w: ', w)
            print('target box_h: ', h)
            # 归一化
            xcenter = round(xcenter / W, 6)
            ycenter = round(ycenter / H, 6)
            w = round(w / W, 6)
            h = round(h / H, 6)
 
            print('>>>>>>>>>>')
            print(xcenter)
            print(ycenter)
            print(w)
            print(h)
 
            class_dict = dict(zip(name_list, range(0, len(name_list))))
            class_name = obj.find('name').text
            if class_name not in name_list:
                pass
            else:
                class_id = class_dict[class_name]
                print('类别: ', class_id)
                print("创建成功: {}".format(fil_name + '.txt'))
                print('----------------------------------------------------')
                out.write(str(class_id) + " " + str(xcenter) + " " + str(ycenter) + " " + str(w) + " " + str(h) + "\n")
 
                # show_img
                m = os.path.join(img_path + '/' + j)
                block = cv2.imread(m)
                cv2.rectangle(block, pt1=(int((xcenter - w / 2) * W), int((ycenter - h / 2) * H)),
                              pt2=(int((xcenter + w / 2) * W), int((ycenter + h / 2) * H)),
                              color=(0, 255, 0), thickness=2)
                cv2.imshow('block', block)
                cv2.waitKey(300)
 
 
def folder_Path():
    img_path = './images_new'
    xml_path = './xmls_new'  # xml路径
    labels = './labels_new'  # 转txt路径
    name_list = ['trafficlight', 'stop', 'speedlimit', 'crosswalk']  # 类别名
    xml2txt(xml_path, labels, name_list, img_path)
 
 
if __name__ == '__main__':
    folder_Path()


                
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值