COCO JSON is the standard format for instance detection and segmentation datasets. It’s accepted by Detectron2, MMDetection, YOLOv8, FiftyOne, and most other modern CV frameworks. Creating valid COCO JSON by hand is tedious — but drawing annotations in a tool that exports it directly takes minutes.
This guide covers the COCO format structure and shows how to export it from RegionKit.
COCO JSON structure
A COCO annotation file has three required top-level arrays:
{
"images": [...],
"annotations": [...],
"categories": [...]
}
images
Each entry describes one image in the dataset:
{
"id": 1,
"file_name": "frame_001.jpg",
"width": 1920,
"height": 1080
}
annotations
Each entry describes one annotated object:
{
"id": 1,
"image_id": 1,
"category_id": 1,
"bbox": [120, 80, 200, 150],
"segmentation": [[120, 80, 320, 80, 320, 230, 120, 230]],
"area": 30000,
"iscrowd": 0
}
bbox — [x, y, width, height] in pixels, where (x, y) is the top-left corner. Note: this is different from YOLO (which uses centre coordinates) and different from some other formats that use [x1, y1, x2, y2].
segmentation — a list of polygon coordinate arrays, each being a flat [x0, y0, x1, y1, ...] list. For a simple (non-compound) polygon, this is a list with one element.
area — area of the annotation in pixels squared, computed from the segmentation polygon.
iscrowd — 0 for individual instances, 1 for crowd annotations (a group of objects annotated together).
categories
Each category defines a class:
{
"id": 1,
"name": "person",
"supercategory": "human"
}
Category IDs start at 1 (not 0) in the COCO convention. This is a common source of off-by-one errors when converting to YOLO (which is 0-indexed).
Exporting from RegionKit
- Open RegionKit and load your image
- Draw bounding boxes (
R) and/or polygon segmentation masks (P) - Set the label for each annotation in the Properties panel — this becomes the COCO category name
- Click Export → COCO JSON
RegionKit generates a valid COCO file with all three arrays populated:
images— one entry with the loaded image’s filename and dimensionsannotations— one entry per visible annotation, withbbox,segmentation, and computedareacategories— one entry per unique label, with IDs assigned in order of first appearance
Loading COCO JSON in Python
import json
with open('annotations.json') as f:
coco = json.load(f)
# Build lookup maps
img_map = {img['id']: img for img in coco['images']}
cat_map = {cat['id']: cat['name'] for cat in coco['categories']}
# Print each annotation
for ann in coco['annotations']:
img = img_map[ann['image_id']]
cat = cat_map[ann['category_id']]
bbox = ann['bbox']
area = ann.get('area', 0)
print(f"[{cat}] in {img['file_name']}")
print(f" bbox: x={bbox[0]}, y={bbox[1]}, w={bbox[2]}, h={bbox[3]}")
print(f" area: {area:.0f} px²")
if ann.get('segmentation'):
pts = ann['segmentation'][0]
n_vertices = len(pts) // 2
print(f" polygon: {n_vertices} vertices")
Loading with Detectron2
from detectron2.data.datasets import register_coco_instances
from detectron2.data import MetadataCatalog, DatasetCatalog
register_coco_instances(
"my_dataset_train",
{},
"annotations/train.json",
"images/train"
)
metadata = MetadataCatalog.get("my_dataset_train")
dataset = DatasetCatalog.get("my_dataset_train")
print(f"Classes: {metadata.thing_classes}")
print(f"Images: {len(dataset)}")
Validating COCO JSON
The pycocotools library provides a validation and evaluation API:
from pycocotools.coco import COCO
coco = COCO('annotations.json')
print(f"Images: {len(coco.imgs)}")
print(f"Categories: {[c['name'] for c in coco.loadCats(coco.getCatIds())]}")
print(f"Annotations: {len(coco.anns)}")
If the file is malformed (missing fields, inconsistent IDs), COCO() will raise a descriptive error.
Common COCO mistakes
Category IDs starting at 0 — COCO convention uses 1-based IDs. Some tools (including RegionKit’s export) use 1-based IDs; others start at 0. If your framework errors on the first category, check whether it expects 0-based or 1-based.
bbox format confusion — COCO uses [x, y, width, height] (top-left + dimensions), not [x1, y1, x2, y2] (two corners). Converting incorrectly shifts all your annotations.
Missing area field — Some frameworks require area. RegionKit computes it from the polygon using the shoelace formula. If you’re generating COCO JSON manually, compute area as bbox_width * bbox_height (approximate) or from the actual polygon.
Empty segmentation for bounding-box-only tasks — If you’re doing detection (not segmentation), the segmentation field can be an empty list []. Some frameworks require it present; others tolerate it missing.
Related: COCO Format Guide · COCO Annotation Editor