# How to Export Detection Results to COCO Format with Class Mapping in Python

> Export detection results to COCO format with class mapping using supervision in Python. Learn to remap class IDs and customize category ordering for your COCO JSON.

- Repository: [Roboflow/supervision](https://github.com/roboflow/supervision)
- Tags: how-to-guide
- Published: 2026-04-06

---

**Use the `save_coco_annotations` function from `supervision.dataset.formats.coco` combined with `build_coco_class_index_mapping` and `map_detections_class_id` to remap class indices before serialization, ensuring your COCO JSON follows a custom category ordering.**

The Roboflow **Supervision** library provides a complete toolkit for converting detection datasets into the COCO format while controlling class index mappings. When working with custom models that output non-sequential class IDs, you need a reliable way to **export detection results to COCO format with class mapping** to ensure compatibility with downstream evaluation tools and datasets that expect contiguous zero-based indices.

## Core Functions for COCO Export

The COCO export pipeline in Supervision is built around three primary utilities located in [`src/supervision/dataset/formats/coco.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/dataset/formats/coco.py):

- **`save_coco_annotations`** (`lines 331-384`): Orchestrates the entire export process, building COCO headers (`info`, `licenses`, `categories`, `images`) and writing the final JSON file.
- **`detections_to_coco_annotations`** (`lines 158-185`): Converts individual `Detections` objects into COCO annotation dictionaries, handling bounding boxes, masks, crowd flags, and polygon approximation.
- **`build_coco_class_index_mapping`** (`lines 34-44`): Creates a mapping dictionary that translates original class IDs to target indices based on a specified class name order.

For remapping operations, Supervision provides **`map_detections_class_id`** in [`src/supervision/dataset/utils.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/dataset/utils.py) (`lines 16-19`), which rewrites `class_id` values in `Detections` objects according to a user-supplied dictionary.

## Step-by-Step Workflow

To **export detection results to COCO format with class mapping**, follow this sequence:

1. **Prepare your dataset** as a `DetectionDataset` object that yields `(image_path, image, detections)` tuples.
2. **Define target class ordering** by supplying a list of class names in your desired sequence.
3. **Build the mapping** using `build_coco_class_index_mapping` to create a dictionary translating `{source_class_id: target_index}`.
4. **Remap detections** by applying `map_detections_class_id` to each `Detections` object before export.
5. **Execute the export** by calling `save_coco_annotations` with your remapped dataset and output path.

## Practical Code Examples

### Basic Export Without Mapping

For datasets where class IDs are already sequential and match your target ordering, use the direct export method:

```python
from supervision.dataset.core import DetectionDataset
from supervision.dataset.formats.coco import save_coco_annotations

# Assuming my_dataset is a DetectionDataset instance

save_coco_annotations(
    dataset=my_dataset,
    annotation_path="exports/my_dataset_coco.json"
)

```

Under the hood, `save_coco_annotations` iterates over the dataset (lines 352-364), calls `detections_to_coco_annotations` for each image (lines 158-185), and assembles the standard COCO structure before writing via `save_json_file` (line 384).

### Export with Custom Class Mapping

When your model outputs class IDs `[0, 1, 2]` but you need the COCO file to list categories in the order `["person", "car", "dog"]`, implement a remapping wrapper:

```python
from supervision.dataset.core import DetectionDataset
from supervision.dataset.formats.coco import (
    save_coco_annotations,
    build_coco_class_index_mapping,
)
from supervision.dataset.utils import map_detections_class_id

# Define your desired category order

target_classes = ["person", "car", "dog"]
source_classes = my_dataset.classes  # Original order from your dataset

# Create mapping from original IDs to target indices

class_mapping = build_coco_class_index_mapping(
    coco_categories=[{"id": i, "name": name} for i, name in enumerate(source_classes)],
    target_classes=target_classes,
)

# Create a wrapper dataset that remaps class IDs on-the-fly

class MappedDataset(DetectionDataset):
    def __iter__(self):
        for image_path, image, detections in super().__iter__():
            detections = map_detections_class_id(
                source_to_target_mapping=class_mapping,
                detections=detections,
            )
            yield image_path, image, detections

mapped_dataset = MappedDataset(
    images_dir=my_dataset.images_dir,
    annotations=my_dataset.annotations,
    classes=target_classes,  # Updated class list for the exporter

)

# Export with remapped indices

save_coco_annotations(
    dataset=mapped_dataset,
    annotation_path="exports/custom_mapped_coco.json"
)

```

The `build_coco_class_index_mapping` function constructs the translation dictionary (lines 34-44), while `map_detections_class_id` applies the transformation to each detection instance (lines 16-19 in [`dataset/utils.py`](https://github.com/roboflow/supervision/blob/main/dataset/utils.py)). The exporter then uses these remapped IDs when setting `category_id` in the final JSON (line 210).

### Controlling Polygon Approximation

When exporting instance segmentation masks, you can fine-tune polygon generation parameters:

```python
save_coco_annotations(
    dataset=my_dataset,
    annotation_path="exports/precise_coco.json",
    min_image_area_percentage=0.0,  # Include masks of any size

    max_image_area_percentage=1.0,  # No upper size limit

    approximation_percentage=0.95,  # Higher fidelity (default is 0.75)

)

```

These parameters are forwarded to `approximate_mask_with_polygons` inside `detections_to_coco_annotations` (lines 186-195), controlling which masks are included based on image area percentage and how closely polygons follow mask boundaries.

## Summary

- **Supervision** provides a complete COCO export pipeline through [`src/supervision/dataset/formats/coco.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/dataset/formats/coco.py), handling bounding boxes, masks, and metadata.
- Use **`build_coco_class_index_mapping`** to create translation dictionaries between source model IDs and target COCO indices.
- Apply **`map_detections_class_id`** to transform `Detections` objects before export when working with non-sequential class IDs.
- The **`save_coco_annotations`** function automatically handles COCO structure generation, polygon approximation, and JSON serialization.
- All functionality operates on the standard `DetectionDataset` protocol, making it compatible with any detection pipeline that produces Supervision `Detections` objects.

## Frequently Asked Questions

### What is the difference between source class IDs and target class indices in COCO export?

**Source class IDs** are the integer labels output by your specific model (which may be arbitrary or non-contiguous), while **target class indices** are the sequential integers (0, 1, 2...) required by the COCO format specification for the `category_id` field. The `build_coco_class_index_mapping` function bridges this gap by creating a dictionary that maps your specific model's IDs to the standard zero-based indices expected by evaluation tools.

### How do I handle missing classes when building a class mapping?

If your target class list contains categories not present in the source dataset, `build_coco_class_index_mapping` will assign them indices in the target ordering but no annotations will reference those IDs in the exported JSON. This is valid COCO format—the categories array lists all possible classes, while annotations only reference the ones present. Ensure your target class list represents your complete ontology, not just the classes visible in the current dataset split.

### Can I export instance segmentation masks to COCO format using Supervision?

Yes. When your `Detections` objects contain a `mask` attribute (NumPy arrays of shape `(N, H, W)`), `detections_to_coco_annotations` automatically converts these to polygon format using `approximate_mask_with_polygons` (lines 186-195). The resulting COCO JSON includes RLE or polygon segmentation data in the `segmentation` field of each annotation, compatible with the COCO API and standard evaluation scripts.

### Why are my exported class IDs not sequential in the COCO JSON?

If you observe non-sequential `category_id` values in the output, you likely skipped the remapping step and passed the dataset directly to `save_coco_annotations`. The exporter preserves original class IDs by default. To enforce sequential ordering, you must use `build_coco_class_index_mapping` to create a mapping dictionary and apply it via `map_detections_class_id` before export, as shown in the custom mapping example above.