COCO ↔ YOLO round trip, measured

Converting between COCO and YOLO preserves geometry almost exactly — a full cycle cannot move a box corner by more than 9 × 10⁻⁴ of a pixel, and a 128-shape sweep gets nowhere past 6 × 10⁻⁴ — and loses everything that is not geometry, because a YOLO row has no field for it. This page separates the three outcomes: preserved, converted into something else, and lost. Every line comes from a test that runs the real exporters and parsers in both directions.

Reproduce it: npx jest src/offline/dataset-io/coco-yolo-round-trip.test.ts. The test prints the measurements quoted here and fails if a converter changes, which is the mechanism that keeps this page true.

How this was measured

A project is exported to COCO, the archive is parsed back in, the result is exported to YOLO, and that archive is parsed back in again — through the same code the application uses, not a simplified stand-in. Then the same trip is run in the opposite direction. The image is 1200 × 800, and no coordinate is a round fraction of it, so a normalisation error cannot hide behind a convenient number.

The trip is measured over the standard layer only — the plain COCO and YOLO files a third-party tool reads. AnnotateIt also writes an annotateit-metadata.json sidecar beside them, which makes its own round trip exact; measuring that instead would have answered a different and much easier question. The last section covers what the sidecar changes.

What is preserved

DataResultMeasured
Bounding box geometryPreservedWorst corner 6 × 10⁻⁴ px over a 128-shape sweep, against an arithmetic ceiling of 9 × 10⁻⁴ px
Segmentation polygon geometryPreserved, vertex for vertexWorst vertex 4 × 10⁻⁴ px over the same sweep, ceiling 6 × 10⁻⁴ px; vertex count unchanged
Class identity and class orderPreservedobj.names index 0 stays the first label; COCO category ids stay 1-based in label order
Image association and dimensionsPreservedCOCO stores width/height; YOLO recovers them from the image bytes
Images with no objectsPreserved by bothA COCO image record with no annotations; an empty YOLO .txt

The precision result is worth stating plainly because it contradicts a common worry: normalising to fractions and back does not meaningfully move an annotation. Six decimal places on a 1200-pixel image is a four-ten-thousandth of a pixel. Whatever you lose converting COCO to YOLO, it is not the position of your boxes.

What is converted into something else

These are not losses in the sense of data disappearing, and not preservation either. The object arrives, in a different shape, because the target has no way to say what the source said.

DataBecomesConsequence
A pixel mask (COCO RLE)One traced polygon row per connected componentHoles are filled and thin bridges disappear; a two-blob mask with a hole came out as 2 polygon rows
An open polylineA closed ribbon polygon of the stroke widthMeasured as an 8 px tall ribbon from a zero-height line, in both COCO and YOLO
A rotated boxIts axis-aligned boundsThe angle is gone from both formats; a 90°-rotated 200 × 100 box arrives as 100 × 200
One COCO instance with several ringsOne separate annotation per ringGeometry is right, but the rings are no longer known to be one object — 1 instance became 2
A polygon imported into a detection projectIts bounding boxChosen by the project type you import into, not by the format
The COCO area fieldRecomputed from the geometryA triangle’s 56 700 px² came back as 56 699.88 — recalculated, not carried

The multi-ring case is the one most likely to surprise. A COCO annotation is allowed to be one object described by several polygons — a car split by a lamp post in front of it. YOLO has no way to group rows, so the object arrives as two objects. If instance identity matters more than format ubiquity, COCO or Datumaro is the export to choose.

Where data is lost

DataCOCOYOLO
Per-object attributesKept in an attributes block (the CVAT dialect)Lost — the row is five numbers
iscrowdA first-class fieldLost
Keypoints and posesperson_keypoints records with x/y/visibilityNot offered — YOLO is not available for a keypoint project
Mask holes and disjoint detailKept exactly, as native RLELost to outline tracing
Instance grouping of multiple ringsKeptLost
Rotation angleLostLost
Text prompts, label colours, hierarchy, task schemaLostLost

Read across that table and the summary is simple: COCO is the richer of the two, YOLO is the more universally trained-on, and the conversion is lossy in exactly one direction. Going YOLO → COCO invents nothing and loses nothing, because everything a YOLO file can say, COCO can also say. Going COCO → YOLO discards whatever the row grammar cannot express.

The other direction, in detail

YOLO inputResult in COCO
Detection rowExact absolute pixels in bbox, empty segmentation, iscrowd 0
Segmentation rowA polygon ring in segmentation with a shoelace area
Pose rowRejected on import whenever the dataset declares kpt_shape or flip_idx, in any YAML spelling; an archive declaring neither is genuinely ambiguous
OBB row (8 values)Read as a four-point polygon; the angle is not reconstructed
Undeclared class indexRejected — the row cannot be attributed to a class
Coordinates outside 0–1Rejected rather than clamped

What changes with the AnnotateIt sidecar

Every annotated export carries an annotateit-metadata.json file next to the standard ones. The YOLO files remain a plain YOLO dataset — the same five-number rows, readable by any trainer — while re-importing that same archive into AnnotateIt restores the attributes, prompts, label hierarchy, exact shapes and project task schema that the standard layer dropped. Verified in the same test: an attribute that is provably absent from the YOLO rows comes back from the sidecar.

So there are two different questions with two different answers. “What survives COCO → YOLO?” is answered by the tables above. “What survives AnnotateIt → YOLO → AnnotateIt?” is answered by: everything, as long as you keep the sidecar in the archive.

Choosing between them

If you needExport
An Ultralytics training run with a train/val/test layoutYOLO in split mode
Pixel masks that keep their holesCOCO (native RLE)
Keypoints and skeletonsCOCO person-keypoints
Per-object attributes or crowd regionsCOCO
Rotated boxes, open polylines, or anything with the angle intactDatumaro
A faithful return trip into AnnotateItAny format — keep the sidecar

See also

Video tutorial

AnnotateIt tutorial