COCOObjectDetectionResult
Represents one prediction in the COCO "results" format: a per-image, per-category detection with a confidence score.
Stale class docstring in source
This class's docstring in source (and its embedded usage example) describes (N,)-shaped array attributes -- that text is stale, left over from before COCOObjectDetectionResult/COCOObjectDetectionResults were split into separate singular/plural classes. The Parameters/Attributes documented below reflect the actual (scalar) constructor and properties, not that docstring.
Parameters
| Field | Type | Description |
|---|---|---|
image_id | int | Source image id. |
category_id | int | Category id. |
image_height | int | Source image height, > 0. |
image_width | int | Source image width, > 0. |
score | float | Detection confidence score, >= 0. Stored at float32 precision so a serialization round-trip can't silently change the value. |
bbox | np.ndarray | list[float] | list[int] | None | Optional box in [x, y, w, h] format, shape (4,). At least one of bbox/segmentation must be provided. |
segmentation | dict[str, Any] | list[list[float] | list[int]] | None | Optional segmentation: a polygon (one or more flattened [x1, y1, x2, y2, ...] coordinate lists), a COCO RLE dict (compressed counts as str/bytes, or uncompressed counts as list[int]), or None. Normalized to an encoded COCO RLE dict internally. See SegmentationFormat. |
Raises
| Exception | Condition |
|---|---|
ValueError | Both bbox and segmentation are None. |
ValueError | bbox is provided but isn't shape (4,). |
ValueError | image_height or image_width is <= 0. |
TypeError | segmentation is neither None, a valid polygon list, nor an RLE dict with size/counts keys. |
ValueError | segmentation is an already-encoded RLE dict whose size doesn't match (image_height, image_width). |
Attributes
| Attribute | Type | Description |
|---|---|---|
image_id | int | Source image id. |
category_id | int | Category id. |
image_height | int | Source image height. |
image_width | int | Source image width. |
score | float | Detection confidence score. |
bbox | np.ndarray | None | Defensive copy of the box, shape (4,), [x, y, w, h], or None. |
segmentation | dict[str, Any] | None | Defensive copy of the canonical encoded COCO RLE dict, or None. |
Type hints in source are misleading here
The image_id/category_id/image_height/image_width/score properties are type-hinted as returning np.ndarray, and segmentation as list[dict | None] | None -- both leftover from the plural class this one was adapted from. At runtime they return plain Python int/float/dict/None as documented in the table above; the constructor's _validate casts every scalar field with int(...)/float(np.float32(...)).
Methods
| Method | Description |
|---|---|
COCOObjectDetectionResult.coerce(value) | Returns value unchanged if it's already a COCOObjectDetectionResult; if it's a dict, constructs one via COCOObjectDetectionResult(**value). Raises TypeError for any other input. |
COCOObjectDetectionResult.convert_segmentation(segmentation, source_type, target_type, *, height=None, width=None, min_points=3, epsilon=0.0) | Converts a segmentation between "rle", "polygon", and "mask" representations (source_type/target_type each accept a SegmentationFormat member or its string value). height/width are required only when source_type is "polygon" (RLE and mask are self-describing). min_points/epsilon only affect conversion to "polygon" (drop contours with fewer than min_points vertices; simplify with cv2.approxPolyDP when epsilon > 0). A stateless class method shared with COCOObjectDetectionResults, COCOObjectDetectionAnnotation, and COCOObjectDetectionAnnotations. Raises ImportError if converting to "polygon" and OpenCV isn't installed; raises TypeError/ValueError for invalid formats. |
get_segmentation is broken on this class
get_segmentation(idx, target_type) is also inherited from the same shared segmentation mixin, but it assumes a self._segmentations list -- the plural classes' internal storage. This class stores a single self._segmentation instead, so calling get_segmentation on a COCOObjectDetectionResult raises AttributeError: 'COCOObjectDetectionResult' object has no attribute '_segmentations'. Use the segmentation property together with convert_segmentation instead -- there's only one entry, so no index is needed.
Operators
| Operation | Behavior |
|---|---|
a == b | True only if b is also a COCOObjectDetectionResult with equal image_id, category_id, image_height, image_width, score, bbox (element-wise), and segmentation (size and counts); NotImplemented if b isn't a COCOObjectDetectionResult. |
hash(result) | Not supported (__hash__ = None), despite the object being immutable after construction. |
Serialization
to_pyarrow/from_pyarrow round-trip through a length-1 pa.StructArray:
StructArray length 1
├── image_id: int32
├── category_id: int32
├── bbox: fixed_size_list<float32>[4] (null when bbox is None)
├── image_height: int32
├── image_width: int32
├── score: float32
├── segmentation_height: int32 (0 when segmentation is None)
├── segmentation_width: int32 (0 when segmentation is None)
└── segmentation_count: string (null when segmentation is None)The RLE dict isn't stored as a nested struct: its size is split into segmentation_height/segmentation_width and its counts becomes the flat segmentation_count string field. A null segmentation_count is the sole marker that this result has no segmentation -- there's no separate validity field.
Visualization
datatypes.visualize(result, entity_path=...): when bbox is present, logs a single rr.Boxes2D at {entity_path}/boxes with class_ids=[category_id] and labels=[f"{score:.2f}"]. When segmentation is present, additionally decodes the RLE and logs an rr.SegmentationImage at {entity_path}/masks/mask0, with a two-class rr.AnnotationContext (transparent background, foreground labeled "cat={category_id} ({score:.2f})").
Example
COCOObjectDetectionResult has no standalone example file -- it's demonstrated as the element produced by indexing a COCOObjectDetectionResults batch (detection_at_index = bbox_results[index]) in that class's example, embedded verbatim below.
"""Demonstrates the Telekinesis COCOObjectDetectionResults datatype."""
import time
import rerun as rr
import numpy as np
from loguru import logger
from telekinesis import datatypes
def coco_object_detection_results_example():
"""Demonstrate creation, indexing, segmentation conversion, visualization, and serialization."""
# ======================= Case 1: Bounding Boxes Only =======================
H, W = 720, 1280
bbox_results = datatypes.COCOObjectDetectionResults(
image_ids=np.array([7, 7], dtype=np.int32),
category_ids=np.array([1, 2], dtype=np.int32),
image_heights=np.array([H, H], dtype=np.int32),
image_widths=np.array([W, W], dtype=np.int32),
scores=np.array([0.95, 0.82], dtype=np.float32),
bboxes=np.array(
[[0, 0, 10, 10], [20, 20, 5, 5]],
dtype=np.float32,
),
)
logger.info(f"Original COCOObjectDetectionResults: {bbox_results}")
# ======================= Inspect ===========================================
image_ids = bbox_results.image_ids
category_ids = bbox_results.category_ids
image_heights = bbox_results.image_heights
image_widths = bbox_results.image_widths
scores = bbox_results.scores
bboxes = bbox_results.bboxes
segmentations = bbox_results.segmentations
logger.info(f"Number of results in batch: {len(bboxes)}")
logger.info(
f"image_ids={image_ids}, "
f"category_ids={category_ids}, "
f"image_heights={image_heights}, "
f"image_widths={image_widths}, "
f"scores={scores}"
)
logger.info(f"Bboxes: {bboxes}")
logger.info(f"Segmentations: {segmentations}")
# ======================= Index =============================================
index = 0
detection_at_index = bbox_results[index]
image_id = detection_at_index.image_id
category_id = detection_at_index.category_id
bbox = detection_at_index.bbox
score = detection_at_index.score
logger.info(
f"ObjectDetectionResult at index {index}: "
f"image_id={image_id}, "
f"category_id={category_id}, "
f"bbox={bbox}, "
f"score={score}"
)
# ======================= Visualize =========================================
rr.init("coco_object_detection_results_example", spawn=True)
datatypes.visualize(bbox_results, entity_path="/COCOObjectDetectionResults")
first_result = bbox_results[0]
datatypes.visualize(first_result, entity_path="/COCOObjectDetectionResults/FirstResult")
# ======================= Case 2: Bbox + Segmentation =======================
bbox_segmentation_results = datatypes.COCOObjectDetectionResults(
image_ids=np.array([7, 7], dtype=np.int32),
category_ids=np.array([1, 2], dtype=np.int32),
image_heights=np.array([H, H], dtype=np.int32),
image_widths=np.array([W, W], dtype=np.int32),
scores=np.array([0.95, 0.82], dtype=np.float32),
bboxes=np.array([[0, 0, 10, 10], [20, 20, 5, 5]], dtype=np.float32),
segmentations=[
[[0, 2, 10, 0, 10, 10, 0, 10]],
[[22, 20, 25, 20, 25, 25, 20, 25]],
],
)
datatypes.visualize(
bbox_segmentation_results, entity_path="/COCOObjectDetectionResultsWithSegmentation"
)
# ======================= Case 3: Segmentation Only =========================
segmentation_only_results = datatypes.COCOObjectDetectionResults(
image_ids=np.array([7, 7], dtype=np.int32),
category_ids=np.array([1, 2], dtype=np.int32),
image_heights=np.array([H, H], dtype=np.int32),
image_widths=np.array([W, W], dtype=np.int32),
scores=np.array([0.95, 0.82], dtype=np.float32),
segmentations=[
[[0, 2, 10, 0, 10, 10, 0, 10]],
[[22, 20, 25, 20, 25, 25, 20, 25]],
],
)
datatypes.visualize(
segmentation_only_results, entity_path="/COCOObjectDetectionResultsWithOnlySegmentation"
)
# ======================= Convert Segmentation ==============================
as_rle = bbox_segmentation_results.segmentations[0]
as_polygon = bbox_segmentation_results.get_segmentation(0, "polygon")
as_mask = bbox_segmentation_results.get_segmentation(0, "mask")
logger.info(f"Segmentation 0 as RLE: {as_rle}")
logger.info(f"Segmentation 0 as polygon: {as_polygon}")
logger.info(f"Segmentation 0 as mask: shape={as_mask.shape}, dtype={as_mask.dtype}")
mask_back_to_rle = datatypes.COCOObjectDetectionResults.convert_segmentation(
as_mask, "mask", "rle"
)
polygon_to_mask = datatypes.COCOObjectDetectionResults.convert_segmentation(
as_polygon,
datatypes.SegmentationFormat.POLYGON,
datatypes.SegmentationFormat.MASK,
height=H,
width=W,
)
logger.info(f"Mask converted back to RLE: {mask_back_to_rle}")
logger.info(
f"Polygon converted to mask: shape={polygon_to_mask.shape}, dtype={polygon_to_mask.dtype}"
)
# ======================= Serialize / Deserialize ===========================
start = time.perf_counter()
serialized = datatypes.serialize(bbox_results)
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 COCOObjectDetectionResults: {deserialized}")
logger.info(f"Round-trip successful: {deserialized == bbox_results}")
logger.info(f"Serialization time: {serialization_ms:.3f} ms")
logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")
if __name__ == "__main__":
coco_object_detection_results_example()
