【数据标注】YOLO 系列中 labelme 标记的 json 文件与 txt 互转

本文介绍了深度学习中图像目标检测和分割任务的数据标注方式,如BoundingBox、Mask和Keypoint,以及对应的XML、JSON、CSV等格式。重点讨论了Yolo系列算法中如何将JSON标注文件转换为Yolo所需的TXT格式,包括bbox和mask的转换方法,并提供了转换代码示例。此外,提到了数据集如COCO、PascalVOC和ImageNet的标注格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在深度学习领域中,数据的标注方式和对应的数据格式确实五花八门。下面是一些常见的标注方式和对应的数据格式:

    1. 目标检测标注方式:对于图像目标检测任务,常见的标注方式包括Bounding Box、Mask、Keypoint等。其中,
    • Bounding Box指的是在图像中用矩形框标记出目标的位置和大小,通常用左上角和右下角的坐标表示;
    • Mask指的是将目标的轮廓用像素点进行标记,通常用二值图像表示;
    • Keypoint指的是在目标上标记出关键点的位置,通常用关键点坐标表示。
    • 这些标注方式通常用XML、JSON、CSV等格式进行存储。
    1. 分割标注方式:对于图像分割任务,常见的标注方式包括Semantic Segmentation、Instance Segmentation等。其中,
    • 语义分割Semantic Segmentation:指的是将图像中的每个像素都进行分类,以实现对图像的整体理解;
    • 实例分割Instance Segmentation:指的是将图像中的每个目标都进行分割,以实现对目标的精细理解。
    • 这些标注方式通常用PNG、JPEG、TIFF等格式进行存储。

1

在公开数据中,有一些特色代表性的标注文件存储格式,例如:

    1. COCO:这是一个广泛使用的图像目标检测和分割数据集,其标注文件采用JSON格式进行存储。
    1. Pascal VOC:这是一个经典的图像目标检测和分割数据集,其标注文件采用XML格式进行存储。
    1. ImageNet:这是一个包含超过1400万张图像的数据集,其标注文件采用TXT格式进行存储,每张图像对应一个TXT文件。
    1. Open Images:这是一个由Google发布的大规模图像数据集,其标注文件采用CSV格式进行存储。

本文主要记录在yolo系列目标检测和分割算法中,尝尝会将json文件,转为yolo所需要的txt文件。分别包括了目标检测和分割的数据转换形式。

一、yolo_v5 的 bbox 转 txt

yolo v5bbox目标检测中,数据形式是(x-center,y_center, width, height)。都是图像的相对大小

import numpy as np
import os
from PIL import Image
import json
import pandas as pd

label_list = ['a', 'b']
class LabelJson(object):
    def __init__(self, abs_path=None, mode='saveTXT') -> None:
        super().__init__()
        self.abs_path = abs_path
        self.mode = mode
        self.read()

    
    def read(self):
        with open(self.abs_path,'r', encoding='utf-8') as f:
            lj = json.load(f)
        self.wh = [lj.get('imageWidth'), lj.get('imageHeight')]
        shapes = lj.get('shapes')
        if self.mode=='saveTXT':
            self.cls = [i.get('label') for i in shapes]

        points = [i.get('points') for i in shapes]
        points = [np.array(i, dtype=np.int32).reshape((-1, 2)) for i in points]
        self.loc = points
        self.box = [[j[:, 0].min(), j[:, 1].min(), j[:, 0].max(), j[:, 1].max()] for j in points]
        self.img_name=lj.get('imagePath')
        self.is_pos = bool(self.cls)
        return self

def saveTXT_bbox(json_info, save_path, imp):
    box = np.array(json_info.box)
    w, h = json_info.wh
    cls = json_info.cls
    if w is None:
        img = Image.open(imp)
        w, h = img.size
    print(cls)

        with open(save_path, 'w', encoding='utf-8') as ff:
        for idx, (xmin, ymin, xmax, ymax) in enumerate(box):
            label = cls[idx].split('-')[0]

            ff.write(f'{label_list.index(label)} {(xmin+xmax)/(2*w)} {(ymin+ymax)/(2*h)} {(xmax-xmin)/w} {(ymax-ymin)/h}\n')

def json2txtBbox_main(img_dir, json_dir, save_dir):
    for imgfile in os.listdir(img_dir):
        print(imgfile)
        name, suffix = os.path.splitext(imgfile)
        json_path = os.path.join(json_dir, name + '.json')
        if os.path.exists(json_path):
            img_path = os.path.join(img_dir, imgfile)
            json_info = LabelJson(json_path, mode='saveTXT')
            saveTXT_bbox(json_info, os.path.join(save_dir, name + '.txt'), img_path)


if __name__=="__main__":
    img_dir = r'./image'
    json_dir = r'./label'
    save_dir = r'./txt'

    json2txtBbox_main(img_dir, json_dir, save_dir)

保存后的txt文件,存储内容为:

1

二、yolo_v8 的 mask 转 txt

yolo v8中,可以实现对目标的分割,其中分割数据集文件中单行的格式如下:

<class-index> <x1> <y1> <x2> <y2> ... <xn> <yn>
  • class-index: is the index of the class for the object,
  • x1 y1 x2 y2 … xn yn: are the bounding coordinates of the object’s segmentation mask.

例如:

0 0.6812 0.48541 0.67 0.4875 0.67656 0.487 0.675 0.489 0.66
1 0.5046 0.0 0.5015 0.004 0.4984 0.00416 0.4937 0.010 0.492 0.0104

数据整理定义参考链接:Ultralytics YOLOv8 Docs
Instance Segmentation Datasets Overview

实现代码如下所示:

def saveTXT_mask(json_info, save_path, imp):
    points = json_info.loc
    w, h = json_info.wh
    cls = json_info.cls
    if w is None:
        img = Image.open(imp)
        w, h = img.size

    with open(save_path, 'w', encoding='utf-8') as ff:
        for idx, point in enumerate(points):
            label = cls[idx]

            coor_str = ''
            for (x, y) in point:
                coor_str += str(round(x/w, 4))+' '+str(round(y/h, 4))+' '
            print("coor_str:", coor_str)

            ff.write(f'{label_list.index(label)} {coor_str}\n')

def json2txtMask_main(img_dir, json_dir, save_dir):
    for imgfile in os.listdir(img_dir):
        print(imgfile)
        name, suffix = os.path.splitext(imgfile)
        json_path = os.path.join(json_dir, name + '.json')
        if os.path.exists(json_path):
            img_path = os.path.join(img_dir, imgfile)
            json_info = LabelJson(json_path, mode='saveTXT')
            saveTXT_mask(json_info, os.path.join(save_dir, name + '.txt'), img_path)


if __name__ == "__main__":
    img_dir = r'./image'
    json_dir = r'./label'
    save_dir = r'./txt'
    json2txtMask_main(img_dir, json_dir, save_dir)

保存后的txt文件,存储内容为:

在这里插入图片描述

三、txt 转 labelme json

第一章节的反过程。既然能给转过去,那就给转回来,这里就是这样的。代码记录如下:

import torch
import numpy as np
import base64, os
from PIL import Image
import io
import json

def xywh2xyxy(x, w, h):
    # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[:, 0] = (x[:, 0] - x[:, 2] / 2)*w  # top left x
    y[:, 1] = (x[:, 1] - x[:, 3] / 2)*h  # top left y
    y[:, 2] = (x[:, 0] + x[:, 2] / 2)*w  # bottom right x
    y[:, 3] = (x[:, 1] + x[:, 3] / 2)*h  # bottom right y
    return np.array(y, dtype='int32')

def txt2bbox(txtfile_path, w, h):

    cls, xywh_list = [], []
    with open(txtfile_path, "r") as f:
        for line in f.readlines():
            line = line.strip('\n').split(' ')  # 去掉列表中每一个元素的换行符
            cls.append(line[0])
            xywh_list.append(line[1:])

    xywh_array = np.array(xywh_list, dtype='float64')
    xyxy_array = xywh2xyxy(xywh_array, w, h)
    return cls, xyxy_array.tolist()

def base64encode_img(src_image):
    # src_image = Image.open(image_path)
    # src_image = Image.fromarray(cv2.cvtColor(src_image, cv2.COLOR_BGR2RGB))
    output_buffer = io.BytesIO()
    src_image.save(output_buffer, format='JPEG')
    byte_data = output_buffer.getvalue()
    base64_str = base64.b64encode(byte_data).decode('utf-8')
    return base64_str
def savejson(points_list, clses_list, img_tmp, filename, save_dir):
    A = dict()
    listbigoption = []
    for cls, points in zip(clses_list, points_list):
        listobject = dict()
        x1, y1, x2, y2 = points

        listobject['points'] = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
        listobject['line_color'] = 'null'
        listobject['label'] = cls

        listobject['fill_color'] = 'null'
        listbigoption.append(listobject)

    A['imageData'] = base64encode_img(img_tmp)
    A['imagePath'] = filename
    A['shapes'] = listbigoption
    A['flags'] = {}

    suffix = os.path.splitext(filename)[-1]
    with open(save_dir + "/" + filename.replace(suffix, ".json"), 'w', encoding='utf-8') as f:
        json.dump(A, f, indent=2, ensure_ascii=False)
def txt2json_main():
    img_dir = r'./images'
    txt_dir = r'./val'
    save_dir = r'./json'

    for imgfile in os.listdir(img_dir):
        print(imgfile)
        name, suffix = os.path.splitext(imgfile)
        txtfile = imgfile.replace(suffix, '.txt')
        txt_path = os.path.join(txt_dir, txtfile)
        if not os.path.isfile(txt_path):
            continue
        img_path = os.path.join(img_dir, imgfile)
        img = Image.open(img_path)
        w, h = img.size
        cls, xyxy = txt2bbox(txt_path, w, h)
        print(cls)
        print(xyxy)
        print()
        savejson(xyxy, cls, img, imgfile, save_dir)

四、总结

整个过程大致就分为以下几步:

  1. 读取原数据存储文件,拿到需要转储的对应的数字
  2. 转化为新的数组形式
  3. 另存为新的文件格式

最后,记得检查下转储后的文件是否正确。这个可以在训练阶段,查看训练过程中生成的图片,记录了标签和图像合并到一起之后的结果。帮助我们检验下制作的标注文件,是否有问题,并做出及时的调整。


如果您觉得本篇文章对你有帮助,欢迎点赞 👍,让更多人看到,这是对我继续写下去的鼓励。如果能再点击下方的红包打赏,给博主来一杯咖啡,那就太好了。💪

<think>好的,我现在需要帮助用户将Python中的XML数据转换为JSON格式。首先,我得回忆一下用户提供的引用内容,看看有哪些方法可用。用户提到了几个引用,其中引用[2]和[3]提到了使用Python自带的模块或者xmltodict库来实现转换。另外,引用[4]和[5]可能涉及更复杂的结构转换,比如处理标注文件,但用户的问题可能更基础。 首先,用户的需求是转换XML到JSON,我需要考虑两种常见的方法:使用内置库如xml.etree.ElementTree,或者使用第三方库如xmltodict。根据引用[3],xmltodict.parse可以将XML转换为字典,然后就可以用json库转成JSON。这可能更简单,但需要安装xmltodict。而使用ElementTree的方法不需要额外安装库,但代码可能更繁琐。 接下来,我应该分步骤解释这两种方法。第一步可能是安装必要的库,比如xmltodict,如果用户选择第二种方法。然后,读取XML数据,无论是从字符串还是文件。对于文件处理,可能需要打开文件并读取内容,或者解析字符串。 然后,将XML解析为Python字典,这是关键步骤。xmltodict的parse方法可以自动将XML节点转换为字典,而ElementTree则需要手动遍历节点,构建字典结构,这对于复杂的XML可能比较麻烦。因此,推荐使用xmltodict的方法更高效。 接着,将字典转换为JSON,这可以通过json.dumps()实现,需要注意indent参数美化输出,以及ensure_ascii参数处理中文字符。此外,保存到文件的话,使用json.dump()直接写入文件。 可能的问题:XML中的属性如何处理?xmltodict会自动处理属性,通常放在@前缀的键中。而ElementTree需要手动提取attrib属性。此外,某些XML结构可能有多个同名节点,转换为字典或列表时需要处理,比如使用force_list参数。 用户可能需要示例代码,所以需要提供两种方法的代码示例。第一种使用xmltodict,第二种使用ElementTree。同时,提醒用户注意安装xmltodict,以及处理文件读写时的路径问题。 另外,用户的问题可能涉及转换后的JSON结构是否符合预期,比如是否需要保留XML的某些特性,或者调整键的名称。可能需要指导用户如何调整解析后的字典结构,再转换为JSON。 最后,生成相关问题,可能涉及处理属性、处理复杂结构、性能优化,或者逆向转换等。需要确保问题相关且有帮助。</think>### 分步方法实现XML转JSON 以下是两种常用的Python方法,分别基于`xmltodict`库和内置库`xml.etree.ElementTree`: --- #### **方法1:使用`xmltodict`库(推荐)** **步骤:** 1. **安装库**(如未安装): ```bash pip install xmltodict ``` 2. **读取XML数据**(假设从文件读取): ```python with open("input.xml", "r", encoding="utf-8") as f: xml_str = f.read() ``` 3. **解析XML为字典**: ```python import xmltodict json_dict = xmltodict.parse(xml_str, encoding="utf-8") # 解析为OrderedDict[^3] ``` 4. **转换为JSON字符串并保存**: ```python import json json_str = json.dumps(json_dict, indent=4, ensure_ascii=False) # 美化输出,支持中文[^2] with open("output.json", "w", encoding="utf-8") as f: f.write(json_str) ``` --- #### **方法2:使用内置库`xml.etree.ElementTree`** **步骤:** 1. **解析XML为Element对象**: ```python import xml.etree.ElementTree as ET tree = ET.parse("input.xml") root = tree.getroot() ``` 2. **递归转换为字典**: ```python def xml_to_dict(element): result = {} for child in element: child_data = xml_to_dict(child) if child.tag in result: if not isinstance(result[child.tag], list): result[child.tag] = [result[child.tag]] result[child.tag].append(child_data) else: result[child.tag] = child_data if not result: # 处理叶子节点 return element.text return result json_dict = {root.tag: xml_to_dict(root)} ``` 3. **转换为JSON并保存**: ```python import json with open("output.json", "w", encoding="utf-8") as f: json.dump(json_dict, f, indent=4, ensure_ascii=False) ``` --- ### 关键注意事项 1. **XML属性处理**: - `xmltodict`会自动将属性转换为`@属性名`的键,例如`<node id="1">`会变为`{"@id": "1", ...}`。 - 内置库方法需手动提取`element.attrib`。 2. **复杂结构兼容性**: - 如果XML包含重复标签,需确保转换为字典或列表(如方法2中的列表处理逻辑)[^4]。 3. **编码问题**: 使用`ensure_ascii=False`避免中文字符被转义为Unicode码。 --- ### 示例代码(完整流程) ```python # 方法1示例 import xmltodict import json # 从文件读取XML with open("data.xml", "r", encoding="utf-8") as f: xml_data = f.read() # 转换为字典并保存为JSON json_data = xmltodict.parse(xml_data) with open("output.json", "w", encoding="utf-8") as f: json.dump(json_data, f, indent=4, ensure_ascii=False) ``` ---
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值