Skip to content

ImageBatch

Represents an ordered collection of images.

Parameters

FieldTypeDescription
dataSequence[Image | np.ndarray]Images to store. Bare np.ndarrays are converted via Image(...).
compressionImageCompression | int | NoneIf given, overrides every item's codec (each image is re-encoded with it). If None (default), each Image keeps its own codec, and bare ndarrays default to ImageCompression.NONE. See ImageCompression.

Raises

ExceptionCondition
TypeErrorAn item isn't an Image/np.ndarray, or compression isn't a valid ImageCompression/matching int
ValueErrorAn item has an unsupported dtype or shape (per Image._validate)

Attributes

AttributeTypeDescription
shapeslist[tuple[int, ...]]Each image's pixel-array shape, in order.
dtypeslist[str]Each image's NumPy dtype name, in order.
compressionslist[ImageCompression]Each image's compression codec, in order.

Methods

MethodDescription
to_numpy(copy=True)Returns each image's pixel array as a list[np.ndarray]; copy=False returns the batch's internal arrays directly.
ImageBatch.coerce(value)Returns value unchanged if already an ImageBatch; otherwise wraps a list/tuple of Image/np.ndarray. Raises TypeError for anything else.

Operators

OperationBehavior
len(batch)Number of images.
batch[i]int returns a materialized Image (defensive copy; IndexError if out of range); slice or boolean np.ndarray mask returns an ImageBatch sub-batch. Anything else raises TypeError.
batch == otherTrue only if other is an ImageBatch with the same number of images and element-equal pixel arrays in order; compression is not compared. NotImplemented if other isn't an ImageBatch.
hash(batch)Not supported.

There's no __array__ -- images in a batch can have different shapes/dtypes, so the batch as a whole can't be viewed as one NumPy array. Use to_numpy() (a list) or index into one Image instead.

Serialization

Arrow layout:

text
StructArray length 1
├── data:        list<binary>  (length 1, inner length N; raw or ZSTD-framed per row)
├── height:      list<int32>   (length 1, inner length N)
├── width:       list<int32>   (length 1, inner length N)
├── channels:    list<int32>   (length 1, inner length N)
├── dtype:       list<string>  (length 1, inner length N)
└── compression: list<int8>    (length 1, inner length N)

One ImageBatch is serialized as one Arrow row, with all N images packed into the single list element of each field.

Visualization

datatypes.visualize(batch, entity_path=...) logs each image under its own indexed child path ({entity_path}/{i}) as rr.Image.

Example

python
"""Demonstrates the Telekinesis ImageBatch datatype."""

import time
from pathlib import Path

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

from telekinesis import datatypes

def image_batch_example():
    """Demonstrate creation, inspection, visualization, indexing, grayscale conversion, rebuilding, and serialization."""

    # ======================= Create ============================================
    ROOT_PATH = Path(__file__).parent
    image_1 = np.random.randint(0, 255, (512, 512, 3), dtype=np.uint8)
    image_2 = datatypes.Image.from_path(ROOT_PATH / "data/sample.jpg").to_numpy()
    images = [image_1, image_2]
    image_batch = datatypes.ImageBatch(images)
    logger.info(f"Original ImageBatch: {image_batch}")

    # ======================= Inspect ===========================================
    dtypes = image_batch.dtypes
    shapes = image_batch.shapes
    compressions = image_batch.compressions
    numpy_array = image_batch.to_numpy()

    logger.info(f"dtypes={dtypes}, shapes={shapes}, compressions={compressions}")
    logger.info(f"NumPy array: {numpy_array}")

    # ======================= Visualize =========================================
    rr.init("image_batch_example", spawn=True)
    datatypes.visualize(image_batch, entity_path="/ImageBatch")

    # ======================= Index =============================================
    index = 1
    image_at_index = image_batch[index]
    logger.info(f"Image at index {index}: {image_at_index}")
    datatypes.visualize(image_at_index, entity_path="/ImageBatch/Image_1")

    # ======================= Grayscale =========================================
    gray_image = image_at_index.to_grayscale()
    logger.info(f"Grayscale image at index {index}: {gray_image}")
    datatypes.visualize(gray_image, entity_path="/ImageBatch/Image_1/Grayscale")
    gray_image.save_to_path(ROOT_PATH / "data/grayscale_image.jpg")

    # ======================= Rebuild ===========================================
    index = 0
    updated_image = np.random.randint(0, 255, (1907, 512, 3), dtype=np.uint8)
    images[index] = updated_image
    image_batch = datatypes.ImageBatch(images)
    logger.info(f"Rebuilt ImageBatch at index 0: {image_batch}")

    # ======================= Serialize / Deserialize ===========================
    start = time.perf_counter()
    serialized = datatypes.serialize(image_batch)
    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 ImageBatch: {deserialized}")
    logger.info(f"Round-trip successful: {deserialized == image_batch}")
    logger.info(f"Serialization time: {serialization_ms:.3f} ms")
    logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")


if __name__ == "__main__":
    image_batch_example()