前言
本文分析将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)
分享完成~