Skip to content
Tutorial8 min di lettura

Disegnare bounding box per il rilevamento di oggetti

Crea in Potato un task di rilevamento di oggetti, da una configurazione iniziale con tre etichette a una da produzione per riprese di guida, poi esporta in COCO o YOLO.

Potato Team

Questo tutorial costruisce in Potato un task di annotazione per il rilevamento di oggetti, partendo da una configurazione con tre etichette fino all'esportazione COCO o YOLO su cui si addestra un rilevatore. Per l'elenco completo degli strumenti per immagini e delle loro opzioni, vedi la documentazione sull'annotazione di immagini.

Il file di dati

Ogni riga del file di dati è un'immagine. Potato ha bisogno di un identificativo e di un campo con l'URL o il percorso dell'immagine, e item_properties indica quale campo è quale:

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

Un percorso relativo viene risolto rispetto a media_directory, quindi le immagini locali non richiedono un file server in esecuzione.

Una configurazione minima

Lo schema image_annotation gestisce la superficie di disegno. Richiede un nome, una descrizione che gli annotatori leggono come istruzione, gli strumenti da attivare e l'insieme delle etichette:

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"

Tre etichette sono un punto di partenza voluto. Ogni etichetta in più è una decisione che l'annotatore deve prendere su ogni oggetto, e un caso limite su cui va istruito, quindi parti dalle classi di cui il modello ha davvero bisogno e aggiungi le altre quando le linee guida reggono.

Avvia il server e apri il task:

bash
potato start config.yaml -p 8000

Cosa fa l'annotatore

L'annotatore sceglie un'etichetta, poi trascina un rettangolo attorno a ogni oggetto, regolando gli angoli finché il riquadro non è aderente:

Interfaccia di annotazione con bounding box per il rilevamento di oggettiLo strumento bounding box permette di disegnare ed etichettare regioni rettangolari sulle immagini

Zoom e spostamento sono attivi per impostazione predefinita, cosa che conta per gli oggetti piccoli o lontani. Puoi disattivarli entrambi con zoom_enabled: false e pan_enabled: false quando le immagini sono uniformi e i controlli in più sono solo d'intralcio.

Scorciatoie per le etichette

Con il profilo di tasti predefinito v7, gli strumenti usano tasti con lettere, quindi r seleziona lo strumento riquadro, p il poligono e b il pennello. Le scorciatoie si impostano per ogni etichetta con key_value, e le cifre sono la scelta sicura, perché un'etichetta il cui key_value coincide con un tasto di strumento attiva entrambe le azioni insieme:

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"

Vale la pena scegliere i colori invece di accettare quelli predefiniti. Nelle scene di traffico dense l'annotatore scorre l'immagine cercando ciò che gli è sfuggito, e due colori simili su classi vicine rallentano questa ricerca. Imposta keybinding_profile: legacy se i tuoi annotatori hanno imparato i tasti di Potato precedenti alla 2.8 e preferisci non riaddestrarli.

Aggiungere attributi agli oggetti

Potato non ha un blocco di attributi per singolo riquadro. Di un'etichetta di image_annotation vengono letti name, color e key_value, e tutto il resto nel dizionario dell'etichetta viene scartato, quindi una lista annidata attributes: non ha alcun effetto e dopo aver disegnato un riquadro non compare nessuna domanda.

Ci sono due modi per raccogliere la stessa informazione. Puoi inserire la distinzione nell'insieme delle etichette, così l'attributo fa parte di ciò che l'annotatore sceglie:

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"

Oppure puoi chiedere dell'immagine nel suo complesso con un secondo schema. Questo si applica all'elemento e non a un singolo riquadro, ed è questo il compromesso. Sai che qualcosa nell'immagine era occluso, ma non quale riquadro fosse:

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

L'importazione da VIA è l'unico caso in cui gli attributi si conservano. via_importer legge i region_attributes di ogni regione e li incorpora nell'etichetta. È un concetto legato all'importazione, non alla configurazione.

Richiedere almeno un riquadro

Un'immagine senza riquadri è ambigua, perché può voler dire che l'annotatore non ha trovato nulla oppure che ha saltato l'elemento. min_annotations impone la distinzione rifiutando un invio con meno forme del minimo, così un'immagine davvero vuota richiede un'etichetta negativa esplicita:

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

Controllare le dimensioni di visualizzazione dell'immagine

La superficie di disegno segue l'immagine visualizzata, quindi un'immagine mostrata in piccolo dà all'annotatore una tela piccola. instance_display imposta le dimensioni, e lo zoom resta disponibile per il lavoro di dettaglio:

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

Una configurazione da produzione

La configurazione di guida qui sotto contiene le cinque classi di cui ha bisogno un dataset di rilevamento. Le etichette mantengono le scorciatoie numeriche, così la mano sinistra dell'annotatore resta sulla fila dei numeri, e min_annotations: 1 regge perché ogni fotogramma di questo corpus contiene almeno un utente della strada:

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"

Cinque classi sono già al limite di ciò che le scorciatoie numeriche coprono comodamente. Oltre dieci l'annotatore cerca i tasti invece di leggere il fotogramma, ed è a quel punto che conviene dividere il task in due piuttosto che aggiungere un'altra etichetta.

Come appaiono le annotazioni

Potato salva tutte le forme di un'immagine in un array JSON piatto, qualunque sia lo strumento che le ha disegnate. Le coordinate sono normalizzate nell'intervallo da 0 a 1 rispetto all'immagine, così le annotazioni restano corrette anche se in seguito l'immagine viene servita a una dimensione diversa:

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}
  }
]

Un bbox porta la sua geometria sotto coordinates come {x, y, width, height}. I poligoni portano una lista di punti sotto la stessa chiave, mentre le maschere portano una codifica run-length sotto rle.

Esportare per l'addestramento

Il codice di addestramento dei rilevatori legge COCO o YOLO, e Potato li scrive entrambi:

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

Le due esportazioni differiscono per più della sintassi. COCO scrive un unico annotations.json con riquadri in pixel nella forma [x, y, width, height], e non copia le immagini, quindi i valori di file_name puntano a dove le immagini si trovano già. YOLO scrive un file di testo per immagine con coordinate del centro normalizzate, suddivide immagini ed etichette nelle directory train/ e val/, e aggiunge data.yaml e classes.txt, così un addestramento può leggere l'elenco delle classi senza che glielo si debba passare a parte.

Mantenere i riquadri coerenti

Disegna i riquadri aderenti all'oggetto, perché ogni pixel di sfondo dentro il riquadro è un pixel che il modello impara a chiamare primo piano. Metti per iscritto come trattare i casi difficili (occlusioni, oggetti tagliati dal bordo del fotogramma, riflessi) e rivedete insieme un gruppo di esempi nella prima settimana, finché cambiare la linea guida costa ancora poco. Tieni d'occhio il numero di riquadri e la distribuzione delle dimensioni per ciascun annotatore mentre lo studio procede, perché uno scarto improvviso di solito significa che una persona ha iniziato a interpretare una regola in modo diverso, non che le immagini siano cambiate.

Prossimi passi

Per le altre opzioni sulle immagini, vedi Annotazione di Immagini.