YOLO format compatibility matrix
AnnotateIt reads and writes YOLO detection and instance-segmentation datasets, and does not read or write YOLO pose or OBB. Because YOLOv5, YOLOv8 and YOLOv11 share one label-row grammar, there is no per-version importer: what decides whether an archive works is the row shape and the class list, not the version number on the box. This page is the full matrix, including what is refused and why.
Everything below is read out of the importer and exporter source, and the conversion behaviour is locked down by tests that fail if an exporter changes without this page changing with it (src/offline/dataset-io/coco-yolo-round-trip.test.ts and exporters/shape-format-matrix.test.ts).
YOLOv5, YOLOv8 and YOLOv11
These three are usually presented as three formats. For annotation data they are very nearly one. A label file is still one plain-text row per object, still a class index followed by numbers normalised to the image, and a detection row written for YOLOv5 is byte-identical to one written for YOLOv11. What actually changed between them is the dataset descriptor and the set of tasks the framework offers.
| Generation | What differs in the data | AnnotateIt |
|---|---|---|
| YOLOv5 | data.yaml with names as a list (names: [person, car]) and nc:; Darknet-style obj.names also common | Read — both forms |
| YOLOv8 | data.yaml with names as an index map (names: {0: person, 1: car}); adds segment, pose and obb tasks | Read — detect and segment rows |
| YOLOv11 | Same descriptor and same row grammar as v8; the changes are in the model, not the labels | Read — detect and segment rows |
So the honest statement is not “AnnotateIt supports YOLOv8”. It is that AnnotateIt reads the YOLO label grammar and both descriptor dialects, which covers v5, v8 and v11 datasets for the two tasks it supports — and does not silently accept the task-specific row shapes it does not support.
Task matrix
| YOLO task | Row shape | Import | Export |
|---|---|---|---|
| Detection | class cx cy w h | Yes — becomes a bounding box | Yes — from an object-detection project |
| Instance segmentation | class x1 y1 x2 y2 … (even count, ≥ 6 values) | Yes — becomes a polygon | Yes — from an instance-segmentation project |
| Pose / keypoints | class cx cy w h then x y [v] per joint | No — a dataset declaring kpt_shape or flip_idx is rejected with an error | No — YOLO is not offered for a keypoint project |
| Oriented boxes (OBB) | class x1 y1 x2 y2 x3 y3 x4 y4 | Partly — read as a 4-point polygon; the angle is not reconstructed | No — a rotated box exports as its axis-aligned bounds |
| Classification | Folder-per-class, no label files | No — see below | Yes — from a single-label classification project |
Two of those rows deserve their reasoning rather than just a “no”:
- Pose is refused, not guessed — and the signal it is refused on matters. A pose row is “class cx cy w h” followed by a flat coordinate list, which is the same grammar as a segmentation polygon; the two cannot be told apart from the row alone. Ultralytics declares the layout as kpt_shape: [joints, dimensions] in the dataset YAML, and AnnotateIt refuses any archive carrying it, whichever layout it names — [N, 2] without visibility just as much as [N, 3] with it. YAML can spell that value as a flow sequence, an indented block or a quoted scalar, and all three are recognised; a kpt_shape that cannot be parsed is also a refusal rather than a shrug, because “present but not understood” collapsing into “absent” is precisely how a pose dataset would slip through as polygons. flip_idx, the other key only a pose dataset carries, counts as the same declaration. Without any declaration at all, only the common COCO-17 case is caught on its own, because 4 + 17×3 happens to be an odd number of values and fails row validation; a pose archive that ships no YAML and uses an even value count is genuinely ambiguous and would be read as polygons.
- Classification import is refused on purpose. A YOLO object row labels a box inside the image, not the image as a whole, so offering to import one as an image-level class would misrepresent the data. External YOLO classification datasets are folder-per-class with no label files at all, and that import path is not implemented. AnnotateIt can export this layout from a single-label classification project; multi-label, hierarchical, unlabeled and anomaly samples are rejected rather than flattened into incorrect classes.
The OBB row is the one place where a YOLO archive can be misread, and it is unavoidable: an oriented-box row and a four-vertex segmentation polygon are the same eight numbers. AnnotateIt reads them as a polygon, which keeps every corner in the right place — you lose the knowledge that it was meant to be a rectangle, not the geometry. Export it as Datumaro if you need an angle to survive.
Class lists the importer accepts
An archive is only recognised as YOLO if a class list is found, because the class index in every row is meaningless without one. Any of these will do:
| Form | Example |
|---|---|
| A file ending in obj.names | One class name per line |
| YAML inline list | names: [person, forklift, helmet] |
| YAML block list | names: then - person, - forklift on following lines |
| YAML inline map | names: {0: person, 1: forklift} |
| YAML block map | names: then 0: person, 1: forklift on following lines |
Quoted scalars, escaped quotes and trailing # comments are handled, and an index map is sorted by index rather than by reading order. Where a map is used, the index in the file is authoritative; where a list is used, position is the index. Either way the order is the contract — a class list in a different order to the one the labels were written with shifts every class in the dataset, and nothing in the format can detect that.
Train / validation / test structure
What AnnotateIt writes
Two layouts, depending on whether the dataset has a saved split and you export in split mode.
export.zip
├── data.yaml # path/train/val/test + nc + names index map
├── images/
│ ├── train/…
│ ├── val/…
│ └── test/…
└── labels/
├── train/…
├── val/…
└── test/…export.zip
├── obj.names # one class per line
├── obj.data # classes / names / train
├── train.txt # list of image paths
└── obj_train_data/
├── img_0001.jpg
└── img_0001.txtIn split mode the validation subset is written to val/, following the Ultralytics convention rather than the app’s internal name for it. Every image gets a label file even when it has no objects — an empty .txt is how YOLO expresses a negative example, and dropping the file instead would quietly remove those images from training.
What AnnotateIt recognises on import
- images/train, images/val, images/test — and the training/valid/validation spellings.
- The transposed layout: train/images, val/images, test/images.
- An AnnotateIt annotateit-splits.json manifest, which wins over folder guessing when present because it carries the ratios, seed and per-image assignments the standard cannot.
A split recovered from folders alone is imported as locked manual assignments with ratios derived from the observed counts — honest about the fact that the original seed and ratios are not recoverable from the layout.
Label files are matched to images by path with the extension removed, not by bare filename. That is what makes split datasets safe: labels/train/img1.txt and labels/val/img1.txt are different keys, so identically-named files in different subsets do not overwrite each other.
Import and export limitations
The format itself is the limit in most of these. A YOLO row is a class index and some coordinates; there is no field for anything else.
| What | Through YOLO | Keep it by |
|---|---|---|
| Pixel masks | Traced to polygon rows, one per connected component — holes are filled | Exporting COCO (native RLE) or Datumaro |
| Polylines | Closed into a polygon ribbon of the stroke width | Exporting Datumaro, the only format that keeps a line open |
| Rotated boxes | Flattened to axis-aligned bounds | Exporting Datumaro, which stores the angle as an attribute |
| Circles | Written as their bounding box | Exporting Datumaro |
| Keypoints / poses | Not offered at all | Exporting COCO person-keypoints or Datumaro |
| Attributes and iscrowd | No field exists — dropped from the standard layer | Exporting COCO or Datumaro, or re-importing AnnotateIt’s own archive |
| Text prompts, label colours, hierarchy, task schema | No field exists | The annotateit-metadata.json sidecar, or Datumaro |
| Video | YOLO holds no video | Exporting Datumaro, Supervisely Video, MOT or MOTS |
The Export dialog lists what the chosen format will leave behind before you commit, so none of this is discovered after the fact. And every annotated export also carries an annotateit-metadata.json sidecar: the YOLO files stay a valid YOLO dataset for any trainer, while re-importing the same archive into AnnotateIt restores the attributes, prompts and hierarchy the standard had nowhere to put.
What the importer refuses
The contract is fail-closed: a well-formed archive imports completely, and anything ambiguous is rejected before a single annotation is written rather than half-imported.
- A class index the class list does not declare — the row cannot be attributed to a class, and guessing would silently mislabel objects.
- Coordinates outside the normalised 0–1 range, or a box with non-positive width or height. They are rejected rather than clamped: a clamped box is a wrong box that looks right.
- A polygon with zero area.
- A row whose value count matches neither grammar — which is how a pose file is caught.
- A label file with no matching image.
- Duplicate class names in the class list.
An empty label file is not an error. It is a valid negative example, and an archive whose label files are all empty is still recognised as YOLO provided it has a class list and conventional label paths — otherwise a dataset of hard negatives would import as unlabelled media.
Coordinate precision
YOLO stores fractions of the image, so a round trip through it is a division and a multiplication. Coordinates are written to six decimal places of the normalised value, so a stored vertex cannot move by more than half a step — 0.5 × 10⁻⁶ of the image, which is 6 × 10⁻⁴ pixels on a 1200-pixel axis. A box corner is worse by half again, because it is reconstructed from two separately rounded numbers as cx − w/2, putting its ceiling at 9 × 10⁻⁴ pixels. Those are arithmetic bounds, and the round-trip test asserts them across a 128-shape sweep that reaches 6 × 10⁻⁴ pixels at its worst. Geometry is not where YOLO loses data; the missing fields are.