前言
本文分析将labelme的标签,转为YOLOv8、YOLOv5的格式,实现模型训练。
首先了解YOLOv8和YOLOv5标签格式,然后了解labelme标签格式,最近实现数据格式转换。

1、YOLOv8和YOLOv5标签格式
YOLOv8 的标签格式与 YOLOv5 基本相同,使用一种简单的txt文本格式,来存储每个图像的标注数据。
- 每个图像对应一个文本文件,这些文本文件与图像文件位于同一目录
- 并且具有相同的文件名,但标签文件类型是 .txt。
每个文本文件包含多行,每一行代表一个物体的标注。每行包含以下信息:(相邻数据用空格间隔开)
<class_id> <x_center> <y_center> <width> <height>
- <class_id>: 类别 ID,从 0 开始。
- <x_center>: 边界框中心的 x 坐标,归一化到图像宽度(值在 0 到 1 之间)。
- <y_center>: 边界框中心的 y 坐标,归一化到图像高度(值在 0 到 1 之间)。
- <width>: 边界框的宽度,归一化到图像宽度(值在 0 到 1 之间)。
- <height>: 边界框的高度,归一化到图像高度(值在 0 到 1 之间)。
示例:假设有一张名为 image01.jpg 的图像,其对应的标签文件 image01.txt 包含以下内容:
0 0.5 0.5 0.25 0.25
1 0.3 0.3 0.1 0.2
这表示图像中有两个对象:
- 第一个对象的类别 ID 是 0,边界框中心点在图像的中间 (0.5, 0.5),边界框的宽度和高度分别是图像宽度和高度的 25%。
- 第二个对象的类别 ID 是 1,边界框中心点在图像的左上方 (0.3, 0.3),边界框的宽度是图像宽度的 10%,高度是图像高度的 20%。
2、labelme标签格式
开源地址:https://github.com/labelmeai/labelme
Labelme 是一个图像标注工具,通常用于生成用于训练机器学习模型的标注数据。
Labelme 输出的数据格式是 JSON 文件,其中包含图像的标注信息。
{
    "version": "4.2.10",
    "flags": {},
    "shapes": [
        {
            "label": "class1",
            "points": [
                [x1, y1],
                [x2, y2],
                ...
            ],
            "group_id": null,
            "shape_type": "polygon",
            "flags": {}
        },
        {
            "label": "class2",
            "points": [
                [x1, y1],
                [x2, y2],
                ...
            ],
            "group_id": null,
            "shape_type": "rectangle",
            "flags": {}
        }
    ],
    "imagePath": "image_name.jpg",
    "imageData": "base64_encoded_image_data",
    "imageHeight": 1024,
    "imageWidth": 768
}
- version: Labelme 软件的版本号。
- flags: 一个可选的标志字典,通常为空。
- shapes: 一个列表,包含每个标注的详细信息。 
  - label: 这个标注的标签名称。
- points: 一个数组,包含标注的点的坐标。对于多边形,每个点用 (x, y) 表示;对于矩形,通常有两个点:左上角和右下角。
- group_id: 一个可选的组 ID,用于标记同一组的多个形状。
- shape_type: 形状的类型,例如 "polygon"、"rectangle"、"circle" 等。
- flags: 可选的标志字典,通常为空。
 
- imagePath: 原始图像的路径或名称。
- imageData: 图像数据的 base64 编码字符串。这个字段可以为空,如果有图像路径信息的话。
- imageHeight: 图像的高度。
- imageWidth: 图像的宽度。
3、labelme转YOLOv8、YOLOv5
首先定义标签名字和数字的映射 ,比如有两个类别car、bus。
label_map = {
    "car": 0,
    "bus": 1
}如果只有单个类别,比如car。
label_map = {
    "car": 0
}方案1——单个文件转换,代码如下所示:
import json
import os
# 定义标签映射
label_map = {
    "car": 0,
    "bus": 1
}
def convert_labelme_to_yolo(json_path, output_dir):
    with open(json_path, 'r') as f:
        labelme_data = json.load(f)
    image_width = labelme_data['imageWidth']
    image_height = labelme_data['imageHeight']
    yolo_annotations = []
    for shape in labelme_data['shapes']:
        label = shape['label']
        if label not in label_map:
            continue  # 忽略未定义的标签
        class_id = label_map[label]
        points = shape['points']
        if shape['shape_type'] == 'rectangle':
            (x1, y1), (x2, y2) = points
        elif shape['shape_type'] == 'polygon':
            x1, y1 = min(point[0] for point in points), min(point[1] for point in points)
            x2, y2 = max(point[0] for point in points), max(point[1] for point in points)
        else:
            continue  # 其他类型不处理
        x_center = (x1 + x2) / 2.0 / image_width
        y_center = (y1 + y2) / 2.0 / image_height
        width = (x2 - x1) / image_width
        height = (y2 - y1) / image_height
        yolo_annotations.append(f"{class_id} {x_center} {y_center} {width} {height}")
    output_file = os.path.join(output_dir, os.path.splitext(os.path.basename(json_path))[0] + '.txt')
    with open(output_file, 'w') as f:
        f.write('\n'.join(yolo_annotations))
# 示例使用
convert_labelme_to_yolo('/path/to/labelme_file.json', '/path/to/output_dir')
方案2——多个标签文件转换
读取一个文件下所有json文件,然后转为YOLO的txt标签格式
import json
import os
# 定义标签映射
label_map = {
    "car": 0,
    "bus": 1
}
def convert_labelme_to_yolo(json_path, output_dir):
    with open(json_path, 'r') as f:
        labelme_data = json.load(f)
    image_width = labelme_data['imageWidth']
    image_height = labelme_data['imageHeight']
    yolo_annotations = []
    for shape in labelme_data['shapes']:
        label = shape['label']
        if label not in label_map:
            continue  # 忽略未定义的标签
        class_id = label_map[label]
        points = shape['points']
        if shape['shape_type'] == 'rectangle':
            (x1, y1), (x2, y2) = points
        elif shape['shape_type'] == 'polygon':
            x1, y1 = min(point[0] for point in points), min(point[1] for point in points)
            x2, y2 = max(point[0] for point in points), max(point[1] for point in points)
        else:
            continue  # 其他类型不处理
        x_center = (x1 + x2) / 2.0 / image_width
        y_center = (y1 + y2) / 2.0 / image_height
        width = (x2 - x1) / image_width
        height = (y2 - y1) / image_height
        yolo_annotations.append(f"{class_id} {x_center} {y_center} {width} {height}")
    output_file = os.path.join(output_dir, os.path.splitext(os.path.basename(json_path))[0] + '.txt')
    with open(output_file, 'w') as f:
        f.write('\n'.join(yolo_annotations))
def process_folder(input_folder, output_folder):
    # 创建输出文件夹(如果不存在)
    os.makedirs(output_folder, exist_ok=True)
    
    # 处理输入文件夹中的每个 JSON 文件
    for filename in os.listdir(input_folder):
        if filename.endswith(".json"):
            json_path = os.path.join(input_folder, filename)
            convert_labelme_to_yolo(json_path, output_folder)
# 示例使用
input_folder = "/mnt/data/buffer_nails_all"
output_folder = "/mnt/data/yolo_labels"
process_folder(input_folder, output_folder)
# 列出输出文件夹中的文件以确认
os.listdir(output_folder)
分享完成~






![leetcode-04-[24]两两交换链表中的节点[19]删除链表的倒数第N个节点[160]相交链表[142]环形链表II](https://img-blog.csdnimg.cn/direct/264c65aaede0404283eeaadb966a964c.png)












