Skip to content
Tutorials7 Min. Lesezeit

Bounding Boxes für die Objekterkennung zeichnen

Baue in Potato eine Aufgabe zur Objekterkennung, von einer Startkonfiguration mit drei Labels bis zu einer Produktivkonfiguration für Fahraufnahmen, und exportiere nach COCO oder YOLO.

Potato Team

Dieses Tutorial baut in Potato eine Annotationsaufgabe zur Objekterkennung auf, von einer Konfiguration mit drei Labels bis zum COCO- oder YOLO-Export, auf dem ein Detektor trainiert wird. Die vollständige Liste der Bildwerkzeuge und ihrer Optionen steht in der Dokumentation zur Bildannotation.

Die Datendatei

Jede Zeile der Datendatei ist ein Bild. Potato braucht eine Kennung und ein Feld mit der URL oder dem Pfad des Bildes, und item_properties legt fest, welches Feld welches ist:

json
{"id": "img_001", "image_url": "https://example.com/street_001.jpg"}
{"id": "img_002", "image_url": "/static/images/street_002.jpg"}

Ein relativer Pfad wird gegen media_directory aufgelöst, lokale Bilder brauchen also keinen laufenden Dateiserver.

Eine minimale Konfiguration

Das Schema image_annotation stellt die Zeichenfläche bereit. Es braucht einen Namen, eine Beschreibung, die Annotierende als Anweisung lesen, die zu aktivierenden Werkzeuge und die Labelmenge:

yaml
annotation_task_name: "Object Detection Annotation"
task_dir: "."
output_annotation_dir: "annotation_output/"
 
data_files:
  - "data/images.json"
 
item_properties:
  id_key: id
  text_key: image_url
 
annotation_schemes:
  - annotation_type: image_annotation
    name: objects
    description: "Draw a box around every car, person, and bicycle"
    tools:
      - bbox
    labels:
      - name: car
        color: "#FF6B6B"
      - name: person
        color: "#4ECDC4"
      - name: bicycle
        color: "#45B7D1"

Drei Labels sind ein bewusst gewählter Anfang. Jedes zusätzliche Label ist eine Entscheidung, die Annotierende bei jedem Objekt treffen müssen, und ein Grenzfall, für den sie eine Regel brauchen. Fang deshalb mit den Klassen an, die das Modell tatsächlich braucht, und ergänze den Rest, sobald die Richtlinien tragen.

Starte den Server und öffne die Aufgabe:

bash
potato start config.yaml -p 8000

Was Annotierende tun

Annotierende wählen ein Label und ziehen dann um jedes Objekt ein Rechteck auf, dessen Ecken sie nachjustieren, bis die Box eng anliegt:

Oberfläche zur Bounding-Box-Annotation für ObjekterkennungMit dem Bounding-Box-Werkzeug zeichnen und labeln Annotierende rechteckige Bereiche auf Bildern

Zoomen und Verschieben sind standardmäßig aktiv, was bei kleinen oder weit entfernten Objekten wichtig ist. Beides lässt sich mit zoom_enabled: false und pan_enabled: false abschalten, wenn die Bilder einheitlich sind und die zusätzlichen Bedienelemente nur stören.

Tastenkürzel für Labels

Im Standard-Tastenprofil v7 liegen die Werkzeuge auf Buchstabentasten: r wählt das Box-Werkzeug, p das Polygon und b den Pinsel. Tastenkürzel für Labels werden pro Label mit key_value gesetzt, und Ziffern sind dabei die sichere Wahl, denn ein Label, dessen key_value mit einer Werkzeugtaste kollidiert, löst beide Aktionen gleichzeitig aus:

yaml
annotation_schemes:
  - annotation_type: image_annotation
    name: objects
    description: "Annotate all visible objects"
    tools:
      - bbox
    labels:
      - name: person
        color: "#FF6B6B"
        key_value: "1"
 
      - name: car
        color: "#4ECDC4"
        key_value: "2"
 
      - name: motorcycle
        color: "#45B7D1"
        key_value: "3"
 
      - name: bicycle
        color: "#96CEB4"
        key_value: "4"
 
      - name: traffic_light
        color: "#FFEAA7"
        key_value: "5"
 
      - name: stop_sign
        color: "#DDA0DD"
        key_value: "6"

Die Farben lohnen eine bewusste Wahl, statt die Vorgaben zu übernehmen. In dichten Verkehrsszenen suchen Annotierende das Bild nach Übersehenem ab, und zwei ähnliche Farben bei benachbarten Klassen verlangsamen diese Suche. Setze keybinding_profile: legacy, wenn deine Annotierenden die Tasten von Potato vor 2.8 gelernt haben und du sie nicht umschulen willst.

Attribute für Objekte ergänzen

Potato hat keinen Attributblock pro Box. Von einem image_annotation-Label werden name, color und key_value gelesen, alles andere im Label-Dictionary wird verworfen. Eine verschachtelte attributes:-Liste bewirkt also nichts, und nach dem Zeichnen einer Box erscheint keine Abfrage.

Dieselbe Information lässt sich auf zwei Wegen erfassen. Nimm die Unterscheidung in die Labelmenge auf, sodass das Attribut Teil dessen ist, was Annotierende auswählen:

yaml
annotation_schemes:
  - annotation_type: image_annotation
    name: objects
    description: "Draw a box around each object"
    tools:
      - bbox
    labels:
      - name: person
        color: "#FF6B6B"
      - name: person_occluded
        color: "#C44536"

Oder frag mit einem zweiten Schema nach dem Bild als Ganzem. Das gilt dann für das Element und nicht für eine einzelne Box, und darin liegt der Kompromiss: Du erfährst, dass im Bild etwas verdeckt war, aber nicht, welche Box es betraf:

yaml
  - annotation_type: multiselect
    name: image_conditions
    description: "Conditions affecting this image"
    labels:
      - Heavy occlusion
      - Truncated objects
      - Low light

Beim Import aus VIA bleiben Attribute als einziger Fall erhalten. via_importer liest die region_attributes jeder Region und übernimmt sie ins Label. Das ist ein Konzept des Imports, nicht der Konfiguration.

Mindestens eine Box verlangen

Ein Bild ohne Boxen ist mehrdeutig, denn es kann heißen, dass nichts gefunden wurde, oder dass jemand das Element übersprungen hat. min_annotations erzwingt die Unterscheidung, indem es eine Abgabe mit weniger Formen als dem Mindestwert ablehnt. Ein tatsächlich leeres Bild braucht dann ein ausdrückliches Negativ-Label:

yaml
annotation_schemes:
  - annotation_type: image_annotation
    name: objects
    description: "Draw a box around every traffic participant"
    tools:
      - bbox
    labels: [...]
    min_annotations: 1

Steuern, wie groß das Bild dargestellt wird

Die Zeichenfläche folgt dem dargestellten Bild, ein klein dargestelltes Bild gibt Annotierenden also eine kleine Zeichenfläche. instance_display legt die Größe fest, und Zoomen bleibt für Detailarbeit verfügbar:

yaml
instance_display:
  fields:
    - key: image_url
      type: image
      label: "Frame"
      display_options:
        max_width: 1200
        max_height: 800
        zoomable: true

Eine Produktivkonfiguration

Die Fahrkonfiguration unten enthält die fünf Klassen, die ein Detektionsdatensatz braucht. Die Labels behalten Zifferntasten, damit die linke Hand der Annotierenden auf der Zahlenreihe bleibt, und min_annotations: 1 passt, weil jedes Bild in diesem Korpus mindestens einen Verkehrsteilnehmer enthält:

yaml
annotation_task_name: "Autonomous Driving - Object Detection"
task_dir: "."
output_annotation_dir: "annotation_output/"
 
data_files:
  - "data/driving_frames.json"
 
item_properties:
  id_key: id
  text_key: frame_path
 
annotation_schemes:
  - annotation_type: image_annotation
    name: objects
    description: "Annotate all traffic participants and objects"
    tools:
      - bbox
    min_annotations: 1
    labels:
      - name: vehicle
        color: "#FF6B6B"
        key_value: "1"
 
      - name: pedestrian
        color: "#4ECDC4"
        key_value: "2"
 
      - name: cyclist
        color: "#45B7D1"
        key_value: "3"
 
      - name: traffic_sign
        color: "#FFEAA7"
        key_value: "4"
 
      - name: traffic_light
        color: "#DDA0DD"
        key_value: "5"

Fünf Klassen sind schon an der Grenze dessen, was Zifferntasten bequem abdecken. Ab zehn suchen Annotierende nach Tasten, statt das Bild zu lesen, und ab diesem Punkt ist es besser, die Aufgabe in zwei aufzuteilen, als ein weiteres Label hinzuzufügen.

Wie die Annotationen aussehen

Potato speichert alle Formen eines Bildes in einem flachen JSON-Array, unabhängig davon, mit welchem Werkzeug sie gezeichnet wurden. Die Koordinaten sind relativ zum Bild auf den Bereich 0 bis 1 normalisiert, sodass die Annotationen stimmen, auch wenn das Bild später in einer anderen Größe ausgeliefert wird:

json
[
  {
    "type": "bbox",
    "label": "vehicle",
    "color": "#FF6B6B",
    "coordinates": {"x": 0.234, "y": 0.352, "width": 0.120, "height": 0.130}
  },
  {
    "type": "bbox",
    "label": "pedestrian",
    "color": "#4ECDC4",
    "coordinates": {"x": 0.427, "y": 0.370, "width": 0.026, "height": 0.139}
  }
]

Eine bbox trägt ihre Geometrie unter coordinates als {x, y, width, height}. Polygone tragen unter demselben Schlüssel eine Liste von Punkten, Masken dagegen eine Lauflängenkodierung unter rle.

Export fürs Training

Trainingscode für Detektoren liest COCO oder YOLO, und Potato schreibt beides:

bash
python -m potato.export --config config.yaml --format coco --output ./export/
python -m potato.export --config config.yaml --format yolo --output ./export/

Die beiden Exporte unterscheiden sich in mehr als der Syntax. COCO schreibt eine einzige annotations.json mit Pixel-Boxen als [x, y, width, height] und kopiert die Bilder nicht, die file_name-Werte zeigen also dorthin, wo die Bilder bereits liegen. YOLO schreibt eine Textdatei pro Bild mit normalisierten Mittelpunktkoordinaten, teilt Bilder und Labels auf die Verzeichnisse train/ und val/ auf und legt data.yaml und classes.txt an, damit ein Trainingslauf die Klassenliste lesen kann, ohne dass man sie ihm separat übergibt.

Die Boxen konsistent halten

Zieh die Boxen eng um das Objekt, denn jedes Hintergrundpixel in der Box ist ein Pixel, das das Modell als Vordergrund zu bezeichnen lernt. Halte schriftlich fest, wie mit den schwierigen Fällen umzugehen ist (Verdeckung, am Bildrand abgeschnittene Objekte, Spiegelungen), und geht in der ersten Woche gemeinsam einen Stapel Beispiele durch, solange eine Änderung der Richtlinie noch wenig kostet. Beobachte während der Studie die Anzahl der Boxen und die Größenverteilung für jede annotierende Person, denn eine plötzliche Verschiebung bedeutet meist, dass eine Person eine Regel anders auslegt, und nicht, dass sich die Bilder geändert haben.

Nächste Schritte

Die übrigen Bildoptionen findest du unter Bildannotation.