图像标注
在 Potato 中配置图像标注,支持边界框、多边形、掩码和关键点。用 instance_display 显示图像,并导出为 COCO 或 YOLO 格式。
Potato 支持图像标注,涵盖分类、目标检测、分割和关键点任务。
显示图像
显示图像和收集标注是两项彼此独立的设置。instance_display 决定标注者看到什么,annotation_schemes 决定他们回答什么。把两者分开,任何内容类型就都可以和任何问题类型搭配:
instance_display:
fields:
- key: image_url # Field name in your data file
type: image
label: "Image to classify"
display_options:
max_width: 600
zoomable: trueimage_annotation 方案是用来在图像上绘制的,不是用来显示图像的。分类任务只需要上面的显示字段,再加一个 radio 或 multiselect 方案。
显示选项
| 选项 | 说明 |
|---|---|
max_width | 渲染的最大宽度,单位为像素 |
max_height | 渲染的最大高度,单位为像素 |
zoomable | 启用点击放大 |
object_fit | 图像如何填充它的容器 |
alt_text | 供屏幕阅读器使用的替代文本 |
Potato 不认识的选项名会被当成配置错误,而不是被静默忽略,报错信息还会提示最接近的真实选项名。
数据格式
在数据文件中引用图像:
{"id": "img_001", "image_url": "https://example.com/photo_001.jpg"}
{"id": "img_002", "image_url": "/static/images/photo_002.png"}data_files 是一个路径列表,item_properties 把你的字段名映射到 Potato 读取的字段名上:
data_files:
- "data/image_tasks.json"
item_properties:
id_key: id
text_key: image_url # The field holding the image URL图像分类
对整张图像进行分类:
annotation_schemes:
- annotation_type: radio
name: category
description: "What is shown in this image?"
labels:
- Cat
- Dog
- Bird
- Other
- annotation_type: multiselect
name: attributes
description: "Select all that apply"
labels:
- Indoor
- Outdoor
- Multiple animals
- Human present多标签分类
annotation_schemes:
- annotation_type: multiselect
name: objects
description: "What objects are visible?"
labels:
- Person
- Car
- Building
- Tree
- Animal
- Furniture
- Food
- Electronic device图像质量评估
annotation_schemes:
- annotation_type: likert
name: quality
description: "Overall image quality"
size: 5
min_label: "Very poor"
max_label: "Excellent"
- annotation_type: multiselect
name: issues
description: "Select any quality issues"
labels:
- Blurry
- Overexposed
- Underexposed
- Noisy
- Low resolution
- Watermark visible在图像上绘制
image_annotation 方案必须同时提供 tools 和 labels。为每个标签指定颜色和 key_value 值得多写几行,因为标注者用按键分配标签比用鼠标点击快得多:
annotation_schemes:
- annotation_type: image_annotation
name: object_detection
description: "Draw boxes around objects"
tools:
- bbox
- polygon
labels:
- name: person
color: "#FF6B6B"
key_value: "1"
- name: vehicle
color: "#4ECDC4"
key_value: "2"
- name: animal
color: "#45B7D1"
key_value: "3"
min_annotations: 1
zoom_enabled: true
pan_enabled: truecolor 必须是十六进制(#rgb、#rrggbb 或 #rrggbbaa)。CSS 颜色名和 rgb() 不会被读取:形状看上去没问题,但掩码是逐像素绘制的,会退回成红色。
可用工具
| 工具 | 说明 |
|---|---|
bbox | 矩形边界框 |
polygon | 多点闭合多边形 |
polyline | 多点开放路径,用于车道线、血管、裂缝、海岸线 |
ellipse | 椭圆和圆,用于细胞、细胞核、车轮、人脸 |
keypoint_set | 有序骨架,用于人体或动物姿态以及面部关键点 |
cuboid_2d | 投影到图像上的 3D 框,KITTI 风格 |
sam | 魔术棒:点一下物体,由模型完成分割 |
freeform | 自由手绘 |
landmark | 单点标记 |
brush | 用于像素级掩码的分割画笔 |
eraser | 擦除掩码区域 |
fill | 对封闭区域做漫水填充 |
多边形是闭合的,围出一块面积;折线是开放的,有长度但没有内部区域。这个区别在导出时有影响:折线会被写成 area: 0,永远不会被当作区域处理。
多边形标注
用于非矩形区域:
annotation_schemes:
- annotation_type: image_annotation
name: regions
description: "Outline regions of interest"
tools:
- polygon
labels:
- name: Building
- name: Road
- name: Vegetation
- name: Water分割掩码
做像素级的工作时,启用绘制类工具。掩码以游程编码存储,而不是逐像素数组,这正是整图掩码仍然很小的原因:
annotation_schemes:
- annotation_type: image_annotation
name: segmentation
description: "Paint regions using the brush tool"
tools:
- brush
- eraser
- fill
labels:
- name: road
color: "#45B7D1"
- name: sidewalk
color: "#96CEB4"
brush_size: 20
mask_opacity: 0.5
mask_mode: semanticmask_mode: semantic 会把同一类的所有笔画合并成一个区域。同类的两个物体必须分开时,改用 instance。
输出格式
标注存成一个扁平的 JSON 数组,所有形状和掩码都在同一个列表里。坐标相对图像归一化到 0 到 1 的范围,因此图像以不同尺寸提供时标注依然正确:
[
{
"type": "bbox",
"label": "person",
"color": "#FF6B6B",
"coordinates": {"x": 0.125, "y": 0.104, "width": 0.3125, "height": 0.625}
},
{
"type": "polygon",
"label": "vehicle",
"color": "#4ECDC4",
"coordinates": [
{"x": 0.02, "y": 0.04}, {"x": 0.20, "y": 0.04},
{"x": 0.20, "y": 0.21}, {"x": 0.02, "y": 0.21}
]
},
{
"type": "mask",
"label": "road",
"color": "#45B7D1",
"rle": {"counts": [12, 40, 8], "size": [480, 640]}
}
]几何字段的名字随类型而变:
| 类型 | 几何字段 | 形状 |
|---|---|---|
bbox | coordinates | {x, y, width, height},已归一化 |
polygon | coordinates | {x, y} 的列表,已归一化 |
landmark | coordinates | {x, y},已归一化 |
freeform | coordinates | {path, left, top, scaleX, scaleY} |
mask | rle | {counts: [ints], size: [height, width]} |
rle.size 是 [height, width],顺序就是如此。在 Python 里与这些结构互相转换时,请使用 potato.export.cv_utils 中的 normalize_annotation_object() 和 to_client_object(),不要直接读取字段。
形状的来源
被采纳的模型检测结果和手绘的框一旦存下来就别无二致,所以每个形状都记录自己的来源。没有它,用模型做预标注的项目就无法从自己的数据里算出自己的采纳率:
{
"type": "bbox",
"label": "person",
"coordinates": {"x": 0.125, "y": 0.104, "width": 0.3125, "height": 0.625},
"source": "ai",
"ai_model": "yolov8n",
"confidence": 0.82,
"edited": true
}| 字段 | 含义 |
|---|---|
source | human(在浏览器中绘制)、ai(模型提出、标注者采纳)、import(随数据一起导入) |
ai_model | 端点报告了模型名时,具体是哪个模型 |
confidence | 检测器为这个形状给出的分数 |
edited | 标注者后来移动、缩放或重新标注了一个本来不是自己画的形状 |
carried_over | 从上一张图像沿用过来,而不是在这张图像上画的 |
edited 区分的是原样采纳一个框,和采纳之后又做了修正。只有前者才算与模型一致。
审核已有标注
要审核已经存在的形状,应该导入它们,而不是把它们塞进某个数据字段。potato import 会把一个 COCO 文件变成可直接运行的项目,并给每个形状打上 source: import:
potato import --input instances_val2017.json \
--image-dir /data/val2017 \
--image-url-prefix /files \
--output-dir my-project/ \
--schema-name object_detection
potato start my-project/config.yaml -p 8000标注者随后就地修正导入的形状,导出时人工与模型的划分在这一个来回中得以保留。
图像比较
用 pairwise 方案比较两张图像。items_key 指明存放这一对图像的数据字段:
annotation_schemes:
- annotation_type: pairwise
name: preference
description: "Which image looks better?"
mode: binary
items_key: images
show_labels: true
labels:
- "Original"
- "Edited"
allow_tie: true
tie_label: "About the same"图像描述
annotation_schemes:
- annotation_type: text
name: caption
description: "Write a caption for this image"
rows: 4
placeholder: "Describe what you see..."描述质量审核
把图像和生成的描述并排显示,然后给描述评分:
instance_display:
fields:
- key: image_url
type: image
label: "Image"
- key: generated_caption
type: text
label: "Generated caption"
annotation_schemes:
- annotation_type: likert
name: accuracy
description: "How accurate is this caption?"
size: 5
min_label: "Very inaccurate"
max_label: "Very accurate"
- annotation_type: likert
name: fluency
description: "How natural is the language?"
size: 5
min_label: "Very awkward"
max_label: "Very natural"
- annotation_type: text
name: improved_caption
description: "Suggest a better caption (optional)"
rows: 4
label_requirement:
required: false内容审核
annotation_schemes:
- annotation_type: radio
name: safe_for_work
description: "Is this image safe for work?"
labels:
- Safe
- Questionable
- Not Safe
- annotation_type: multiselect
name: violation_types
description: "Select all violations (if any)"
labels:
- Violence
- Adult content
- Hate symbols
- Graphic content
- Spam/advertisement交换格式
Potato 能读取 COCO JSON,包括其中所有的分割编码和 crowd 区域,并能写出 COCO、YOLO、Pascal VOC 以及按标签分开的掩码 PNG:
python -m potato.export -c my-project/config.yaml -f coco -o ./export/YOLO 和 Pascal VOC 只承载边界框,因此其他形状会被简化成它们的外接框。
完整示例:目标检测审核
annotation_task_name: "Object Detection Verification"
task_dir: "."
data_files:
- "data/detections.json"
item_properties:
id_key: id
text_key: image_url
instance_display:
fields:
- key: image_url
type: image
label: "Image"
display_options:
max_width: 1000
zoomable: true
annotation_schemes:
# Correct the boxes
- annotation_type: image_annotation
name: objects
description: "Verify and correct object boxes"
tools:
- bbox
labels:
- name: Person
key_value: "1"
- name: Vehicle
key_value: "2"
- name: Animal
key_value: "3"
- name: Object
key_value: "4"
zoom_enabled: true
pan_enabled: true
# Overall assessment
- annotation_type: radio
name: prediction_quality
description: "How accurate were the predictions?"
labels:
- All correct
- Minor corrections needed
- Major corrections needed
- Mostly incorrect
- annotation_type: number
name: missed_objects
description: "How many objects were missed?"
min: 0
max: 50
- annotation_type: text
name: notes
description: "Any issues or comments?"
rows: 4
label_requirement:
required: false
output_annotation_dir: "output/"
export_annotation_format: jsonl
user_config:
allow_all_users: true启动服务器:
potato start config.yaml -p 8000性能提示
- 标注前先缩放 - 过大的源图像会拖慢加载,对标注本身却没有帮助
- 照片用 JPEG - 文件更小,加载更快
- 图形用 PNG - 图表和截图的质量更好
- 限制渲染尺寸 - 设置
max_width,免得浏览器在每个实例上缩放一张巨图 - 统一预处理 - 在整个数据集上统一尺寸和格式