Skip to content

sim2bot.CameraFrame

CameraFrame dataclass

One encoded camera frame plus capture and stream metadata.

Attributes:

  • camera_id (str) –

    Global scene camera ID used for the subscription.

  • codec (int) –

    Numeric wire codec: 0 JPEG, 1 reserved H.264, or 2 raw RGBA.

  • keyframe (bool) –

    Whether the frame is marked as independently decodable.

  • sequence (int) –

    Monotonically increasing sequence number for this feed.

  • capture_ts (float) –

    Browser wall-clock capture timestamp in Unix seconds.

  • width (int) –

    Frame width in pixels.

  • height (int) –

    Frame height in pixels.

  • data (bytes) –

    Encoded image bytes, excluding the Sim2Bot frame header.

Source code in sim2bot/client.py
@dataclass
class CameraFrame:
    """One encoded camera frame plus capture and stream metadata.

    Attributes:
        camera_id: Global scene camera ID used for the subscription.
        codec: Numeric wire codec: 0 JPEG, 1 reserved H.264, or 2 raw RGBA.
        keyframe: Whether the frame is marked as independently decodable.
        sequence: Monotonically increasing sequence number for this feed.
        capture_ts: Browser wall-clock capture timestamp in Unix seconds.
        width: Frame width in pixels.
        height: Frame height in pixels.
        data: Encoded image bytes, excluding the Sim2Bot frame header.
    """

    camera_id: str
    codec: int
    keyframe: bool
    sequence: int
    capture_ts: float
    width: int
    height: int
    data: bytes

    @property
    def age(self) -> float:
        """Return the current frame age in seconds.

        This is an approximate capture-to-consumer measurement based on wall
        clocks. It includes browser production, bridge relay, and Python delay.

        Returns:
            Elapsed wall-clock seconds since the browser captured this frame.
        """
        return time.time() - self.capture_ts

    def image(self) -> Any:
        """Decode the frame to a NumPy BGR image.

        JPEG and raw RGBA frames are supported. Raw WebGL frames are vertically
        flipped and converted from RGBA to BGR. H.264 decoding is reserved for a
        future implementation.

        Returns:
            A ``height × width × 3`` uint8 BGR NumPy array. A truncated raw frame
            returns ``None``.

        Raises:
            RuntimeError: If the optional OpenCV/NumPy dependencies are absent.
            NotImplementedError: If the frame uses the reserved H.264 codec.

        Examples:
            Install ``sim2bot[cv2]``, then decode a received frame::

                frame = stream.read(timeout=2.0)
                if frame is not None:
                    image_bgr = frame.image()
        """
        if self.codec == _CODEC_H264:
            raise NotImplementedError("H.264 decode needs the sim2bot[av] extra.")
        try:
            import cv2  # type: ignore
            import numpy as np  # type: ignore
        except ImportError as exc:  # pragma: no cover - dependency hint
            raise RuntimeError(
                "Decoding camera frames needs OpenCV + numpy: pip install 'sim2bot[cv2]'"
            ) from exc
        if self.codec == _CODEC_RAW:
            # Uncompressed RGBA, bottom-up (WebGL order): reshape, flip, RGBA->BGR.
            need = self.width * self.height * 4
            if len(self.data) < need:
                return None
            arr = np.frombuffer(self.data, np.uint8, count=need)
            img = arr.reshape(self.height, self.width, 4)[::-1]
            return cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)
        return cv2.imdecode(np.frombuffer(self.data, np.uint8), cv2.IMREAD_COLOR)

Practical examples

Examples use objects discovered from the current scene rather than hard-coded robot metadata.

Inspect metadata

frame = feed.read()
if frame:
    print(frame.camera_id, frame.sequence)
    print(frame.width, frame.height, frame.age)

Access encoded bytes

frame = feed.read()
if frame:
    encoded = frame.data
    print(len(encoded), "bytes")

Guidance

Usage tip

Use data when forwarding encoded JPEG; call image() only when your Python code needs decoded pixels.


See also