AnyLabeling: AI-Powered Data Annotation
7 / 11
Lesson 7 of 11

Lesson 07: Export Formats & Pipelines

7 min readViet-Anh NguyenViet-Anh Nguyen

You have annotated your images. Now you need to get those labels into a format your training framework understands. AnyLabeling exports to four formats natively, and each one maps to a different set of training tools.

The Four Export Formats

YOLO Format

Output: One .txt file per image + a classes.txt file.

Structure:

dataset/
├── images/
│   ├── img_001.jpg
│   └── img_002.jpg
├── labels/
│   ├── img_001.txt
│   └── img_002.txt
└── classes.txt

Label file format (per line):

<class_id> <center_x> <center_y> <width> <height>

All coordinates are normalized to [0, 1] relative to image dimensions.

Best for: Ultralytics YOLOv5, YOLOv8, YOLO11, and any YOLO-family trainer.

How to export:

  1. Go to Tools > Export Annotations > Export YOLO Annotations.
  2. Select the task type (detection, segmentation, OBB, or keypoints).
  3. Provide a classes.txt or let AnyLabeling generate one from your labels.
  4. Click OK. Labels are saved to a labels/ subfolder by default.

Segmentation variant: For instance segmentation, each line contains the class ID followed by polygon coordinates: <class_id> <x1> <y1> <x2> <y2> ... <xn> <yn>. All coordinates normalized.

COCO Format

Output: A single annotations.json file containing all images and annotations.

Structure:

{
  "images": [{ "id": 1, "file_name": "img_001.jpg", "width": 1280, "height": 720 }],
  "annotations": [
    {
      "id": 1,
      "image_id": 1,
      "category_id": 1,
      "bbox": [100, 150, 300, 200],
      "area": 60000,
      "segmentation": [[100, 150, 400, 150, 400, 350, 100, 350]],
      "iscrowd": 0
    }
  ],
  "categories": [{ "id": 1, "name": "car" }]
}

Best for: Detectron2, MMDetection, TensorFlow Object Detection API, DETR, and most research frameworks.

How to export:

  1. Go to Tools > Export Annotations > Export COCO Annotations.
  2. Select the task (detection, segmentation, or keypoints).
  3. Configure options and click OK.
  4. The export runs in a background thread (can take time for large datasets) and produces a single JSON file.

COCO bbox format: [x_min, y_min, width, height] in absolute pixels. This is different from YOLO's normalized center format. Do not confuse them.

Pascal VOC Format

Output: One .xml file per image.

Structure:

<annotation>
  <folder>images</folder>
  <filename>img_001.jpg</filename>
  <size>
    <width>1280</width>
    <height>720</height>
    <depth>3</depth>
  </size>
  <object>
    <name>car</name>
    <bndbox>
      <xmin>100</xmin>
      <ymin>150</ymin>
      <xmax>400</xmax>
      <ymax>350</ymax>
    </bndbox>
  </object>
</annotation>

Best for: Legacy systems, TensorFlow 1.x object detection, any pipeline expecting VOC-style XML.

How to export:

  1. Go to Tools > Export Annotations > Export VOC Annotations.
  2. Configure and click OK.
  3. XML files are saved to an Annotations/ subfolder.

VOC bbox format: [xmin, ymin, xmax, ymax] in absolute pixels. A third coordinate convention to keep straight.

CreateML Format

Output: A annotations.json file in Apple's CreateML format.

Best for: Training models with Apple's CreateML for iOS/macOS deployment.

Choosing the Right Format

Training FrameworkFormatNotes
Ultralytics (YOLOv5/v8/11)YOLONative format, no conversion needed
Detectron2COCORegister with register_coco_instances()
MMDetectionCOCOSupported natively
TensorFlow Object DetectionVOC or COCOTFRecord conversion still needed
PaddleDetectionCOCOSupported natively
Apple CreateMLCreateMLNative format
Custom PyTorch trainingCOCOMost flexible; easy to parse with pycocotools

If you are unsure, export to COCO. It is the most widely supported format and preserves the most information (bounding boxes, segmentation polygons, keypoints, and area).

Coordinate System Summary

This trips people up constantly:

FormatBbox ConventionCoordinates
YOLOcenter_x, center_y, width, heightNormalized [0,1]
COCOx_min, y_min, width, heightAbsolute pixels
VOCx_min, y_min, x_max, y_maxAbsolute pixels

A box at pixel position (100, 150) with size (300, 200) in a 1280x720 image:

YOLO:  0  0.1953  0.3472  0.2344  0.2778
COCO:  {"bbox": [100, 150, 300, 200]}
VOC:   <xmin>100</xmin> <ymin>150</ymin> <xmax>400</xmax> <ymax>350</ymax>

Format Conversion with Python

Sometimes you need a format AnyLabeling does not export directly, or you need to transform the export. Here are the conversions you will use most:

COCO to YOLO

import json
from pathlib import Path

def coco_to_yolo(coco_json_path, output_dir):
    with open(coco_json_path) as f:
        coco = json.load(f)

    # Build lookups
    images = {img["id"]: img for img in coco["images"]}
    categories = {cat["id"]: idx for idx, cat in enumerate(coco["categories"])}

    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    # Group annotations by image
    from collections import defaultdict
    anns_by_image = defaultdict(list)
    for ann in coco["annotations"]:
        anns_by_image[ann["image_id"]].append(ann)

    for image_id, anns in anns_by_image.items():
        img = images[image_id]
        w, h = img["width"], img["height"]
        stem = Path(img["file_name"]).stem

        lines = []
        for ann in anns:
            cls_id = categories[ann["category_id"]]
            bx, by, bw, bh = ann["bbox"]
            # Convert COCO (xmin, ymin, w, h) to YOLO (cx, cy, w, h) normalized
            cx = (bx + bw / 2) / w
            cy = (by + bh / 2) / h
            nw = bw / w
            nh = bh / h
            lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}")

        (output_dir / f"{stem}.txt").write_text("\n".join(lines))

    # Write classes.txt
    class_names = [cat["name"] for cat in sorted(coco["categories"], key=lambda c: categories[c["id"]])]
    (output_dir / "classes.txt").write_text("\n".join(class_names))

# Usage
coco_to_yolo("annotations.json", "labels/")

YOLO to COCO

import json
from pathlib import Path
from PIL import Image

def yolo_to_coco(images_dir, labels_dir, classes_file, output_path):
    classes = Path(classes_file).read_text().strip().split("\n")
    categories = [{"id": i, "name": name} for i, name in enumerate(classes)]

    images_dir = Path(images_dir)
    labels_dir = Path(labels_dir)

    coco = {"images": [], "annotations": [], "categories": categories}
    ann_id = 0

    for img_id, img_path in enumerate(sorted(images_dir.glob("*"))):
        if img_path.suffix.lower() not in (".jpg", ".jpeg", ".png", ".bmp"):
            continue

        img = Image.open(img_path)
        w, h = img.size
        coco["images"].append({
            "id": img_id, "file_name": img_path.name, "width": w, "height": h
        })

        label_path = labels_dir / f"{img_path.stem}.txt"
        if not label_path.exists():
            continue

        for line in label_path.read_text().strip().split("\n"):
            if not line.strip():
                continue
            parts = line.split()
            cls_id = int(parts[0])
            cx, cy, nw, nh = map(float, parts[1:5])
            bx = (cx - nw / 2) * w
            by = (cy - nh / 2) * h
            bw = nw * w
            bh = nh * h

            coco["annotations"].append({
                "id": ann_id, "image_id": img_id, "category_id": cls_id,
                "bbox": [round(bx, 2), round(by, 2), round(bw, 2), round(bh, 2)],
                "area": round(bw * bh, 2), "iscrowd": 0
            })
            ann_id += 1

    Path(output_path).write_text(json.dumps(coco, indent=2))

# Usage
yolo_to_coco("images/", "labels/", "classes.txt", "coco_annotations.json")

Dataset Split Script

Most training frameworks expect train/val/test splits. Here is a quick split script:

import random
import shutil
from pathlib import Path

def split_dataset(images_dir, labels_dir, output_dir, ratios=(0.8, 0.1, 0.1)):
    images = sorted(Path(images_dir).glob("*"))
    images = [p for p in images if p.suffix.lower() in (".jpg", ".jpeg", ".png", ".bmp")]
    random.shuffle(images)

    n = len(images)
    train_end = int(n * ratios[0])
    val_end = train_end + int(n * ratios[1])

    splits = {
        "train": images[:train_end],
        "val": images[train_end:val_end],
        "test": images[val_end:],
    }

    for split_name, split_images in splits.items():
        img_out = Path(output_dir) / split_name / "images"
        lbl_out = Path(output_dir) / split_name / "labels"
        img_out.mkdir(parents=True, exist_ok=True)
        lbl_out.mkdir(parents=True, exist_ok=True)

        for img_path in split_images:
            shutil.copy2(img_path, img_out / img_path.name)
            label_path = Path(labels_dir) / f"{img_path.stem}.txt"
            if label_path.exists():
                shutil.copy2(label_path, lbl_out / label_path.name)

    print(f"Split: {len(splits['train'])} train, {len(splits['val'])} val, {len(splits['test'])} test")

# Usage
split_dataset("images/", "labels/", "dataset_split/")

Key Takeaways

  • YOLO format for Ultralytics training. COCO format for everything else.
  • Know the three coordinate conventions: YOLO (normalized center), COCO (absolute min + size), VOC (absolute min/max). Mixing them up is the number one annotation export bug.
  • Export to COCO when in doubt — it preserves the most information and converts to other formats easily.
  • Always split your dataset before training. 80/10/10 is a reasonable default.

In the next lesson, we load custom ONNX models into AnyLabeling for domain-specific auto-labeling.

Lesson 07: Export Formats & Pipelines - Viet-Anh on Software