Contours
Represents a batch of contours, each an independent ordered list of 2D polygon vertices.
Parameters
| Field | Type | Description |
|---|---|---|
points | list[list[list[int]]] | list[np.ndarray] | Length-N list of (K_i, 2) point arrays, one variable-length contour per annotation; each element is converted independently to a contiguous int64 array. |
Raises
| Exception | Condition |
|---|---|
ValueError | points is not a list (note: ValueError, not TypeError, even though this is a container-type mismatch — verified empirically), or any element, after conversion to int64, isn't 2-D or its second axis isn't length 2 |
Conversion of each element to int64 isn't wrapped in a try/except; a value NumPy can't cast raises whatever error NumPy itself produces.
Attributes
| Attribute | Type | Description |
|---|---|---|
points | list[np.ndarray] | Length-N list of defensive copies, one int64 (K_i, 2) array per contour. |
Methods
| Method | Description |
|---|---|
Contours.coerce(value) | Returns value unchanged if it's already a Contours; wraps a list of point arrays into one otherwise. Raises TypeError for anything else. |
Operators
| Operation | Behavior |
|---|---|
contours == other | True only if other is a Contours with the same number of contours and elementwise-equal coordinates for every contour. NotImplemented (so False) for any other type. |
len(contours) | The number of contours, N. |
contours[i] | An int returns a single Contour for that row (supports negative indices; raises IndexError out of range). A slice or boolean np.ndarray mask returns a new Contours sub-batch (raises ValueError for a wrongly-sized mask). Raises TypeError for any other index type. |
hash(contours) | Not supported, despite being immutable after construction. |
repr(contours) | Contours(num_contours=2, total_points=5), or Contours(num_contours=0) when empty — verified empirically. |
Visualization
datatypes.visualize(contours, entity_path=...) logs every contour as a closed rerun 2D line strip (rr.LineStrips2D(strips)), each with its first vertex repeated at the end to close the loop. Contours has no registered label handler — passing label= to visualize() is silently ignored (no error, no label rendered).
Example
python
"""Demonstrates the Telekinesis Contours datatype."""
import time
import rerun as rr
import numpy as np
from loguru import logger
from telekinesis import datatypes
def contours_example():
"""Demonstrate creation, inspection, visualization, indexing, empty contours, and serialization."""
# ======================= Create ============================================
contour_1 = np.array([[118, 84], [134, 79], [150, 86], [139, 115]], dtype=np.int64)
contour_2 = np.array([[210, 145], [238, 140], [276, 160], [245, 190]], dtype=np.int64)
contour_3 = np.array([[322, 212], [335, 211], [332, 225], [320, 218]], dtype=np.int64)
contours = datatypes.Contours(points=[contour_1, contour_2, contour_3])
logger.info(f"Original Contours: {contours}")
# ======================= Inspect ===========================================
points = contours.points
num_contours = len(contours)
logger.info(f"Points: {points}")
logger.info(f"First contour points: {points[0]}")
logger.info(f"Number of contours: {num_contours}")
# ======================= Visualize =========================================
rr.init("contours_example", spawn=True)
datatypes.visualize(contours, entity_path="/Contours")
# ======================= Index =============================================
first_contour = contours[0]
sub_batch = contours[1:]
logger.info(f"First contour: {first_contour}")
logger.info(f"Sub-batch of contours [1:]: {sub_batch}")
# ======================= Empty Contours ====================================
empty_points = np.empty((0, 2), dtype=np.int64)
empty_contours = datatypes.Contours(points=[empty_points])
logger.info(f"Empty Contours: {empty_contours}")
# ======================= Serialize / Deserialize ===========================
start = time.perf_counter()
serialized = datatypes.serialize(contours)
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 Contours: {deserialized}")
logger.info(f"Round-trip successful: {deserialized == contours}")
logger.info(f"Serialization time: {serialization_ms:.3f} ms")
logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")
if __name__ == "__main__":
contours_example()
