How to Export Detection Results to COCO Format with Class Mapping in Python
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:
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 individualDetectionsobjects 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 (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:
- Prepare your dataset as a
DetectionDatasetobject that yields(image_path, image, detections)tuples. - Define target class ordering by supplying a list of class names in your desired sequence.
- Build the mapping using
build_coco_class_index_mappingto create a dictionary translating{source_class_id: target_index}. - Remap detections by applying
map_detections_class_idto eachDetectionsobject before export. - Execute the export by calling
save_coco_annotationswith 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:
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:
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). 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:
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, handling bounding boxes, masks, and metadata. - Use
build_coco_class_index_mappingto create translation dictionaries between source model IDs and target COCO indices. - Apply
map_detections_class_idto transformDetectionsobjects before export when working with non-sequential class IDs. - The
save_coco_annotationsfunction automatically handles COCO structure generation, polygon approximation, and JSON serialization. - All functionality operates on the standard
DetectionDatasetprotocol, making it compatible with any detection pipeline that produces SupervisionDetectionsobjects.
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →