Ultralytics YOLO27: Trag dich in die Warteliste ein.
Get Started
LektionAnfänger

Benutzerdefinierten Datensatz vorbereiten

Ordne Bilder und Beschriftungen im Ultralytics-YOLO-Format an, wandle Pixel-Bounding-Boxes in normalisierte Beschriftungszeilen um und finde fehlerhafte Beschriftungen, bevor du trainierst.

Ein falsch benannter Ordner oder eine nicht normalisierte Beschriftung lässt das Training nicht abstürzen. Ultralytics überspringt die fehlerhaften Bilder und macht weiter. So trainierst du mit einem Teil deiner Daten, erhältst ein mittelmäßiges Modell und verlierst einen Nachmittag damit, die Ursache an der falschen Stelle zu suchen.

In dieser Lektion geht es um die praktische Umsetzung eines Erkennungsdatensatzes: Ordner, Beschriftungszeilen, Konvertierung und Prüfungen. Du arbeitest mit dem echten Datensatz mit 115 medizinischen Pillenbildern, mit dem auch der Rest dieses Kurses trainiert. Wie du entscheidest, was du sammelst und wie du es beschriftest, wird unter Bewährte Methoden für die Annotation behandelt.

Lernergebnis

Du kannst einen Datensatz im Ultralytics-YOLO-Format strukturieren, Pixel-Bounding-Boxes in normalisierte Beschriftungszeilen umwandeln und fehlende oder beschädigte Beschriftungen anhand der Prüfzeile vor dem Training erkennen.

Schnellkurs
Wenn du dich bereits auskennst, findest du hier die Kurzfassung.
  1. Erstelle images/{train,val} und labels/{train,val} mit übereinstimmenden Dateinamen.

  2. Schreibe für jedes Objekt eine Zeile in class x_center y_center width height; jeder Wert muss zwischen 0 und 1 liegen.

  3. Wandle Pixel-Bounding-Boxes um, indem du durch die Bildbreite und -höhe teilst.

  4. Zeichne eine Beschriftungsdatei auf dem zugehörigen Bild ein und prüfe, ob die Prüfzeile 0 corrupt angibt.

Praxisnah

Übungsdatensatz abrufen#

Dialogfeld „New Dataset“ der Ultralytics Platform: ein Ablagebereich für Bilder, Videos oder YOLO-, COCO- und NDJSON-Datensätze, anschließend Felder für Name, URL, Beschreibung und Aufgabentyp

Die folgende Struktur kann auch im Dialogfeld „New Dataset“ der Ultralytics Platform als ZIP-Datei hochgeladen werden. Ein hier vorbereiteter Datensatz lässt sich also unverändert hochladen. Lade 92 Trainings- und 23 Validierungsbilder von Pillen in Blisterpackungen herunter und entpacke sie nach my_dataset:

curl -L -o medical-pills.zip https://github.com/ultralytics/assets/releases/download/v0.0.0/medical-pills.zip
unzip -q medical-pills.zip -d my_dataset
my_dataset/
├── images/
│   ├── train/   Frame_0.jpg, Frame_100.jpg, ... (92 images)
│   └── val/     Frame_144.jpg, ... (23 images)
└── labels/
    ├── train/   Frame_0.txt, Frame_100.txt, ... (92 files)
    └── val/     Frame_144.txt, ... (23 files)

Es gibt kein Manifest. Ultralytics findet jede Beschriftung, indem /images/ im Bildpfad durch /labels/ und die Dateierweiterung durch .txt ersetzt wird. Daher liest images/val/Frame_144.jpg die Datei labels/val/Frame_144.txt. Daraus folgen zwei Dinge:

  • Die Ordner müssen genau images und labels heißen und nebeneinander liegen.
  • Ein Bild mit einer leeren oder fehlenden Beschriftungsdatei ist ein Hintergrundbild: gültig und nützlich, um dem Modell beizubringen, was es nicht erkennen soll – aber nur, wenn das beabsichtigt ist.

Beschriftungszeile lesen#

YOLO-Beschriftungsdatei für zidane.jpg mit einer Zeile pro Objekt: Klassenindex, dann normalisiertes x-Zentrum, y-Zentrum, Breite und Höhe

Jede Zeile steht für ein Objekt: zuerst ein ganzzahliger Klassenindex, dann Mittelpunkt und Größe der Bounding-Box als Bruchteile der Bildbreite und -höhe. Eine echte Zeile aus labels/train/Frame_0.txt:

0 0.330729 0.816204 0.103125 0.121296
  • 0: Klassenindex 0, den die YAML-Datei pill zuordnet.
  • 0.330729 0.816204: Der Mittelpunkt der Box liegt bei 33 % der Bildbreite und 82 % der Bildhöhe.
  • 0.103125 0.121296: Die Box ist etwa 10 % der Bildbreite und 12 % der Bildhöhe groß.

Beispiel: Pixel-Bounding-Box umwandeln#

Beschriftungswerkzeuge exportieren häufig Pixelkoordinaten der Ecken (x1, y1, x2, y2). Teile sie durch die Bildgröße. Die Person ganz links im 810×1080 großen bus.jpg aus der letzten Lektion erstreckt sich von (49, 398) bis (240, 902):

FeldFormelWert
x_center(x1 + x2) / 2 / Breite(49 + 240) / 2 / 810 = 0.178395
y_center(y1 + y2) / 2 / Höhe(398 + 902) / 2 / 1080 = 0.601852
width(x2 − x1) / Breite191 / 810 = 0.235802
height(y2 − y1) / Höhe504 / 1080 = 0.466667

Die meisten Beschriftungswerkzeuge und Tools wie Roboflow können direkt im YOLO-Format exportieren. Für COCO JSON folge dem Leitfaden zum Training mit COCO JSON; das Repository JSON2YOLO behandelt weitere Formate. Für Pascal-VOC-XML lässt sich die obige Formel in ein kurzes Skript übertragen:

import xml.etree.ElementTree as ET
from pathlib import Path

CLASSES = ["person", "bus"]  # index = class id; must match names in data.yaml

def voc_to_yolo(xml_file: Path, out_dir: Path) -> None:
    root = ET.parse(xml_file).getroot()
    w, h = int(root.find("size/width").text), int(root.find("size/height").text)
    lines = []
    for obj in root.iter("object"):
        name = obj.find("name").text
        if name not in CLASSES:
            continue
        box = obj.find("bndbox")
        x1, y1, x2, y2 = (float(box.find(k).text) for k in ("xmin", "ymin", "xmax", "ymax"))
        xc, yc, bw, bh = (x1 + x2) / 2 / w, (y1 + y2) / 2 / h, (x2 - x1) / w, (y2 - y1) / h
        lines.append(f"{CLASSES.index(name)} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}")
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / f"{xml_file.stem}.txt").write_text("\n".join(lines) + "\n")

voc_to_yolo(Path("bus.xml"), Path("labels"))
print(Path("labels/bus.txt").read_text())

Bei Ausführung auf einem bus.xml mit den Personen- und Bus-Bounding-Boxes aus der vorherigen Lektion gibt das Skript das berechnete Beispiel und den Bus aus:

0 0.178395 0.601852 0.235802 0.466667
1 0.495679 0.454167 0.991358 0.482407

Vor dem Training prüfen#

Zeichne einige Beschriftungsdateien auf den zugehörigen Bildern ein. Eine verschobene Klasse, vertauschte x- und y-Koordinaten oder Boxen um Schatten sind mit bloßem Auge erkennbar, in Metriken aber unsichtbar:

from ultralytics.data.utils import visualize_image_annotations

visualize_image_annotations(
    "my_dataset/images/train/Frame_0.jpg",
    "my_dataset/labels/train/Frame_0.txt",
    {0: "pill"},
)

Lass Ultralytics anschließend alle Dateien prüfen. Beim ersten Einlesen eines Datensatzes durchsucht das Programm die Beschriftungen und entscheidet für jede Zeile, ob das jeweilige Bild verwendbar ist:

Die Prüfzeile zeigt das Ergebnis an: 23 images, 0 backgrounds, 0 corrupt steht für einen fehlerfreien Datensatz. Alles andere weist auf einen der unten aufgeführten Fehler hin.

Diese Fehler vermeiden#

Normalisierung vergessen

Symptom: ignoring corrupt image/label: non-normalized or out of bounds coordinates [634. 881. 198. 163.] und eine Anzahl ungleich null für corrupt. Ursache: Pixelwerte statt Bruchteile. Lösung: Teile durch Bildbreite und Bildhöhe. Jeder Labelwert über 1 bedeutet, dass die Box nie normalisiert wurde.

Labels-Ordner falsch benannt

Symptom: yolo val durchsucht 0 images, 23 backgrounds, meldet No labels found als Warnung und bricht das Training mit ValueError: train: No labels found ab – hier, weil der Label-Ordner annotations hieß. Lösung: Benenne die Ordner exakt images und labels und verwende übereinstimmende Dateinamen.

Klassenindizes beginnen bei 1

Symptom: Label class 1 exceeds dataset class count 1. Possible class labels are 0-0. Ursache: Das Exportwerkzeug hat die Klassen ab 1 nummeriert. Lösung: YOLO-Klassen beginnen bei 0; ziehe im Konvertierungsskript 1 ab oder korrigiere die Exporteinstellung.

Ecken statt Mittelpunkte

Wenn die Boxen um eine halbe Boxhöhe nach unten und nach rechts verschoben gezeichnet werden, hast du x1 y1 w h statt x_center y_center w h geschrieben. Eine Datei zu zeichnen deckt das in Sekunden auf; keine Prüfung beim Durchsuchen kann das leisten.

Systematische Prüfungen für einen ganzen Datensatz findest du unter Qualitätskontrolle von Datensätzen. Integrierte Datensätze wie coco8, coco128 und african-wildlife eignen sich als Vorlagen für die Ordnerstruktur; die Übersicht der Datensätze führt sie alle auf. Der Trainer ergänzt deine Beschriftungen um Datenerweiterung. Eine kurze Datenbereinigung zahlt sich daher in jeder Epoche aus.

Ausprobieren

Lade den Pillendatensatz nach my_dataset herunter. Liefere drei Prüfergebnisse: die Anzahl der Bilder und Beschriftungen für jeden Split, die Ausgabe eines Skripts, das jeden Beschriftungswert außerhalb von 0–1 ausgibt (es sollte nichts ausgeben), und eine Beschriftungsdatei, die mit visualize_image_annotations auf dem zugehörigen Bild eingezeichnet ist. Wenn du eigene beschriftete Bilder hast, wandle 10 davon in dieselbe Struktur um und zeichne eines ein.

Abgeschlossen, wenn
Du hast die Lektion abgeschlossen, wenn alle folgenden Punkte zutreffen.
  • my_dataset enthält images/train, images/val, labels/train und labels/val mit übereinstimmenden Dateinamen und Anzahlen.

  • Dein Prüfskript findet keine Beschriftungswerte außerhalb von 0–1.

  • Du hast mindestens eine Beschriftungsdatei auf dem zugehörigen Bild eingezeichnet, und die Boxen liegen auf den Pillen.

Lösung anzeigen
for s in train val; do
  echo "$s: $(ls my_dataset/images/$s | wc -l) images, $(ls my_dataset/labels/$s | wc -l) labels"
done
# train: 92 images, 92 labels
# val:   23 images, 23 labels
from pathlib import Path

for txt in Path("my_dataset/labels").rglob("*.txt"):
    for line in txt.read_text().splitlines():
        values = [float(v) for v in line.split()[1:]]
        if any(v < 0 or v > 1 for v in values):
            print(txt, line)

Wenn das Skript nichts ausgibt, sind alle Koordinaten normalisiert. Gleiche Anzahlen pro Split bedeuten, dass jedes Bild eine Beschriftungsdatei hat; die Prüfzeile der nächsten Lektion bestätigt, dass keine davon beschädigt ist.

Wie es weitergeht

Die Ordner sind bereit. Als Nächstes schreibst du die eine YAML-Datei, die Ultralytics mitteilt, wo sie liegen und was die Klassen bedeuten.