Skip to content

COCOObjectDetectionAnnotations

SUMMARY

A batch of COCO-style object detection or instance segmentation annotations.

python
from telekinesis import datatypes
annotations = datatypes.COCOObjectDetectionAnnotations(
    ids=[0, 1],
    image_ids=[7, 7],
    category_ids=[1, 2],
    bboxes=[[0, 0, 10, 10], [20, 20, 5, 5]],
    areas=[100.0, 25.0],
    iscrowds=[False, False],
)

Segmentation representation follows iscrowds

Entry i with iscrowds[i]=False stores that row's segmentations[i] as a native polygon; iscrowds[i]=True stores it as an encoded COCO RLE dict. Call as_masks() to rasterize every row to a binary mask.

API Reference
Complete API documentation for COCOObjectDetectionAnnotations, including parameters, attributes, and methods.
View Reference →

Parameters

ParameterTypeDefaultDescription
idslist[int] | np.ndarrayRequiredAnnotation identifiers with shape (N,).
image_idslist[int] | np.ndarrayRequiredSource image identifiers with shape (N,).
category_idslist[int] | np.ndarrayRequiredAnnotated category identifiers with shape (N,).
bboxeslist[list[float]] | np.ndarrayRequiredBounding boxes with shape (N, 4), with one [x, y, width, height] row per annotation. Width and height must be non-negative.
areaslist[float] | np.ndarrayRequiredAnnotation areas with shape (N,). Each value must be greater than or equal to 0.
iscrowdslist[bool] | np.ndarrayRequiredCrowd flags with shape (N,). Each flag determines the expected segmentation representation for the corresponding annotation.
segmentationslist[COCOPolygonSegmentationLike| COCORLESegmentationLike | None] | NoneNoneLength-N sequence of polygon or COCO RLE segmentations, or None entries. Each representation must agree with the corresponding iscrowds value.

Raises

ExceptionCondition
TypeErrorA segmentations entry is present but structurally doesn't match its corresponding iscrowds entry.
ValueErrorbboxes isn't shape (N, 4) (N = bboxes.shape[0]), contains a non-finite value, or has negative width/height for a row.
ValueErrorAny of ids/image_ids/category_ids/areas/iscrowds/segmentations doesn't have length N.
ValueErrorareas contains a negative or non-finite value.
ValueErrorA segmentations entry fails RLE/polygon value validation.

Attributes

AttributeTypeDescription
idsnp.ndarrayDefensive copy, shape (N,) int32, annotation ids.
image_idsnp.ndarrayDefensive copy, shape (N,) int32, source image ids.
category_idsnp.ndarrayDefensive copy, shape (N,) int32, category ids.
bboxesnp.ndarrayDefensive copy, shape (N, 4) float32, [x, y, w, h]. Always present.
areasnp.ndarrayDefensive copy, shape (N,) float32, annotation areas.
iscrowdsnp.ndarrayDefensive copy, shape (N,) bool, crowd flags; selects each entry's segmentation representation.
segmentationslist[COCOPolygonSegmentation| COCORLESegmentation | None] | NoneDefensive copy: a polygon (where iscrowds[i] is False), a canonical RLE dict (where iscrowds[i] is True), or None per entry, or None if no segmentations were provided.

Methods

MethodTypeDescription
COCOObjectDetectionAnnotations.coerce(value)COCOObjectDetectionAnnotationsConverts a dict of constructor arguments into a COCOObjectDetectionAnnotations. If value is already a COCOObjectDetectionAnnotations, it is returned unchanged.
COCOObjectDetectionAnnotations.from_masks(*, ids, image_ids, category_ids, bboxes, areas, masks)COCOObjectDetectionAnnotationsBuilds a batch from a length-N list of binary (H, W) masks, encoding each one as an RLE segmentation. Every entry in masks must be an actual mask, not None, and the resulting batch always has every iscrowds entry True.
as_masks(image_heights=..., image_widths=...)COCOObjectDetectionAnnotationsAsMasksReturns this batch with every segmentation rasterized to a binary mask at its corresponding per-row size. image_heights/image_widths must each have length N; for an iscrowd row the given size must match the size already embedded in its RLE, and for a polygon row it must be a positive integer. See COCOObjectDetectionAnnotationsAsMasks for the returned fields.
COCOObjectDetectionAnnotations.mask_to_rle(mask)COCORLESegmentationEncodes a binary (H, W) mask as RLE, returning a canonical COCORLESegmentation.
COCOObjectDetectionAnnotations.polygon_to_rle(polygon, *, height, width)COCORLESegmentationRasterizes one or more polygons (a COCOPolygonSegmentationLike) and encodes the result as a canonical COCORLESegmentation.
COCOObjectDetectionAnnotations.rle_to_mask(rle)np.ndarrayDecodes a COCORLESegmentationLike into a binary (H, W) mask (uint8).
COCOObjectDetectionAnnotations.rle_to_polygon(rle, *, min_points=3, epsilon=0.0)COCOPolygonSegmentationApproximates a COCORLESegmentationLike as a COCOPolygonSegmentation by tracing the mask's boundary with marching squares.

Operators

OperationBehavior
len(anns)Number of annotations N in the batch.
anns[i]An int returns a single COCOObjectDetectionAnnotation. A slice or boolean np.ndarray mask returns a new COCOObjectDetectionAnnotations sub-batch. Raises IndexError for an out-of-range int, ValueError for a boolean mask of the wrong length, TypeError for any other index type.
a == bTrue only if b is also a COCOObjectDetectionAnnotations with equal ids, image_ids, category_ids, bboxes, areas, iscrowds, and segmentations; NotImplemented if b isn't a COCOObjectDetectionAnnotations.

Representations

RepresentationMethodResult
Masksas_masks(image_heights=..., image_widths=...)COCOObjectDetectionAnnotationsAsMasks

Visualization

python
import rerun as rr

# Your code block
# ....

rr.init("coco_object_detection_annotations_example", spawn=True)
datatypes.visualize(annotations, entity_path="/annotations", label="COCOObjectDetectionAnnotations")

Example

python
"""Demonstrates the Telekinesis COCOObjectDetectionAnnotations datatype."""

import time

import numpy as np
import rerun as rr
from loguru import logger

from telekinesis import datatypes

def coco_object_detection_annotations_example():
    """Demonstrate creation, inspection, operations, visualization, and serialization."""

    # ======================= Create ============================================
    # Segmentation semantics: iscrowd=False -> polygon (stored natively, not
    # converted), iscrowd=True -> encoded COCO RLE. The two representations are
    # never silently converted between each other.
    image_height, image_width = 720, 1280
    annotations = datatypes.COCOObjectDetectionAnnotations(
        ids=np.array([0, 1], dtype=np.int32),
        image_ids=np.array([7, 7], dtype=np.int32),
        category_ids=np.array([1, 2], dtype=np.int32),
        bboxes=np.array([[0, 0, 10, 10], [20, 20, 5, 5]], dtype=np.float32),
        areas=np.array([100.0, 25.0], dtype=np.float32),
        iscrowds=np.array([False, False]),
        segmentations=[
            [[0, 2, 10, 0, 10, 10, 0, 10]],
            [[22, 20, 25, 20, 25, 25, 20, 25]],
        ],
    )
    logger.info(f"Created COCOObjectDetectionAnnotations: {annotations}")

    # ======================= Inspect ===========================================
    logger.info(f"Number of annotations in batch: {len(annotations)}")
    logger.info(f"ids={annotations.ids}")
    logger.info(f"image_ids={annotations.image_ids}")
    logger.info(f"category_ids={annotations.category_ids}")
    logger.info(f"bboxes={annotations.bboxes}")
    logger.info(f"areas={annotations.areas}")
    logger.info(f"iscrowds={annotations.iscrowds}")
    logger.info(f"segmentations={annotations.segmentations}")

    # ======================= Operations =========================================
    index = 0
    single_annotation = annotations[index]
    logger.info(f"COCOObjectDetectionAnnotation at index {index}: {single_annotation}")

    sliced_annotations = annotations[0:1]
    logger.info(f"Sliced COCOObjectDetectionAnnotations: {sliced_annotations}")

    keep_mask = np.array([True, False])
    masked_annotations = annotations[keep_mask]
    logger.info(f"Masked COCOObjectDetectionAnnotations: {masked_annotations}")

    # Neither the single annotation nor the batch stores an image size, so
    # `as_mask`/`as_masks` take the target size explicitly. For an `iscrowd=True`
    # (RLE) entry, the passed size must match the RLE's own embedded size.
    single_as_mask = single_annotation.as_mask(image_height=image_height, image_width=image_width)
    logger.info(
        f"Segmentation 0 as mask: shape={single_as_mask['segmentation'].shape}, "
        f"dtype={single_as_mask['segmentation'].dtype}"
    )

    annotations_as_masks = annotations.as_masks(
        image_heights=[image_height, image_height], image_widths=[image_width, image_width]
    )
    logger.info(f"All masks: shapes={[m.shape for m in annotations_as_masks['segmentations']]}")

    # `from_masks` always sets `iscrowd=True` for every row. `areas` must
    # still be passed explicitly, since it can't be derived from the masks.
    # Every entry in `masks` must be a real mask. For rows without a mask,
    # build them individually with `COCOObjectDetectionAnnotation.from_mask`
    # or the plain constructor instead.
    crowd_mask_0 = np.zeros((image_height, image_width), dtype=np.uint8)
    crowd_mask_0[100:200, 150:400] = 1
    crowd_mask_1 = np.zeros((image_height, image_width), dtype=np.uint8)
    crowd_mask_1[300:350, 200:250] = 1
    crowd_annotations = datatypes.COCOObjectDetectionAnnotations.from_masks(
        ids=[2, 3],
        image_ids=[7, 7],
        category_ids=[3, 3],
        bboxes=[[150, 100, 250, 100], [200, 300, 50, 50]],
        areas=[float(crowd_mask_0.sum()), float(crowd_mask_1.sum())],
        masks=[crowd_mask_0, crowd_mask_1],
    )
    logger.info(f"Crowd annotations batch built from masks: {crowd_annotations}")

    # Mixin helpers shared across all COCO segmentation datatypes.
    mask_from_rle = datatypes.COCOObjectDetectionAnnotations.rle_to_mask(
        crowd_annotations.segmentations[0]
    )
    logger.info(f"Mask decoded via rle_to_mask: shape={mask_from_rle.shape}, dtype={mask_from_rle.dtype}")

    polygon_from_rle = datatypes.COCOObjectDetectionAnnotations.rle_to_polygon(
        crowd_annotations.segmentations[0]
    )
    logger.info(f"Polygon decoded via rle_to_polygon: {polygon_from_rle}")

    rle_from_polygon = datatypes.COCOObjectDetectionAnnotations.polygon_to_rle(
        single_annotation.segmentation, height=image_height, width=image_width
    )
    logger.info(f"RLE encoded via polygon_to_rle: {rle_from_polygon}")

    # ======================= Visualize =========================================
    rr.init("coco_object_detection_annotations_example", spawn=True)
    datatypes.visualize(annotations, entity_path="/coco_object_detection_annotations")
    datatypes.visualize(single_annotation, entity_path="/coco_object_detection_annotations/single")
    datatypes.visualize(crowd_annotations, entity_path="/coco_object_detection_annotations/crowd")

    # ======================= Serialize / Deserialize ===========================
    start = time.perf_counter()
    serialized = datatypes.serialize(annotations)
    serialization_ms = (time.perf_counter() - start) * 1000

    start = time.perf_counter()
    deserialized = datatypes.deserialize(serialized)["param_0"]
    deserialization_ms = (time.perf_counter() - start) * 1000

    logger.info(f"Deserialized COCOObjectDetectionAnnotations: {deserialized}")
    logger.info(f"Round-trip successful: {annotations == deserialized}")
    logger.info(f"Serialization time: {serialization_ms:.3f} ms")
    logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")


if __name__ == "__main__":
    coco_object_detection_annotations_example()