You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CVAT格式XML转YOLO格式报错求助及代码评估需求

CVAT XML转YOLO格式问题解决方案

问题描述

尝试将CVAT格式的XML标注文件转换为YOLO格式时,计算归一化坐标(yolo_x、yolo_y、yolo_w、yolo_h)时出现类型错误,用int()转换坐标后仍触发值错误。

XML示例

<image id="0" name="20230226_145238.jpg" width="510" height="680">
  <box label="meter" source="manual" occluded="0" xtl="202.10" ytl="259.28" xbr="314.74" ybr="305.89" z_order="0">
  </box>
  <box label="zero" source="manual" occluded="0" xtl="219.68" ytl="269.57" xbr="234.84" ybr="293.43" z_order="0">
  </box>
  <box label="eight" source="manual" occluded="0" xtl="234.84" ytl="270.35" xbr="249.11" ybr="293.99" z_order="0">
  </box>
  <box label="one" source="manual" occluded="0" xtl="249.78" ytl="271.69" xbr="263.38" ybr="293.88" z_order="0">
  </box>
  <box label="one" source="manual" occluded="0" xtl="263.38" ytl="272.69" xbr="278.99" ybr="295.10" z_order="0">
  </box>
  <box label="eight" source="manual" occluded="0" xtl="279.55" ytl="272.80" xbr="293.71" ybr="296.33" z_order="0">
  </box>
  <box label="three" source="manual" occluded="0" xtl="295.38" ytl="272.58" xbr="308.68" ybr="297.33" z_order="0">
  </box>
  <box label="dot" source="manual" occluded="0" xtl="289.47" ytl="292.43" xbr="293.93" ybr="296.55" z_order="0">
  </box>
</image>

原转换代码

import os
import glob
from xml.etree.ElementTree import parse

xml_dir = "./data/"

class Voc_to_yolo_convter():
    def __init__(self, xml_path):
        self.xml_path_list = glob.glob(os.path.join(xml_path, "*.xml"))
        # print(self.xml_path_list)

    def get_voc_to_yolo(self):
        for xml_path in self.xml_path_list:
            tree = parse(xml_path)
            root = tree.getroot()

            # image_meta
            meta = root.findall("image")
            for image in meta:
                image_name = image.attrib["name"]
                image_width = image.attrib["width"]
                image_height = image.attrib["height"]
                # print(image_width, image_height)

                # object_meta
                object_metas = image.findall("box")
                for bbox in object_metas:
                    label = bbox.attrib["label"]
                    xtl = int(bbox.attrib["xtl"])
                    ytl = bbox.attrib["ytl"]
                    xbr = bbox.attrib["xbr"]
                    ybr = bbox.attrib["ybr"]

                    # CVAT to yolo
                    yolo_x = round(((xtl + xbr)/2)/image_width, 6)
                    yolo_y = round(((ytl + ybr)/2)/image_height, 6)
                    yolo_w = round((xbr - xtl)/image_width, 6)
                    yolo_h = round((ybr - ytl)/image_height, 6)

                    print(yolo_x, yolo_y, yolo_w, yolo_h)

test= Voc_to_yolo_convter(xml_dir)
test.get_voc_to_yolo()

问题原因

  1. 类型不匹配:XML中读取的坐标(如xtl="202.10")是带小数的字符串,直接用int()转换会报错;同时图片宽高image_width/image_height也是字符串,参与除法运算时触发类型错误。
  2. 变量转换不全:原代码仅转换了xtl为int,其余坐标和尺寸未做类型转换,导致字符串与数值混合计算,引发值错误。

解决方案

将所有从XML读取的数值型属性(坐标、图片尺寸)统一转换为float类型,确保所有参与计算的变量都是数值类型,避免类型不兼容问题。

优化后的转换代码

import os
import glob
from xml.etree.ElementTree import parse

xml_dir = "./data/"
# 自定义标签映射,根据你的数据集修改
LABEL_MAP = {
    "meter": 0,
    "zero": 1,
    "eight": 2,
    "one": 3,
    "three": 4,
    "dot": 5
}

class CVATToYOLOConverter():
    def __init__(self, xml_path):
        self.xml_path_list = glob.glob(os.path.join(xml_path, "*.xml"))
        # 创建labels目录(如果不存在)
        os.makedirs("./labels", exist_ok=True)

    def convert(self):
        for xml_path in self.xml_path_list:
            tree = parse(xml_path)
            root = tree.getroot()

            for image in root.findall("image"):
                image_name = image.attrib["name"]
                # 转换图片尺寸为float
                img_width = float(image.attrib["width"])
                img_height = float(image.attrib["height"])
                # 生成对应YOLO格式的txt文件路径
                txt_path = os.path.join("./labels", os.path.splitext(image_name)[0] + ".txt")
                
                # 清空文件(避免重复写入)
                with open(txt_path, "w", encoding="utf-8") as f:
                    pass

                for bbox in image.findall("box"):
                    label = bbox.attrib["label"]
                    # 转换所有坐标为float
                    xtl = float(bbox.attrib["xtl"])
                    ytl = float(bbox.attrib["ytl"])
                    xbr = float(bbox.attrib["xbr"])
                    ybr = float(bbox.attrib["ybr"])

                    # 计算YOLO归一化坐标
                    yolo_x = round(((xtl + xbr) / 2) / img_width, 6)
                    yolo_y = round(((ytl + ybr) / 2) / img_height, 6)
                    yolo_w = round((xbr - xtl) / img_width, 6)
                    yolo_h = round((ybr - ytl) / img_height, 6)

                    # 写入txt文件(YOLO格式:标签ID 中心x 中心y 宽 高)
                    with open(txt_path, "a", encoding="utf-8") as f:
                        f.write(f"{LABEL_MAP[label]} {yolo_x} {yolo_y} {yolo_w} {yolo_h}\n")
                    
                    print(f"已处理: {image_name} -> {label} {yolo_x} {yolo_y} {yolo_w} {yolo_h}")

# 执行转换
converter = CVATToYOLOConverter(xml_dir)
converter.convert()

代码优化说明

  1. 命名规范:类名和方法名改为符合Python规范的命名方式,可读性更强。
  2. 标签映射:添加LABEL_MAP字典,将文本标签转为YOLO要求的数字ID,符合格式标准。
  3. 文件保存:自动创建labels目录,将转换结果保存为对应图片名称的txt文件,完整实现YOLO格式输出。
  4. 类型统一:所有数值型属性统一转换为float,保留坐标精度的同时避免类型错误。
  5. 健壮性提升:自动创建目录、清空重复文件,避免数据冗余。

内容的提问来源于stack exchange,提问作者박경덕

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:25:16