Skip to content

Migrating from pycocotools

hotcoco is a drop-in replacement for pycocotools. This guide covers the two migration paths and the few differences to be aware of.

Option 1: Change your imports

If you control the code that imports pycocotools, swap the import paths:

# Before
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from pycocotools import mask as mask_util

# After
from hotcoco import COCO, COCOeval
from hotcoco import mask as mask_util

Everything else stays the same.

Option 2: Zero-code drop-in

If pycocotools is imported by a library you don't control, such as mmdet or detectron2, call init_as_pycocotools() once at startup:

from hotcoco import init_as_pycocotools
init_as_pycocotools()

# All pycocotools imports now resolve to hotcoco
from pycocotools.coco import COCO          # → hotcoco.COCO
from pycocotools.cocoeval import COCOeval  # → hotcoco.COCOeval
from pycocotools import mask               # → hotcoco.mask

How the patch works, where to call it, and per-framework notes are in Framework integrations.

Method naming

Both camelCase and snake_case names are supported:

pycocotools (camelCase) hotcoco (snake_case) Notes
getAnnIds() get_ann_ids() Both work
getCatIds() get_cat_ids() Both work
getImgIds() get_img_ids() Both work
loadAnns() load_anns() Both work
loadCats() load_cats() Both work
loadImgs() load_imgs() Both work
loadRes() load_res() Both work
annToRLE() ann_to_rle() Both work
annToMask() ann_to_mask() Both work

The same applies to Params properties: maxDets / max_dets, catIds / cat_ids, imgIds / img_ids, iouThrs / iou_thrs, recThrs / rec_thrs, areaRng / area_rng, areaRngLbl / area_rng_lbl, useCats / use_cats. OKS sigmas have one spelling, kpt_oks_sigmas — pycocotools also spells that attribute in snake_case.

And mask functions: toBbox / to_bbox, frPoly / fr_poly, frBbox / fr_bbox, frPyObjects / fr_py_objects.

Return types

hotcoco returns plain Python dicts and lists, matching pycocotools:

coco = COCO("instances_val2017.json")
anns = coco.load_anns([101])
print(type(anns[0]))  # <class 'dict'>

Annotation dicts carry the same keys — id, image_id, category_id, bbox, area, segmentation, iscrowd — plus any custom fields your dataset defines.

Getters return copies — assign back to apply

The data lives in Rust, so ev.params, coco.dataset, ev.coco_gt, and ev.coco_dt return copies on each access. Attribute assignment (ev.params.cat_ids = [...]) works — the setter routes the change back to Rust — but mutating a container inside a copy is a no-op:

ev.params.cat_ids = [1, 2, 3]       # applied — attribute assignment works
ev.params.maxDets.append(200)       # no-op — mutates a temporary list

md = ev.params.maxDets              # instead: pull, edit, assign back
md.append(200)
ev.params.maxDets = md

The same idiom applies to the dataset dict. pycocotools' in-memory construction flow works because assigning coco.dataset re-indexes:

d = coco.dataset
d["annotations"].append(new_ann)
coco.dataset = d                    # replaces contents and rebuilds the index
coco.createIndex()                  # supported, but a formality after assignment

Known differences

Behavior pycocotools hotcoco
Print on load Prints "loading annotations..." to stdout No progress output; loader warnings go to stderr and coco.load_warnings
COCO() with no args Creates empty instance with print statements Creates empty instance silently
Annotation IDs Requires unique positive integers Also accepts 0-based IDs
getAnnIds(areaRng=...) on annotations missing area Raises KeyError Excludes them from the query
Mutating coco.dataset / ev.params internals in place Mutates shared state No-op on a copy — assign back to apply
summarize() with non-default params Prints the table only Also emits a UserWarning per deviation — filter with warnings.filterwarnings
Performance Single-threaded C + Python Multi-threaded Rust

Metric parity

Every COCO metric matches pycocotools to floating-point precision — the measured differences on COCO val2017, and a script to check your own data, are in Metric parity.

Rust: module paths renamed in 1.0

For Rust users upgrading from 0.x, these module paths moved (crate-root re-exports like hotcoco::COCOeval and hotcoco::Hierarchy are unchanged, so most code needs no edits):

Pre-1.0 module path 1.0
hotcoco::eval hotcoco::detection
hotcoco::hierarchy hotcoco::detection::hierarchy
hotcoco::healthcheck hotcoco::quality::healthcheck
hotcoco::types::{SummaryStats, CategoryStats, DatasetStats} hotcoco::quality
hotcoco::primitives::counts hotcoco::metrics::counts

There are no compatibility aliases for the old module paths. The Python API is unaffected.