Label Studio 图像分割自动标注接入教程:从模型预测到一键上传 Predictions
本文以通用图像分割任务为例,介绍如何启动 Label Studio、配置 Brush 标注界面、导入图片、运行模型生成 mask,并通过 API 将自动标注结果上传为 Label Studio Predictions。文中的路径、类别名、模型名和项目 ID 都是占位示例,实际使用时请替换成自己的配置。
一、整体流程
自动标注的推荐流程如下:
- 启动 Label Studio。
- 新建图像标注项目。
- 配置 BrushLabels 分割标注界面。
- 导入待标注图片。
- 使用训练好的模型对图片生成预测 mask。
- 将预测 mask 转换为 Label Studio brush result。
- 通过 Label Studio API 上传为 Predictions。
- 人工在页面中检查、修改并提交正式标注。
推荐把自动结果上传为 Predictions,不要一开始就直接写入 Annotations。这样自动结果只是模型预测,人工可以逐张确认和修改,不会直接覆盖已有人工标注。
二、环境准备
1. 安装 Label Studio
如果还没有安装,可以使用 pip 安装:
pip install label-studio
如果使用 Conda,建议创建或激活自己的环境:
conda create -n labelstudio-env python=3.10
conda activate labelstudio-env
pip install label-studio
2. 安装模型推理依赖
下面是常见图像分割推理脚本需要的依赖:
pip install opencv-python numpy torch requests
如果你的模型不是 PyTorch,请替换为对应框架的依赖。
三、启动 Label Studio
进入你的工作目录:
cd /path/to/your_project
启动 Label Studio:
label-studio
部分版本也可以显式写成:
label-studio start
启动成功后,浏览器访问:
http://localhost:8080
如果要指定端口:
label-studio --port 8081
四、创建项目并配置标注界面
新建项目后,进入 Settings -> Labeling Interface,使用下面的 XML 配置。
注意:
Image的name需要和后续脚本的--to-name保持一致。BrushLabels的name需要和后续脚本的--from-name保持一致。Label value需要和模型输出的类别目录名保持一致。
<View>
<Image name="image" value="$image" zoom="true" zoomControl="true" rotateControl="true"/>
<BrushLabels name="tag" toName="image">
<Label value="label_a" background="#ff00ff"/>
<Label value="label_b" background="#00dcdc"/>
<Label value="label_c" background="#ff0000"/>
<Label value="label_d" background="#5050ff"/>
</BrushLabels>
</View>
如果你的任务只有两个类别,可以删掉多余的 Label。如果类别更多,继续添加即可。
五、导入图片
建议将待标注图片统一放到一个目录,例如:
data/images/
然后在 Label Studio 项目页面点击 Import,上传该目录中的图片。
导入完成后,进入项目数据页,浏览器地址通常类似:
http://localhost:8080/projects/<project_id>/data
其中 <project_id> 就是后续 API 脚本要用的项目 ID。
六、获取 API Token
进入右上角用户菜单:
Account & Settings -> Access Token
复制 Personal Access Token。
后续示例中统一用:
<your_token>
作为占位符。
新版 Personal Access Token 通常使用:
--token-type pat
旧版固定 Token 可以使用:
--token-type legacy
七、推荐目录结构
建议使用下面的通用目录结构:
your_project/
data/
images/
image_001.jpg
image_002.jpg
models/
model.pt
scripts/
predict_multilabel_unet.py
labelstudio_api_predictions.py
make_labelstudio_multilabel_results.py
outputs/
predictions/
其中:
data/images/:待标注图片。models/model.pt:训练好的分割模型。outputs/predictions/:模型预测输出。scripts/labelstudio_api_predictions.py:读取 Label Studio tasks,并上传 Predictions。
八、运行模型生成自动 mask
下面示例假设模型推理脚本会为每张图片输出:
outputs/predictions/
masks_binary/
label_a/
image_001.png
image_002.png
label_b/
image_001.png
image_002.png
masks_semantic/
overlays/
summary.csv
其中 masks_binary/<label_name>/<image_stem>.png 是每个类别对应的二值 mask。
运行示例:
python scripts/predict_multilabel_unet.py \
--model models/model.pt \
--images-dir data/images \
--out-dir outputs/predictions \
--thresholds "label_a=0.50,label_b=0.50,label_c=0.50,label_d=0.50" \
--prob-upsample
Windows PowerShell 写法:
python scripts\predict_multilabel_unet.py `
--model models\model.pt `
--images-dir data\images `
--out-dir outputs\predictions `
--thresholds "label_a=0.50,label_b=0.50,label_c=0.50,label_d=0.50" `
--prob-upsample
参数说明:
| 参数 | 说明 |
|---|---|
--model |
模型权重路径 |
--images-dir |
待预测图片目录 |
--out-dir |
预测输出目录 |
--thresholds |
每个类别的二值化阈值 |
--prob-upsample |
先放大概率图再二值化,边界通常更平滑 |
九、上传为 Label Studio Predictions
1. 先 dry-run
第一次建议不要直接上传,先生成本地 JSON 文件检查结果。
python scripts/labelstudio_api_predictions.py \
--base-url http://localhost:8080 \
--project-id <project_id> \
--token "<your_token>" \
--token-type pat \
--pred-dir outputs/predictions \
--tasks-out outputs/labelstudio_tasks.json \
--predictions-out outputs/labelstudio_predictions.json \
--from-name tag \
--to-name image \
--model-version auto-segmentation-v1 \
--score 0.80 \
--min-pixels 20
Windows PowerShell 写法:
python scripts\labelstudio_api_predictions.py `
--base-url http://localhost:8080 `
--project-id <project_id> `
--token "<your_token>" `
--token-type pat `
--pred-dir outputs\predictions `
--tasks-out outputs\labelstudio_tasks.json `
--predictions-out outputs\labelstudio_predictions.json `
--from-name tag `
--to-name image `
--model-version auto-segmentation-v1 `
--score 0.80 `
--min-pixels 20
如果输出中能看到类似信息,说明本地转换成功:
generated predictions=<prediction_count>
dry-run: not uploading predictions
2. 正式上传
确认 JSON 没问题后,加上 --upload:
python scripts/labelstudio_api_predictions.py \
--base-url http://localhost:8080 \
--project-id <project_id> \
--token "<your_token>" \
--token-type pat \
--pred-dir outputs/predictions \
--tasks-out outputs/labelstudio_tasks.json \
--predictions-out outputs/labelstudio_predictions.json \
--from-name tag \
--to-name image \
--model-version auto-segmentation-v1 \
--score 0.80 \
--min-pixels 20 \
--upload
上传完成后,刷新 Label Studio 项目页面,打开任务,应该能看到模型预测的 brush mask。
十、核心 Python 代码示例
下面给出一个精简版上传脚本,演示完整思路:拉取项目 tasks、根据图片文件名匹配 mask、构造 Prediction payload、调用 API 上传。
实际生产中建议增加日志、异常重试、分页处理和结果校验。
import base64
import json
from pathlib import Path
import cv2
import numpy as np
import requests
BASE_URL = "http://localhost:8080"
PROJECT_ID = "<project_id>"
TOKEN = "<your_token>"
PRED_DIR = Path("outputs/predictions")
FROM_NAME = "tag"
TO_NAME = "image"
MODEL_VERSION = "auto-segmentation-v1"
LABELS = ["label_a", "label_b", "label_c", "label_d"]
def request_json(method, url, token, **kwargs):
headers = kwargs.pop("headers", {})
headers["Authorization"] = f"Token {token}"
response = requests.request(method, url, headers=headers, **kwargs)
response.raise_for_status()
if response.text:
return response.json()
return None
def fetch_tasks():
url = f"{BASE_URL}/api/projects/{PROJECT_ID}/tasks/"
return request_json("GET", url, TOKEN)
def image_stem_from_task(task):
image_url = task["data"]["image"]
filename = image_url.split("/")[-1]
return Path(filename).stem
def mask_to_brush_result(mask_path, label_name, original_width, original_height):
mask = cv2.imread(str(mask_path), cv2.IMREAD_GRAYSCALE)
if mask is None:
return None
mask = (mask > 0).astype(np.uint8) * 255
if int(mask.sum()) == 0:
return None
success, encoded = cv2.imencode(".png", mask)
if not success:
return None
png_base64 = base64.b64encode(encoded.tobytes()).decode("ascii")
return {
"from_name": FROM_NAME,
"to_name": TO_NAME,
"type": "brushlabels",
"origin": "prediction",
"value": {
"format": "brushlabels",
"brushlabels": [label_name],
"rle": png_base64,
"width": original_width,
"height": original_height,
},
}
def build_prediction_for_task(task):
stem = image_stem_from_task(task)
results = []
# 如果能从 task 里拿到真实宽高,建议使用真实宽高。
# 这里为了示例,使用任务数据中的 width/height 字段;没有时需要自行读取原图尺寸。
width = task["data"].get("width")
height = task["data"].get("height")
if not width or not height:
raise ValueError("task data must contain width and height, or read them from local images")
for label_name in LABELS:
mask_path = PRED_DIR / "masks_binary" / label_name / f"{stem}.png"
if not mask_path.exists():
continue
result = mask_to_brush_result(mask_path, label_name, width, height)
if result:
results.append(result)
if not results:
return None
return {
"task": task["id"],
"model_version": MODEL_VERSION,
"score": 0.8,
"result": results,
}
def upload_prediction(prediction):
url = f"{BASE_URL}/api/predictions/"
return request_json("POST", url, TOKEN, json=prediction)
def main():
tasks = fetch_tasks()
predictions = []
for task in tasks:
pred = build_prediction_for_task(task)
if pred:
predictions.append(pred)
Path("outputs/labelstudio_predictions_preview.json").write_text(
json.dumps(predictions, ensure_ascii=False, indent=2),
encoding="utf-8",
)
for pred in predictions:
upload_prediction(pred)
print(f"uploaded predictions: {len(predictions)}")
if __name__ == "__main__":
main()
注意:Label Studio brush result 在不同版本中对 mask 编码格式有差异。正式项目中建议优先复用已经验证过的转换脚本,确保生成的
rle、width、height、from_name、to_name与当前 Label Studio 版本一致。
十一、如果要生成 Annotations
一般不建议第一步就把自动结果写成正式 Annotations。如果确实需要,可以先把预测结果转换成 annotations payload:
python scripts/make_labelstudio_multilabel_results.py \
--tasks-json outputs/labelstudio_tasks.json \
--pred-dir outputs/predictions \
--out outputs/labelstudio_annotations.json \
--format annotations-api \
--start-id <start_task_id> \
--end-id <end_task_id> \
--from-name tag \
--to-name image \
--model-version auto-segmentation-v1 \
--score 0.80 \
--min-pixels 20
然后再通过替换脚本进行 dry-run:
python scripts/labelstudio_replace_annotations.py \
--base-url http://localhost:8080 \
--project-id <project_id> \
--token "<your_token>" \
--token-type pat \
--annotations-json outputs/labelstudio_annotations.json \
--start-id <start_task_id> \
--end-id <end_task_id> \
--plan-out outputs/replace_plan.json
确认 replace_plan.json 后,再追加 --execute 执行。
十二、常见问题
1. 页面里看不到自动 mask
重点检查:
- XML 中
BrushLabels name是否等于脚本的--from-name。 - XML 中
Image name是否等于脚本的--to-name。 Label value是否和masks_binary/<label_name>/目录名一致。- mask 文件名是否和任务图片名能匹配。
- mask 是否为空,是否低于
--min-pixels。
2. API 返回 401
常见原因:
- Token 复制错误。
- Personal Access Token 和 legacy token 类型混用。
- 当前版本需要先用 Personal Access Token 换取短期 Bearer token。
可以先确认 token 类型:
python scripts/labelstudio_api_predictions.py \
--base-url http://localhost:8080 \
--project-id <project_id> \
--token "<your_token>" \
--token-type pat \
--pred-dir outputs/predictions
3. 图片文件名匹配不上
Label Studio 上传本地图片后,URL 中可能带有额外前缀。脚本中建议做文件名归一化,例如:
from pathlib import Path
def normalize_name(image_url: str) -> str:
filename = image_url.split("/")[-1]
stem = Path(filename).stem
# 如果你的系统会给文件名前面加 UUID,可以在这里去掉前缀。
return stem
4. 已有人工标注怎么办
建议上传 Predictions 时默认跳过已有人工标注的任务。人工标注通常优先级更高,不建议自动结果直接覆盖。
如果确实需要重新上传预测,可以增加类似参数:
--include-existing-predictions
5. 阈值怎么调
阈值太低会产生很多误检,阈值太高会漏掉目标。建议先对少量图片生成 overlays/ 可视化图,人工查看后再批量处理。
示例:
python scripts/predict_multilabel_unet.py \
--model models/model.pt \
--images-dir data/images \
--out-dir outputs/predictions_v2 \
--thresholds "label_a=0.60,label_b=0.55,label_c=0.70,label_d=0.50" \
--prob-upsample
十三、发布前检查清单
正式批量上传前,建议确认:
- Label Studio 项目能正常打开。
- 项目 ID 正确。
- Token 有效。
- 标注 XML 中的
from_name和to_name与脚本一致。 - 图片已全部导入 Label Studio。
- 本地预测目录存在
masks_binary/<label_name>/xxx.png。 - dry-run 生成的 JSON 数量符合预期。
- 随机抽查几张任务,mask 位置和类别正确。
- 确认上传为 Predictions,而不是直接覆盖 Annotations。
十四、总结
Label Studio 自动标注的关键不是“把模型跑起来”这么简单,而是要保证三组信息完全一致:
- Label Studio 标注配置中的
from_name、to_name、Label value。 - 模型输出目录中的类别名和 mask 文件名。
- API payload 中的任务 ID、图片尺寸、brush result 格式。
建议实践顺序是:先少量图片验证,再 dry-run 生成 JSON,最后批量上传 Predictions。这样既能利用模型提高效率,又能保留人工审核和修正的空间。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)