Label Studio 标注结果格式(Annotation Result Format)完全指南:region、result ID 与 value 结构详解
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
Label Studio 将标注工作区中的所有操作——画框、打标签、建关系、做分类——统一编码为存储在annotation.result字段中的结果(results)列表。本文围绕该结果格式,系统讲解 region 与 result 的概念、ID 生成与关联规则、格式推断原则、预测转标注的 ID 保持机制,并结合仓库源码与四个实战示例(per-region、可选标签、多标签、关系标注)展示如何阅读、编写和校验标注结果。
核心概念:annotation.result、Regions 与 Results
每次标注(annotation)都对应任务中的一个完整标注成果。在 Label Studio 中,标注数据以 JSON 列表的形式保存在标注对象的result字段下,列表中的每一项被称为一个region:
{"result": [{"id": "123", ...}, {"id": "456", ...}], ...}在概念上需要区分两个层级:
- Regions(区域):指被选中的数据范围,可以是文本中的一段 span、图片中的一块区域、音频中的一段波形或任意一个实体,例如绘制出的边界框(bounding box)、选中的文本片段等。
- Results(结果):指赋给该 region 的标签或取值,例如矩形框上标注的类别、某段文本的关系标签等。
Region 可以代表任何标注动作——绘制的边界框、创建的 relation、分配的类别等等,因此它并不仅限于"框选"这一种形态。一次标注的结果列表可以包含多个 region,例如一张图中框出三个物体,就会产生三个带各自 ID 的 region。
从数据模型看,result字段在 label_studio/tasks/models.py 中被定义为JSONField,其help_text明确写着"标注者工作的主要价值——JSON 格式的标注结果"。同时模型还维护了result_count字段(label_studio/tasks/models.py),它统计的是去重后的 region ID 数量(label_studio/tasks/models.py),而非结果条目的总数——这正体现了"同一 region 的多条 result 共享一个 ID"这一核心规则。
result ID 的生成与关联规则
每一个result条目中的"id"字段都是一个字符串,只允许使用A-Za-z0-9_-这些字符。ID 的分配遵循标注结构:
- 每个独立的标注实体获得一个专属 ID。例如一个边界框是一个 ID,一条关系又是一个 ID;
- 同一 ID 用于把不同实体关联起来。例如用
<Relation>标签把两个 region 关联成一条关系,或通过"perRegion"属性让某个控制标签只作用于特定 region。
这种"同 ID 复用"机制是理解整个结果格式的关键:针对同一个 region 的所有 result 条目共享同一个 ID。例如某个矩形框(一个 region)既有绘制结果又有标签结果,那么这两条 result 的id字段完全一致,但通过from_name区分它们各自来自哪个控制标签。
正因如此,后端在写入结果时需要处理"同一 ID 下多个条目"的合法性。在 label_studio/tasks/result_utils.py 中,dedupe_annotation_result_list以(id, from_name, type)三元组为键进行去重:同一 region 下,来自不同控制标签(from_name不同)的 result 会保留;只有三者完全相同(即客户端重复提交的同一条 result)才会被折叠为第一条。该工具同时被序列化器校验与annotation_history快照写入共用,保证Annotation.result中永远不落地重复键行。
结果格式的推断原则
Label Studio 的结果格式不是随意生成的,而是由标注配置(labeling config)的标签结构按以下原则推断而来:
- 配置中必须至少有 1 个 object 标签(如
<Image>、<Text>、<Audio>等),它是数据类型的来源,决定了数据是图片、HTML、视频还是其他类型; - 至少 1 个 control 标签需要挂接到 object 标签上(通过
toName指向 object 的name),才能在该 object 上创建 region; - 每个 control 标签为每个 region 产生 1 条 result。也就是说,一个 region 上的结果条数 = 作用于该 region 的控制标签数量;
- 针对同一 region 的 result 共享同一个 ID;
- 分类(classification)在技术上会创建一个特殊的空分类 region——即便只是对整个任务做一次全局分类而没有绘制任何几何区域,也会以空 region 的形式记录一条 result,从而维持"每个 control 标签都有结果"的格式一致性。
这套推断逻辑保证了结果的形状完全由配置驱动:配置中加了几个perRegion控制标签,每个 region 就对应几条 result;没有挂接控制标签的 object 则不会产生 region。
预测转标注时的 ID 保持机制
当用一条预测(prediction)来创建标注(annotation)时,结果中的 ID 会在标注字段中原样保留。这意味着你可以:
- 追踪由机器学习模型生成的 region,知道每个 ID 对应的预测来自哪里;
- 把模型预测的结果与人工创建、人工复核的标注结果按 ID 直接一一对比,评估模型质量或做主动学习。
从源码看,预测结果在写入前会经过规范化处理。Prediction.prepare_prediction_result(label_studio/tasks/models.py)支持三种输入形态:list直接作为完整结果列表使用;dict会被包装进单个"value"段;字符串或整数等标量则会匹配到单值控制标签(如 Choices、Rating)并放入对应的单值字段(如"choices": ["my_label"])。无论哪种形态,只要最终是完整的 result 列表,其中的 ID 都会保持原样进入 annotation。
"value"字段:结构随标签类型变化
"value"字段表示标注过程的结果(outcome),它的内部结构取决于标注配置中具体使用了哪个标签:
- 矩形框标签产生坐标与尺寸(如
x、y、width、height); - 标签类标签产生
labels数组(如"labels": ["Tea"]); - 文本区产生
text数组; - 数字标签产生
number值,依此类推。
要了解具体某个标签的 value 结构,请查阅对应的 Control tags 文档,找到相关标签并查看其示例与参数。例如perRegion参数在 Choices、TextArea、Number、Rating、Taxonomy、Datetime 等控制标签上均有定义,用于"针对某个 region 而非整个任务"做标注。
实战示例:四种典型结果结构
下面四个示例覆盖了最常见的 result 组合形态,均以图片标注为例。注意示例 JSON 中的// ...表示省略的其它字段(如坐标、尺寸、置信度等),实际数据中是完整字段。
1. per-region 条件标注
当控制标签设置了条件属性perRegion="true"时,该标签的结果会附着到具体 region 上。以下配置要求对矩形框做必须的类别标注,并可选地补充一段 per-region 文本:
<Image name="image" value="$image"/> <RectangleLabels name="product" toName="image"> <Label value="Some label" /> ... </RectangleLabels> <TextArea name="name" toName="image" perRegion="true" />这样一个 region 会对应 1~2 条 result:必须的类别标注 + 可选的 per-region 文本。两条 result 共享同一个"id",通过from_name区分来源:
[{ "id": "X_12fGk", "from_name": "product", "to_name": "image", "type": "rectanglelabels", // ... "value": { "labels": ["Some label"], // ... } }, { "id": "X_12fGk", "from_name": "name", "to_name": "image", "type": "textarea", // ... "value": { "text": ["Roasted beans"], // ... } }]2. 可选标签(optional labels)
绘制工具与可选标签分离:<Rectangle>只负责画框(不要求必须给标签),而<Labels>作为可选的附加分类。一个 region 会产生 1~2 条 result——必选的绘制结果 + 可选的标签结果:
<Image name="image" value="$image"/> <Rectangle name="product" toName="image" /> <Labels name="kind" toName="image"> <Label value="Tea" /> <Label value="Coffee" /> </Labels>[{ "id": "X_12fGk", "from_name": "product", "to_name": "image", "type": "rectangle", // ... "value": { "x": 100, "y": 200, // ... } }, { "id": "X_12fGk", "from_name": "kind", "to_name": "image", "type": "labels", // ... "value": { "labels": ["Tea"], // ... } }]注意:由于<Rectangle>不强制标签、<Labels>也不强制必选,实际保存时可能只有绘制结果一条,也可能两条都有,但 ID 始终一致。
3. 多标签组合(multi-labels)
在同一个 object 上挂接多个可选标签和一个必选 per-region 数字标签:
<Image name="image" value="$image"/> <Rectangle name="product" toName="image" /> <Labels name="kind" toName="image"> <Label value="Tea" /> <Label value="Coffee" /> </Labels> <Labels name="country" toName="image"> <Label value="Sri-Lanka" /> <Label value="Brazil" /> </Labels> <Number name="price" toName="image" perRegion="true" required="true" />此时一个 region 会产生 1~4 条 result:矩形框本身 + 两个可选的标签结果(kind、country)+ 必选的数字结果(price)。所有 result 共享同一"id":
[{ "id": "X_12fGk", "from_name": "product", "to_name": "image", "type": "rectangle", // ... "value": { // rectangle sizes } }, { "id": "X_12fGk", "from_name": "kind", "to_name": "image", "type": "labels", // ... "value": { "labels": ["Coffee"], // ... } }, { "id": "X_12fGk", "from_name": "country", "to_name": "image", "type": "labels", // ... "value": { "labels": ["Brazil"], // ... } }, { "id": "X_12fGk", "from_name": "price", "to_name": "image", "type": "labels", // ... "value": { "number": 12.5, // ... } }]这里price是required="true"的 per-region 标签,因此只要画了框就必然有 4 条 result;若kind或country未选择则对应条目省略,结果数相应减少。
4. 关系标注(relations)
可以在两个 region 之间绘制关系箭头。例如一个目标检测配置,用<RectangleLabels>标注Car与Airplaine两类目标:
<Image name="image" value="$image"/> <RectangleLabels name="kind" toName="image"> <Label value="Car" /> <Label value="Airplaine" /> </RectangleLabels>标注两个物体并建立关系后,结果列表包含两条rectanglelabels结果和一条relation结果。关系条目不包含from_name/to_name/value,而是通过to_id、from_id引用两端的 region ID,并用direction描述箭头方向:
[{ "id": "oid67", "type": "rectanglelabels", // ... }, { "id": "RQbW3Sj_Zr", "type": "rectanglelabels", // ... }, { "type": "relation", "to_id": "RQbW3Sj_Zr", "from_id": "oid66", "direction": "right" }]关系本身就是一种 region(拥有独立实体),因此relation条目也可以有自己的id,同时通过from_id/to_id与参与关系的两个 region 关联。若要给关系赋予语义标签(如 "similar"),可在配置中加入<Relations>+<Relation>标签,参见 Relations 标签文档 与 Relation 标签文档。
源码视角:result 的存储、校验与清洗
理解格式之后,再从后端源码看结果数据是如何被落库与保护的,有助于排查数据问题:
- 存储:
Annotation.result为JSONField(见 label_studio/tasks/models.py),标注的"主要价值"即在此字段;result_count用于记录去重后的 region 数。草稿(Draft)与预测(Prediction)同样各有自己的 result JSONField。 - 校验:序列化器层(label_studio/tasks/serializers.py)要求
result必须是 JSON 可解析且为列表类型,随后调用dedupe_annotation_result_list做去重,并在保存前通过custom_interface_validator按项目配置校验结果结构是否符合标签定义(label_studio/tasks/serializers.py)。 - 清洗:label_studio/tasks/result_utils.py 中的
sanitize_null_bytes会剔除结果中的 NUL 字节(\u0000)——PostgreSQL 的jsonb列无法存储该字符,常见于从 PDF 的 OCR/文本层复制内容进 result 的场景,剔除后可避免写入时抛DataError。
总结
Label Studio 的标注结果格式遵循一套简洁而一致的规则:所有标注操作都被编码为annotation.result下的 region 列表;每个独立标注实体拥有由A-Za-z0-9_-组成的 ID;同一 region 的多个结果共享 ID,靠from_name/type区分;结果的具体数量与value结构完全由标注配置中的 control 标签决定,并遵循"1 个 object 标签 + 1 个以上 control 标签 + 每个 control 标签产生 1 条 result + 分类产生空 region"的推断原则。掌握这套格式,你就能直接阅读、生成与校验 API 返回的标注数据,也能让模型预测结果与人工标注在 ID 层面实现一一对照。
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考