使用 Label Studio 标注关键点数据并转换为 COCO 格式(MMPose 实战指南)
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
Label Studio 是一款广泛使用的开源深度学习标注工具,但在关键点(keypoint)标注场景下,它无法直接导出 MMPose 所需的 COCO 格式标注文件。本文将以 docs/en/user_guides/label_studio.md 为核心,系统讲解如何在 Label Studio 中完成关键点、分割与包围框的规范标注,如何通过 labelstudio2coco.py 将导出的 JSON 一键转换为 COCO 格式,并最终接入 MMPose 的训练/评估配置,同时结合仓库源码剖析转换器内部的实现原理与注意事项。
背景:为什么需要转换脚本
MMPose 的数据集统一采用 COCO 风格标注,其要求每条实例标注(annotation)同时携带keypoints、segmentation与bbox三类信息。而 Label Studio 在标注时会将这些信息分散到不同的独立标注(KeyPointLabels、PolygonLabels、RectangleLabels)中,因此标注阶段必须遵守特定规则,才能在后续转换脚本中正确合并为单个实例。
此外,MMPose 官方还提供了 Label Studio 相关工具的使用入口,该文档中声明:MMPose 对标注工具不做任何限制,只要最终标注结果满足数据格式要求即可(见 dataset_tools.md 的注释说明)。
一、Label Studio 标注规范
1.1 标注界面配置
新建 Label Studio 项目后,需要在项目Settings(设置)中找到Labeling Interface(标注界面),点击Code,粘贴以下示例代码。该配置定义了三种标注类型,分别对应 COCO 格式中的三类字段:
KeyPointLabels:对应keypointsPolygonLabels:对应segmentationRectangleLabels:对应bbox
<View> <KeyPointLabels name="kp-1" toName="img-1"> <Label value="person" background="#D4380D"/> </KeyPointLabels> <PolygonLabels name="polygonlabel" toName="img-1"> <Label value="person" background="#0DA39E"/> </PolygonLabels> <RectangleLabels name="label" toName="img-1"> <Label value="person" background="#DDA0EE"/> </RectangleLabels> <Image name="img-1" value="$img"/> </View>从源码角度可以更清楚地理解该 XML 的作用。labelstudio2coco.py 中的LSConverter.__init__通过xml.etree.ElementTree解析该 XML:
tree = ET.parse(config) root = tree.getroot() labels = root.findall('.//KeyPointLabels/Label') label_values = [label.get('value') for label in labels] self.categories = list() self.category_name_to_id = dict() for i, value in enumerate(label_values): # category id start with 1 self.categories.append({'id': i + 1, 'name': value}) self.category_name_to_id[value] = i + 1即转换器只读取KeyPointLabels下的<Label value="..."/>作为类别名称,category id 从 1 开始按顺序递增,并构建categories列表写入最终 COCO JSON 的categories字段。
1.2 标注顺序(关键规则)
由于需要将不同类型的标注合并到同一个实例,标注顺序决定了各标注的归属关系。规则如下:
- 按
KeyPointLabels→PolygonLabels/RectangleLabels的顺序标注; - 一个实例内以关键点开始、以非关键点结束;
KeyPointLabels的顺序和数量必须与 MMPose 配置文件dataset_info中定义的关键点顺序和数量一致;PolygonLabels与RectangleLabels的标注顺序可以互换,且只需标注其中一种。
注意:bbox和area会基于后标注的 PolygonLabels/RectangleLabels 计算。若先标注 PolygonLabels,则 bbox 基于后标注的 RectangleLabels 范围、area 等于矩形面积;反之,则基于多边形的最小外接矩形与多边形面积。
这一点在 labelstudio2coco.py 的 docstring 中亦有明确描述:
The annotations in label studio must follow the order: keypoint 1, keypoint 2... keypoint n, rect of the instance, polygon of the instance, then annotations of the next instance. Where the order of rect and polygon can be switched, the bbox and area of the instance will be calculated with the data behind. Only annotating one of rect and polygon is also acceptable.1.3 关键点顺序与 dataset_info 对齐
"关键点顺序与dataset_info一致"意味着你需要先确定目标关键点骨架定义。以 COCO 人体 17 关键点为例,其顺序定义在 configs/base/datasets/coco.py 的dataset_info.keypoint_info中:0 为 nose(鼻子)、1 为 left_eye(左眼)、2 为 right_eye(右眼)……直到 16 为 right_ankle(右踝),每个关键点还包含color(可视化颜色)、type(upper/lower)、swap(左右翻转互换关系)等信息。在 Label Studio 中标注时,每个实例的关键点必须严格按照这个顺序依次点选,转换后的keypoints数组才能与 MMPose 的数据加载与评估逻辑匹配。
1.4 导出标注结果
标注完成后,点击项目界面的Export按钮,选择JSON格式,点击Export下载包含标注的 JSON 文件。
注意:导出的文件只包含标注,不含原始图片,因此需要单独提供对应的标注图片。不建议直接使用上传方式导入图片,因为 Label Studio 会截断过长的文件名;推荐使用Export功能中的导出 COCO 格式工具,其下载的压缩包内会包含图片文件夹。
二、使用转换脚本生成 COCO 数据集
2.1 脚本用法与参数
转换脚本位于 tools/dataset_converters/labelstudio2coco.py,其参数解析定义如下:
parser = argparse.ArgumentParser( description='Convert Label Studio JSON file to COCO format JSON File') parser.add_argument('config', help='Labeling Interface xml code file path') parser.add_argument('input', help='Label Studio format JSON file path') parser.add_argument('output', help='The output COCO format JSON file path')三个位置参数分别为:
| 参数 | 含义 | 说明 |
|---|---|---|
config | 标注界面 XML 代码文件 | 即上一节Labeling Interface -> Code中的 XML 内容保存为.xml文件 |
input | Label Studio 导出的 JSON 文件 | 例如project-1-at-2023-05-13-09-22-91b53efa.json |
output | 输出 COCO 格式 JSON 路径 | 若路径不存在,脚本会自动创建 |
实际命令示例:
python tools/dataset_converters/labelstudio2coco.py config.xml project-1-at-2023-05-13-09-22-91b53efa.json output/result.json(该命令同时出现在 docs/en/user_guides/dataset_tools.md 的 Label Studio 章节中,是官方推荐的调用方式。)
2.2 转换后的目录结构
转换完成后,将图片文件夹放入输出目录,即可得到完整的 COCO 数据集,目录结构示例如下:
. ├── images │ ├── 38b480f2.jpg │ └── aeb26f04.jpg └── result.json2.3 接入 MMPose 配置
若要在 MMPose 中使用该数据集,可在配置文件中按如下方式修改dataset:
dataset=dict( type=dataset_type, data_root=data_root, data_mode=data_mode, ann_file='result.json', data_prefix=dict(img='images/'), pipeline=train_pipeline, )其中ann_file指向转换得到的result.json,data_prefix的img指向图片目录。MMPose 的 BaseCocoStyleDataset 会以data_root作为ann_file与data_prefix的根目录来拼接完整路径(源码中ann_file、data_root、data_prefix均为其构造参数),并通过assert exists(self.ann_file)校验标注文件是否存在后开始加载。
三、转换脚本实现原理详解
3.1 整体流程
main()函数依次执行:解析参数 → 实例化LSConverter(config)→ 调用convert_to_coco(input_json, output_json)。核心转换逻辑位于LSConverter.convert_to_coco中,其流程可概括为:
- 读取 Label Studio 导出的 JSON(每个 item 对应一张图片);
- 跳过没有标注的 item(记录 warning 日志);
- 逐条遍历该 item 的
result标注列表,根据label['type']分发到三种处理分支; - 聚合
images、annotations、categories与info后写出 COCO JSON。
3.2 图片信息提取
每个 item 的file_upload字段作为图片文件名,image_id按顺序自增(image_id = len(images))。图片宽高从标注的original_width/original_height字段中获取:
if not height or not width: if 'original_width' not in label or \ 'original_height' not in label: logger.debug( f'original_width or original_height not found' f'in {image_name}') continue # get height and width info from annotations width, height = label['original_width'], label[ 'original_height'] images = add_image(images, width, height, image_id, image_name)add_image内部构造{'width', 'height', 'id', 'file_name'}四字段的 image 记录。
3.3 矩形框(RectangleLabels)处理
Label Studio 的矩形坐标以百分比(0~100)存储,需要乘以original_width/original_height还原为像素坐标:
x = label['value']['x'] y = label['value']['y'] w = label['value']['width'] h = label['value']['height'] x = x * label['original_width'] / 100 y = y * label['original_height'] / 100 w = w * label['original_width'] / 100 h = h * label['original_height'] / 100 # rect annotation should be later than keypoints annotations[-1]['bbox'] = [x, y, w, h] annotations[-1]['area'] = w * h annotations[-1]['num_keypoints'] = kp_num注意annotations[-1]:矩形框的 bbox/area 会直接写入上一条已创建的 keypoint annotation,这正是"矩形必须标注在关键点之后"这一规则的代码体现。
3.4 多边形(PolygonLabels)处理
多边形同样以百分比坐标存储,转换时计算最小外接矩形作为 bbox,并用向量叉积公式(鞋带公式)计算多边形面积:
points_abs = [(x / 100 * width, y / 100 * height) for x, y in label['value']['points']] x, y = zip(*points_abs) x1, y1, x2, y2 = min(x), min(y), max(x), max(y) # calculate bbox and area from polygon's points # which may be different with rect annotation bbox = [x1, y1, x2 - x1, y2 - y1] area = float(0.5 * np.abs( np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))) # polygon label should be later than keypoints annotations[-1]['segmentation'] = [[ coord for point in points_abs for coord in point ]] annotations[-1]['bbox'] = bbox annotations[-1]['area'] = area annotations[-1]['num_keypoints'] = kp_numsegmentation被写成单层 list 的扁平化坐标数组[[x1, y1, x2, y2, ...]],符合 COCO RLE 之外的多边形表示习惯。
3.5 关键点(KeyPointLabels)处理与实例聚合
关键点是实例的"起点":当遇到第一条关键点标注(i == 0)或上一条标注不是关键点类型时,创建一个新的 COCO annotation;否则将当前关键点追加到上一条 annotation 的keypoints数组中:
elif 'keypointlabels' == label['type']: x = label['value']['x'] * label['original_width'] / 100 y = label['value']['y'] * label['original_height'] / 100 # there is no method to annotate visible in Label Studio # so the keypoints' visible code will be 2 except (0,0) if x == y == 0: current_kp = [x, y, 0] kp_num_change = 0 else: current_kp = [x, y, 2] kp_num_change = 1 # create new annotation in coco # when the keypoint is the first point of an instance if i == 0 or item['annotations'][0]['result'][ i - 1]['type'] != 'keypointlabels': annotations.append({ 'id': annotation_id, 'image_id': image_id, 'category_id': category_id, 'keypoints': current_kp, 'ignore': 0, 'iscrowd': 0, }) kp_num = kp_num_change else: annotations[-1]['keypoints'].extend(current_kp) kp_num += kp_num_change这里有一个值得注意的细节:Label Studio 没有提供标注关键点可见性(visible)的方法,因此脚本将所有关键点的可见性码(visible flag)设为2(即"已标注且可见"),唯一例外是坐标恰为(0, 0)的关键点会被视为不存在,可见性码设为0且不计数。这也是原始文档未展开、但阅读源码可以获得的实现事实。
3.6 输出 JSON 结构
最终通过json.dump(..., indent=2)写出包含images、categories、annotations、info四个顶层字段的 COCO JSON,其中info记录年份、版本与日期等元信息。
四、常见问题与注意事项
- 关键点顺序错误:这是最常见的问题。转换脚本不会对关键点做任何重排,仅按标注顺序原样写入。若标注顺序与
dataset_info不一致,训练时关键点将与语义标签错位。建议标注前先打印dataset_info.keypoint_info核对顺序。 - 必须同时标注关键点与框/多边形:每个实例以关键点开始、以非关键点结束。若某实例缺少 PolygonLabels 或 RectangleLabels,其
bbox、area、segmentation字段将缺失,影响训练与评估。 - 文件名截断问题:Label Studio 会截断过长的上传文件名,导致图片路径与标注无法对应,建议使用 Export 中的 COCO 导出功能(压缩包内含图片文件夹)。
- 可见性标记的局限:由于 Label Studio 无法标注关键点可见性,转换结果中所有关键点可见性码均为 2(除 (0,0) 外)。若你的任务对遮挡/可见性敏感,需在转换后自行后处理标注文件。
- 类别 id 从 1 开始:转换脚本为每个
KeyPointLabels下的 Label 按顺序分配从 1 开始的 category id,与部分 COCO 数据集从 0 或 1 起始的习惯不同,接入自定义评估代码时需注意对齐。
五、延伸阅读
- 完整的标注工具使用流程可参考 dataset_tools.md,其中还包含 Browse Dataset、MIM 下载开源数据集、以及其他数据集格式转换脚本的说明;
- 若要了解 MMPose 数据集类如何加载 COCO 标注,可阅读 base_coco_style_dataset.py 的
load_data_list与parse_data_info实现; - 标准 COCO 人体关键点骨架定义见 configs/base/datasets/coco.py;
- 转换脚本 labelstudio2coco.py 基于 label-studio-converter 改写,遵循 Apache 2.0 许可(见脚本头部注释)。
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考