1. 从COCO到YOLO:人体姿态识别数据集格式转换实战
在计算机视觉领域,人体姿态识别一直是热门研究方向。最近我在处理一个运动分析项目时,需要将COCO格式的人体关键点数据转换为YOLO可用的.txt格式。这个看似简单的任务,实际操作中却遇到了不少意料之外的问题。今天我就把完整的转换流程和踩坑经验分享给大家。
COCO数据集是计算机视觉领域的标杆数据集之一,其人体关键点标注包含了17个关节点信息。而YOLO作为当前最流行的目标检测框架,其数据格式与COCO有很大不同。要实现两者间的转换,不仅需要理解两种格式的差异,还要处理关键点坐标系的转换、数据结构的重组等问题。下面我将从数据格式解析开始,逐步展示完整的转换过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. COCO数据集关键点标注结构解析
2.1 COCO标注文件的核心字段
COCO数据集的标注信息通常保存在一个大的JSON文件中。对于人体关键点任务,我们需要特别关注以下几个字段:
json复制"annotations": [
{
"keypoints": [x1,y1,v1,...,x17,y17,v17],
"num_keypoints": int,
"bbox": [x,y,width,height],
"category_id": 1,
"id": int,
"image_id": int,
"iscrowd": 0
}
]
其中每个关键点由三个值表示:(x坐标, y坐标, 可见性标记)。可见性标记v通常取值:
- v=0:未标注点
- v=1:标注但不可见(遮挡)
- v=2:标注且可见
2.2 关键点顺序与人体部位对应关系
COCO定义的17个关键点有固定顺序:
code复制0-鼻子 1-左眼 2-右眼 3-左耳 4-右耳
5-左肩 6-右肩 7-左肘 8-右肘
9-左腕 10-右腕 11-左髋 12-右髋
13-左膝 14-右膝 15-左踝 16-右踝
这个顺序在转换过程中必须严格遵守,否则会导致关键点对应关系错乱。我在第一次尝试时就因为忽略了这一点,导致生成的手臂关键点全部错位。
3. YOLO格式的关键点表示方法
3.1 YOLO关键点标注的标准格式
YOLO格式的.txt文件中,每行对应一个对象,格式为:
code复制<object-class> <x_center> <y_center> <width> <height> <px1> <py1> <p1_visibility> ... <pxn> <pyn> <pn_visibility>
其中:
- 坐标和尺寸都是相对于图像宽高的归一化值(0-1之间)
- 关键点坐标同样需要归一化
- 可见性标记通常保持与COCO一致
3.2 与COCO格式的核心差异
-
坐标系差异:
- COCO使用绝对像素坐标
- YOLO使用相对归一化坐标
-
边界框表示:
- COCO:[x,y,width,height](左上角坐标+宽高)
- YOLO:[x_center,y_center,width,height](中心坐标+宽高)
-
数据结构:
- COCO:JSON层级结构
- YOLO:平面文本文件
4. 完整转换流程与Python实现
4.1 转换步骤拆解
- 加载COCO标注JSON文件
- 遍历所有标注,提取人体实例
- 对每个实例:
- 转换边界框格式(左上角→中心点)
- 归一化所有坐标(除以图像宽高)
- 重组关键点数据
- 按YOLO格式写入.txt文件
4.2 Python实现代码
python复制import json
import os
def coco2yolo_keypoints(coco_json_path, output_dir, img_size_dict):
# 加载COCO标注文件
with open(coco_json_path) as f:
coco_data = json.load(f)
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
# 处理每个标注
for ann in coco_data['annotations']:
if ann['category_id'] != 1: # 1代表人
continue
img_id = ann['image_id']
img_info = next(i for i in coco_data['images'] if i['id'] == img_id)
img_w, img_h = img_info['width'], img_info['height']
# 转换边界框
x, y, w, h = ann['bbox']
x_center = (x + w/2) / img_w
y_center = (y + h/2) / img_h
w_norm = w / img_w
h_norm = h / img_h
# 处理关键点
keypoints = ann['keypoints']
yolo_kpts = []
for i in range(0, len(keypoints), 3):
px = keypoints[i] / img_w
py = keypoints[i+1] / img_h
vis = keypoints[i+2]
yolo_kpts.extend([px, py, vis])
# 生成YOLO格式行
yolo_line = [0, x_center, y_center, w_norm, h_norm] + yolo_kpts
yolo_line = ' '.join(map(str, yolo_line))
# 写入文件
txt_path = os.path.join(output_dir, f"{img_info['file_name'].split('.')[0]}.txt")
with open(txt_path, 'a') as f:
f.write(yolo_line + '\n')
# 使用示例
img_size_dict = {} # 可预先构建{image_id: (width, height)}的字典
coco2yolo_keypoints('person_keypoints_train2017.json', 'yolo_labels', img_size_dict)
4.3 关键实现细节说明
-
图像尺寸处理:
- 必须获取每张图像的实际宽高进行归一化
- 建议预先构建image_id到尺寸的映射字典提升效率
-
文件写入模式:
- 使用追加模式('a')而非写入模式('w')
- 因为一张图像可能包含多个人体实例
-
类别ID处理:
- COCO中person的category_id为1
- YOLO中通常将类别重新编号(这里设为0)
5. 实际应用中的问题与解决方案
5.1 关键点可见性处理
在转换过程中,关键点的可见性标记(v)需要特别注意:
-
v=0(未标注):
这些点在实际应用中通常需要特殊处理。我的做法是将它们的坐标设为(0,0),可见性设为0 -
v=1(标注但不可见):
保留原始坐标,可见性设为1。这在训练时可以帮助模型学习遮挡情况
注意:有些YOLO实现可能只使用0/1表示可见性,需要根据具体版本调整
5.2 边界框的调整策略
COCO的边界框有时不能完全包含所有关键点,特别是对于伸展的肢体。我推荐两种处理方式:
-
保持原样:
忠实于原始标注,即使有些关键点在框外 -
自适应扩展:
根据关键点位置调整边界框:python复制def adjust_bbox(keypoints, bbox): all_x = [k[0] for k in keypoints if k[2] > 0] all_y = [k[1] for k in keypoints if k[2] > 0] min_x, max_x = min(all_x), max(all_x) min_y, max_y = min(all_y), max(all_y) new_w = max_x - min_x new_h = max_y - min_y return [min_x, min_y, new_w, new_h]
5.3 处理拥挤(iscrowd)标注
COCO中iscrowd=1的标注表示拥挤人群,通常不适合用于关键点训练。建议:
python复制if ann['iscrowd'] == 1:
continue # 跳过拥挤标注
6. 转换后的数据验证
6.1 可视化检查方法
转换完成后,强烈建议进行可视化验证:
python复制import cv2
import matplotlib.pyplot as plt
def visualize_yolo_keypoints(img_path, txt_path):
img = cv2.imread(img_path)
h, w = img.shape[:2]
with open(txt_path) as f:
lines = f.readlines()
for line in lines:
parts = list(map(float, line.strip().split()))
# 绘制边界框
x_center, y_center, bw, bh = parts[1:5]
x1 = int((x_center - bw/2) * w)
y1 = int((y_center - bh/2) * h)
x2 = int((x_center + bw/2) * w)
y2 = int((y_center + bh/2) * h)
cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2)
# 绘制关键点
kpts = parts[5:]
for i in range(0, len(kpts), 3):
px = int(kpts[i] * w)
py = int(kpts[i+1] * h)
vis = int(kpts[i+2])
color = (0,0,255) if vis == 2 else (255,0,0)
cv2.circle(img, (px,py), 3, color, -1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()
6.2 常见验证问题
-
关键点错位:
- 检查COCO关键点顺序是否正确
- 确认图像宽高是否使用正确
-
边界框不匹配:
- 验证坐标转换公式
- 检查归一化计算
-
可见性标记异常:
- 确认v值的映射关系
- 检查是否有未处理的特殊情况
7. YOLOv8关键点训练配置
转换后的数据可以直接用于YOLOv8的关键点检测训练。以下是关键的配置要点:
7.1 数据集YAML配置
yaml复制# coco-kpts.yaml
path: /path/to/dataset
train: images/train
val: images/val
test: images/test
# 关键点元数据
kpt_shape: [17, 3] # 17个关键点,每个点3个值(x,y,v)
# 类别
names:
0: person
7.2 训练命令示例
bash复制yolo train model=yolov8n-pose.pt data=coco-kpts.yaml epochs=100 imgsz=640
7.3 关键参数说明
kpt_shape: 必须与你的数据格式匹配flip_idx: 如果需要水平翻转增强,需指定对称关键点索引conf: 关键点置信度阈值,可根据数据质量调整
我在实际使用中发现,YOLOv8对关键点数据的质量比较敏感。建议在训练前仔细检查转换后的数据,确保没有明显的标注错误。
