Skip to content

图像标注

在 Potato 中配置图像标注,支持边界框、多边形、掩码和关键点。用 instance_display 显示图像,并导出为 COCO 或 YOLO 格式。

Potato 支持图像标注,涵盖分类、目标检测、分割和关键点任务。

显示图像

显示图像和收集标注是两项彼此独立的设置。instance_display 决定标注者看到什么,annotation_schemes 决定他们回答什么。把两者分开,任何内容类型就都可以和任何问题类型搭配:

yaml
instance_display:
  fields:
    - key: image_url          # Field name in your data file
      type: image
      label: "Image to classify"
      display_options:
        max_width: 600
        zoomable: true

image_annotation 方案是用来在图像上绘制的,不是用来显示图像的。分类任务只需要上面的显示字段,再加一个 radio 或 multiselect 方案。

显示选项

选项说明
max_width渲染的最大宽度,单位为像素
max_height渲染的最大高度,单位为像素
zoomable启用点击放大
object_fit图像如何填充它的容器
alt_text供屏幕阅读器使用的替代文本

Potato 不认识的选项名会被当成配置错误,而不是被静默忽略,报错信息还会提示最接近的真实选项名。

数据格式

在数据文件中引用图像:

json
{"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 读取的字段名上:

yaml
data_files:
  - "data/image_tasks.json"
 
item_properties:
  id_key: id
  text_key: image_url   # The field holding the image URL

图像分类

对整张图像进行分类:

yaml
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

多标签分类

yaml
annotation_schemes:
  - annotation_type: multiselect
    name: objects
    description: "What objects are visible?"
    labels:
      - Person
      - Car
      - Building
      - Tree
      - Animal
      - Furniture
      - Food
      - Electronic device

图像质量评估

yaml
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 值得多写几行,因为标注者用按键分配标签比用鼠标点击快得多:

yaml
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: true

color 必须是十六进制(#rgb、#rrggbb 或 #rrggbbaa)。CSS 颜色名和 rgb() 不会被读取:形状看上去没问题,但掩码是逐像素绘制的,会退回成红色。

可用工具

工具说明
bbox矩形边界框
polygon多点闭合多边形
polyline多点开放路径,用于车道线、血管、裂缝、海岸线
ellipse椭圆和圆,用于细胞、细胞核、车轮、人脸
keypoint_set有序骨架,用于人体或动物姿态以及面部关键点
cuboid_2d投影到图像上的 3D 框,KITTI 风格
sam魔术棒:点一下物体,由模型完成分割
freeform自由手绘
landmark单点标记
brush用于像素级掩码的分割画笔
eraser擦除掩码区域
fill对封闭区域做漫水填充

多边形是闭合的,围出一块面积;折线是开放的,有长度但没有内部区域。这个区别在导出时有影响:折线会被写成 area: 0,永远不会被当作区域处理。

多边形标注

用于非矩形区域:

yaml
annotation_schemes:
  - annotation_type: image_annotation
    name: regions
    description: "Outline regions of interest"
    tools:
      - polygon
    labels:
      - name: Building
      - name: Road
      - name: Vegetation
      - name: Water

分割掩码

做像素级的工作时,启用绘制类工具。掩码以游程编码存储,而不是逐像素数组,这正是整图掩码仍然很小的原因:

yaml
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: semantic

mask_mode: semantic 会把同一类的所有笔画合并成一个区域。同类的两个物体必须分开时,改用 instance。

输出格式

标注存成一个扁平的 JSON 数组,所有形状和掩码都在同一个列表里。坐标相对图像归一化到 0 到 1 的范围,因此图像以不同尺寸提供时标注依然正确:

json
[
  {
    "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]}
  }
]

几何字段的名字随类型而变:

类型几何字段形状
bboxcoordinates{x, y, width, height},已归一化
polygoncoordinates{x, y} 的列表,已归一化
landmarkcoordinates{x, y},已归一化
freeformcoordinates{path, left, top, scaleX, scaleY}
maskrle{counts: [ints], size: [height, width]}

rle.size 是 [height, width],顺序就是如此。在 Python 里与这些结构互相转换时,请使用 potato.export.cv_utils 中的 normalize_annotation_object() 和 to_client_object(),不要直接读取字段。

形状的来源

被采纳的模型检测结果和手绘的框一旦存下来就别无二致,所以每个形状都记录自己的来源。没有它,用模型做预标注的项目就无法从自己的数据里算出自己的采纳率:

json
{
  "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
}
字段含义
sourcehuman(在浏览器中绘制)、ai(模型提出、标注者采纳)、import(随数据一起导入)
ai_model端点报告了模型名时,具体是哪个模型
confidence检测器为这个形状给出的分数
edited标注者后来移动、缩放或重新标注了一个本来不是自己画的形状
carried_over从上一张图像沿用过来,而不是在这张图像上画的

edited 区分的是原样采纳一个框,和采纳之后又做了修正。只有前者才算与模型一致。

审核已有标注

要审核已经存在的形状,应该导入它们,而不是把它们塞进某个数据字段。potato import 会把一个 COCO 文件变成可直接运行的项目,并给每个形状打上 source: import:

bash
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 指明存放这一对图像的数据字段:

yaml
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"

图像描述

yaml
annotation_schemes:
  - annotation_type: text
    name: caption
    description: "Write a caption for this image"
    rows: 4
    placeholder: "Describe what you see..."

描述质量审核

把图像和生成的描述并排显示,然后给描述评分:

yaml
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

内容审核

yaml
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:

bash
python -m potato.export -c my-project/config.yaml -f coco -o ./export/

YOLO 和 Pascal VOC 只承载边界框,因此其他形状会被简化成它们的外接框。

完整示例:目标检测审核

yaml
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

启动服务器:

bash
potato start config.yaml -p 8000

性能提示

  1. 标注前先缩放 - 过大的源图像会拖慢加载,对标注本身却没有帮助
  2. 照片用 JPEG - 文件更小,加载更快
  3. 图形用 PNG - 图表和截图的质量更好
  4. 限制渲染尺寸 - 设置 max_width,免得浏览器在每个实例上缩放一张巨图
  5. 统一预处理 - 在整个数据集上统一尺寸和格式