Skip to content

video_writing

sleap_io.io.video_writing

Utilities for writing videos.

Classes:

Name Description
MJPEGFrameWriter

Video writer for MJPEG format optimized for seekable frame containers.

VideoWriter

Simple video writer using imageio and FFMPEG.

Attributes:

Name Type Description
__cached__

str(object='') -> str

__doc__

str(object='') -> str

__file__

str(object='') -> str

__name__

str(object='') -> str

__package__

str(object='') -> str

__cached__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/io/__pycache__/video_writing.cpython-313.pyc' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__doc__ = 'Utilities for writing videos.' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__file__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/io/video_writing.py' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__name__ = 'sleap_io.io.video_writing' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__package__ = 'sleap_io.io' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

MJPEGFrameWriter

Video writer for MJPEG format optimized for seekable frame containers.

This writer is designed for scientific/archival use where frames from arbitrary indices need to be independently seekable. Each frame is intra-coded (I-frame only) to ensure reliable random access.

Attributes:

Name Type Description
filename

Path to output MJPEG video file.

fps

Nominal frames per second. Defaults to 30.

quality

MJPEG quality level (2-31, lower is better). Defaults to 2.

output_params

Additional output parameters for FFMPEG.

Notes

This class can be used as a context manager:

with MJPEGFrameWriter("output.avi") as writer:
    for frame in frames:
        writer.write_frame(frame)

Methods:

Name Description
__enter__

Context manager entry.

__eq__

Method generated by attrs for class MJPEGFrameWriter.

__exit__

Context manager exit.

__init__

Method generated by attrs for class MJPEGFrameWriter.

__repr__

Method generated by attrs for class MJPEGFrameWriter.

__setattr__

Method generated by attrs for class MJPEGFrameWriter.

build_output_params

Build the output parameters for FFMPEG MJPEG encoding.

close

Close the MJPEG writer.

open

Open the MJPEG writer.

write_frame

Write a frame to the MJPEG video.

write_frames

Write multiple frames to the MJPEG video.

Source code in sleap_io/io/video_writing.py
@attrs.define
class MJPEGFrameWriter:
    """Video writer for MJPEG format optimized for seekable frame containers.

    This writer is designed for scientific/archival use where frames from arbitrary
    indices need to be independently seekable. Each frame is intra-coded (I-frame only)
    to ensure reliable random access.

    Attributes:
        filename: Path to output MJPEG video file.
        fps: Nominal frames per second. Defaults to 30.
        quality: MJPEG quality level (2-31, lower is better). Defaults to 2.

        output_params: Additional output parameters for FFMPEG.

    Notes:
        This class can be used as a context manager:

        ```python
        with MJPEGFrameWriter("output.avi") as writer:
            for frame in frames:
                writer.write_frame(frame)
        ```
    """

    filename: Path = attrs.field(converter=Path)
    fps: float = 30
    quality: int = 2

    output_params: list[str] = attrs.field(factory=list)
    _writer: "imageio.plugins.ffmpeg.FfmpegFormat.Writer | None" = None
    _frame_index: int = 0

    def build_output_params(self) -> list[str]:
        """Build the output parameters for FFMPEG MJPEG encoding."""
        params = [
            # MJPEG quality (2-32)
            "-q:v",
            str(self.quality),
            # All frames are keyframes (I-frames)
            "-g",
            "1",
            # Use full range (JPEG) color
            "-vf",
            "scale=in_range=pc:out_range=pc,format=yuv420p",
            "-color_range",
            "pc",
        ]

        return params + self.output_params

    def open(self):
        """Open the MJPEG writer."""
        self.close()
        self._frame_index = 0

        self.filename.parent.mkdir(parents=True, exist_ok=True)
        self._writer = iio_v2.get_writer(
            self.filename.as_posix(),
            format="FFMPEG",
            fps=self.fps,
            codec="mjpeg",
            pixelformat="yuv420p",  # Use full range YUV for MJPEG
            output_params=self.build_output_params(),
        )

    def close(self):
        """Close the MJPEG writer."""
        if self._writer is not None:
            self._writer.close()
            self._writer = None
            self._frame_index = 0

    def write_frame(self, frame: np.ndarray):
        """Write a frame to the MJPEG video.

        Args:
            frame: Frame to write. Should be a 2D or 3D numpy array with
                dimensions (height, width) or (height, width, channels).
        """
        if self._writer is None:
            self.open()

        self._writer.append_data(frame)
        self._frame_index += 1

    def write_frames(self, frames: list[np.ndarray]):
        """Write multiple frames to the MJPEG video.

        Args:
            frames: List of frames to write.
        """
        for frame in frames:
            self.write_frame(frame)

    def __enter__(self):
        """Context manager entry."""
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> bool | None:
        """Context manager exit."""
        self.close()
        return False

__annotations__ = {'filename': 'Path', 'fps': 'float', 'quality': 'int', 'output_params': 'list[str]', '_writer': "'imageio.plugins.ffmpeg.FfmpegFormat.Writer | None'", '_frame_index': 'int'} class-attribute

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

__attrs_own_setattr__ = True class-attribute

Returns True when the argument is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

__attrs_props__ = ClassProps(is_exception=False, is_slotted=True, has_weakref_slot=True, is_frozen=False, kw_only=<KeywordOnly.NO: 'no'>, collected_fields_by_mro=True, added_init=True, added_repr=True, added_eq=True, added_ordering=False, hashability=<Hashability.UNHASHABLE: 'unhashable'>, added_match_args=True, added_str=False, added_pickling=True, on_setattr_hook=<function pipe.<locals>.wrapped_pipe at 0x7f08471ce840>, field_transformer=None) class-attribute

Effective class properties as derived from parameters to attr.s() or define() decorators.

This is the same data structure that attrs uses internally to decide how to construct the final class.

Warning:

This feature is currently **experimental** and is not covered by our
strict backwards-compatibility guarantees.

Attributes:

Name Type Description
is_exception bool

Whether the class is treated as an exception class.

is_slotted bool

Whether the class is slotted <slotted classes>.

has_weakref_slot bool

Whether the class has a slot for weak references.

is_frozen bool

Whether the class is frozen.

kw_only KeywordOnly

Whether / how the class enforces keyword-only arguments on the __init__ method.

collected_fields_by_mro bool

Whether the class fields were collected by method resolution order. That is, correctly but unlike dataclasses.

added_init bool

Whether the class has an attrs-generated __init__ method.

added_repr bool

Whether the class has an attrs-generated __repr__ method.

added_eq bool

Whether the class has attrs-generated equality methods.

added_ordering bool

Whether the class has attrs-generated ordering methods.

hashability Hashability

How hashable <hashing> the class is.

added_match_args bool

Whether the class supports positional match <match> over its fields.

added_str bool

Whether the class has an attrs-generated __str__ method.

added_pickling bool

Whether the class has attrs-generated __getstate__ and __setstate__ methods for pickle.

on_setattr_hook Callable[[Any, Attribute[Any], Any], Any] | None

The class's __setattr__ hook.

field_transformer Callable[[Attribute[Any]], Attribute[Any]] | None

The class's field transformers <transform-fields>.

.. versionadded:: 25.4.0

__doc__ = 'Video writer for MJPEG format optimized for seekable frame containers.\n\nThis writer is designed for scientific/archival use where frames from arbitrary\nindices need to be independently seekable. Each frame is intra-coded (I-frame only)\nto ensure reliable random access.\n\nAttributes:\n filename: Path to output MJPEG video file.\n fps: Nominal frames per second. Defaults to 30.\n quality: MJPEG quality level (2-31, lower is better). Defaults to 2.\n\n output_params: Additional output parameters for FFMPEG.\n\nNotes:\n This class can be used as a context manager:\n\n ```python\n with MJPEGFrameWriter("output.avi") as writer:\n for frame in frames:\n writer.write_frame(frame)\n ```\n' class-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__firstlineno__ = 179 class-attribute

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer iteral.

int('0b100', base=0) 4

__match_args__ = ('filename', 'fps', 'quality', 'output_params', '_writer', '_frame_index') class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__module__ = 'sleap_io.io.video_writing' class-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__slots__ = ('filename', 'fps', 'quality', 'output_params', '_writer', '_frame_index', '__weakref__') class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__static_attributes__ = ('_frame_index', '_writer') class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__weakref__ property

list of weak references to the object

__enter__()

Context manager entry.

Source code in sleap_io/io/video_writing.py
def __enter__(self):
    """Context manager entry."""
    return self

__eq__(other)

Method generated by attrs for class MJPEGFrameWriter.

Source code in sleap_io/io/video_writing.py
This preserves coordinate alignment by only adding padding to the bottom and right
edges of the frame. Without this, encoding with x264 may scale or pad symmetrically,
causing coordinate shifts.

Args:
    frame: Frame to pad. Should be a 2D or 3D numpy array with dimensions
        (height, width) or (height, width, channels).
    macro_block_size: Block size to align to. Defaults to 16 for x264.

Returns:

__exit__(exc_type, exc_value, traceback)

Context manager exit.

Source code in sleap_io/io/video_writing.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> bool | None:
    """Context manager exit."""
    self.close()
    return False

__init__(filename, fps=30, quality=2, output_params=NOTHING, writer=None, frame_index=0)

Method generated by attrs for class MJPEGFrameWriter.

Source code in sleap_io/io/video_writing.py
    Padded frame with dimensions divisible by macro_block_size, or the original
    frame if no padding is needed.
"""
h, w = frame.shape[:2]

# Calculate padding needed (only bottom/right)
pad_h = (macro_block_size - (h % macro_block_size)) % macro_block_size
pad_w = (macro_block_size - (w % macro_block_size)) % macro_block_size

if pad_h == 0 and pad_w == 0:
    return frame

__repr__()

Method generated by attrs for class MJPEGFrameWriter.

Source code in sleap_io/io/video_writing.py
"""Utilities for writing videos."""

from __future__ import annotations

from pathlib import Path
from types import TracebackType

import attrs
import imageio
import imageio.v2 as iio_v2
import numpy as np


def _pad_to_macro_block(frame: np.ndarray, macro_block_size: int = 16) -> np.ndarray:
    """Pad frame to be divisible by macro_block_size, padding only bottom/right.

__setattr__(name, val)

Method generated by attrs for class MJPEGFrameWriter.

build_output_params()

Build the output parameters for FFMPEG MJPEG encoding.

Source code in sleap_io/io/video_writing.py
def build_output_params(self) -> list[str]:
    """Build the output parameters for FFMPEG MJPEG encoding."""
    params = [
        # MJPEG quality (2-32)
        "-q:v",
        str(self.quality),
        # All frames are keyframes (I-frames)
        "-g",
        "1",
        # Use full range (JPEG) color
        "-vf",
        "scale=in_range=pc:out_range=pc,format=yuv420p",
        "-color_range",
        "pc",
    ]

    return params + self.output_params

close()

Close the MJPEG writer.

Source code in sleap_io/io/video_writing.py
def close(self):
    """Close the MJPEG writer."""
    if self._writer is not None:
        self._writer.close()
        self._writer = None
        self._frame_index = 0

open()

Open the MJPEG writer.

Source code in sleap_io/io/video_writing.py
def open(self):
    """Open the MJPEG writer."""
    self.close()
    self._frame_index = 0

    self.filename.parent.mkdir(parents=True, exist_ok=True)
    self._writer = iio_v2.get_writer(
        self.filename.as_posix(),
        format="FFMPEG",
        fps=self.fps,
        codec="mjpeg",
        pixelformat="yuv420p",  # Use full range YUV for MJPEG
        output_params=self.build_output_params(),
    )

write_frame(frame)

Write a frame to the MJPEG video.

Parameters:

Name Type Description Default
frame ndarray

Frame to write. Should be a 2D or 3D numpy array with dimensions (height, width) or (height, width, channels).

required
Source code in sleap_io/io/video_writing.py
def write_frame(self, frame: np.ndarray):
    """Write a frame to the MJPEG video.

    Args:
        frame: Frame to write. Should be a 2D or 3D numpy array with
            dimensions (height, width) or (height, width, channels).
    """
    if self._writer is None:
        self.open()

    self._writer.append_data(frame)
    self._frame_index += 1

write_frames(frames)

Write multiple frames to the MJPEG video.

Parameters:

Name Type Description Default
frames list[ndarray]

List of frames to write.

required
Source code in sleap_io/io/video_writing.py
def write_frames(self, frames: list[np.ndarray]):
    """Write multiple frames to the MJPEG video.

    Args:
        frames: List of frames to write.
    """
    for frame in frames:
        self.write_frame(frame)

VideoWriter

Simple video writer using imageio and FFMPEG.

Attributes:

Name Type Description
filename

Path to output video file.

fps

Frames per second. Defaults to 30.

pixelformat

Pixel format for video. Defaults to "yuv420p".

codec

Codec to use for encoding. Defaults to "libx264".

crf

Constant rate factor to control lossiness of video. Values go from 2 to 32, with numbers in the 18 to 30 range being most common. Lower values mean less compressed/higher quality. Defaults to 25. No effect if codec is not "libx264".

preset

H264 encoding preset. Defaults to "superfast". No effect if codec is not "libx264".

keyframe_interval

Interval between keyframes in seconds. If None, uses encoder default. Lower values improve seeking but increase file size. Defaults to None.

no_audio

If True, strips audio from the output. Defaults to False.

output_params

Additional output parameters for FFMPEG. This should be a list of strings corresponding to command line arguments for FFMPEG and libx264. Use ffmpeg -h encoder=libx264 to see all options for libx264 output_params.

Notes

This class can be used as a context manager to ensure the video is properly closed after writing. For example:

with VideoWriter("output.mp4") as writer:
    for frame in frames:
        writer(frame)

Methods:

Name Description
__call__

Write a frame to the video.

__enter__

Context manager entry.

__eq__

Method generated by attrs for class VideoWriter.

__exit__

Context manager exit.

__init__

Method generated by attrs for class VideoWriter.

__repr__

Method generated by attrs for class VideoWriter.

__setattr__

Method generated by attrs for class VideoWriter.

build_output_params

Build the output parameters for FFMPEG.

close

Close the video writer.

open

Open the video writer.

write_frame

Write a frame to the video.

Source code in sleap_io/io/video_writing.py
@attrs.define
class VideoWriter:
    """Simple video writer using imageio and FFMPEG.

    Attributes:
        filename: Path to output video file.
        fps: Frames per second. Defaults to 30.
        pixelformat: Pixel format for video. Defaults to "yuv420p".
        codec: Codec to use for encoding. Defaults to "libx264".
        crf: Constant rate factor to control lossiness of video. Values go from 2 to 32,
            with numbers in the 18 to 30 range being most common. Lower values mean less
            compressed/higher quality. Defaults to 25. No effect if codec is not
            "libx264".
        preset: H264 encoding preset. Defaults to "superfast". No effect if codec is not
            "libx264".
        keyframe_interval: Interval between keyframes in seconds. If None, uses encoder
            default. Lower values improve seeking but increase file size. Defaults to
            None.
        no_audio: If True, strips audio from the output. Defaults to False.
        output_params: Additional output parameters for FFMPEG. This should be a list of
            strings corresponding to command line arguments for FFMPEG and libx264. Use
            `ffmpeg -h encoder=libx264` to see all options for libx264 output_params.

    Notes:
        This class can be used as a context manager to ensure the video is properly
        closed after writing. For example:

        ```python
        with VideoWriter("output.mp4") as writer:
            for frame in frames:
                writer(frame)
        ```
    """

    filename: Path = attrs.field(converter=Path)
    fps: float = 30
    pixelformat: str = "yuv420p"
    codec: str = "libx264"
    crf: int = 25
    preset: str = "superfast"
    keyframe_interval: float | None = None
    no_audio: bool = False
    output_params: list[str] = attrs.field(factory=list)
    _writer: "imageio.plugins.ffmpeg.FfmpegFormat.Writer | None" = None

    def build_output_params(self) -> list[str]:
        """Build the output parameters for FFMPEG."""
        output_params = []
        if self.codec == "libx264":
            output_params.extend(
                [
                    "-crf",
                    str(self.crf),
                    "-preset",
                    self.preset,
                ]
            )
        # Add keyframe interval (GOP size)
        if self.keyframe_interval is not None:
            gop_size = max(1, int(self.fps * self.keyframe_interval))
            output_params.extend(["-g", str(gop_size)])
        # Strip audio if requested
        if self.no_audio:
            output_params.extend(["-an"])
        return output_params + self.output_params

    def open(self):
        """Open the video writer."""
        self.close()

        self.filename.parent.mkdir(parents=True, exist_ok=True)
        self._writer = iio_v2.get_writer(
            self.filename.as_posix(),
            format="FFMPEG",
            fps=self.fps,
            codec=self.codec,
            pixelformat=self.pixelformat,
            output_params=self.build_output_params(),
            # Disable imageio's auto-scaling for non-divisible frame sizes.
            # We handle padding manually in write_frame() to preserve coordinates.
            macro_block_size=1,
        )

    def close(self):
        """Close the video writer."""
        if self._writer is not None:
            self._writer.close()
            self._writer = None

    def write_frame(self, frame: np.ndarray):
        """Write a frame to the video.

        Args:
            frame: Frame to write to video. Should be a 2D or 3D numpy array with
                dimensions (height, width) or (height, width, channels).

        Notes:
            For libx264 codec, frames are automatically padded to dimensions divisible
            by 16 (the macro block size). Padding is only added to the bottom and right
            edges to preserve coordinate alignment.
        """
        if self._writer is None:
            self.open()

        if self.codec == "libx264":
            frame = _pad_to_macro_block(frame, macro_block_size=16)

        self._writer.append_data(frame)

    def __enter__(self):
        """Context manager entry."""
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> bool | None:
        """Context manager exit."""
        self.close()
        return False

    def __call__(self, frame: np.ndarray):
        """Write a frame to the video.

        Args:
            frame: Frame to write to video. Should be a 2D or 3D numpy array with
                dimensions (height, width) or (height, width, channels).
        """
        self.write_frame(frame)

__annotations__ = {'filename': 'Path', 'fps': 'float', 'pixelformat': 'str', 'codec': 'str', 'crf': 'int', 'preset': 'str', 'keyframe_interval': 'float | None', 'no_audio': 'bool', 'output_params': 'list[str]', '_writer': "'imageio.plugins.ffmpeg.FfmpegFormat.Writer | None'"} class-attribute

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

__attrs_own_setattr__ = True class-attribute

Returns True when the argument is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

__attrs_props__ = ClassProps(is_exception=False, is_slotted=True, has_weakref_slot=True, is_frozen=False, kw_only=<KeywordOnly.NO: 'no'>, collected_fields_by_mro=True, added_init=True, added_repr=True, added_eq=True, added_ordering=False, hashability=<Hashability.UNHASHABLE: 'unhashable'>, added_match_args=True, added_str=False, added_pickling=True, on_setattr_hook=<function pipe.<locals>.wrapped_pipe at 0x7f08471ce840>, field_transformer=None) class-attribute

Effective class properties as derived from parameters to attr.s() or define() decorators.

This is the same data structure that attrs uses internally to decide how to construct the final class.

Warning:

This feature is currently **experimental** and is not covered by our
strict backwards-compatibility guarantees.

Attributes:

Name Type Description
is_exception bool

Whether the class is treated as an exception class.

is_slotted bool

Whether the class is slotted <slotted classes>.

has_weakref_slot bool

Whether the class has a slot for weak references.

is_frozen bool

Whether the class is frozen.

kw_only KeywordOnly

Whether / how the class enforces keyword-only arguments on the __init__ method.

collected_fields_by_mro bool

Whether the class fields were collected by method resolution order. That is, correctly but unlike dataclasses.

added_init bool

Whether the class has an attrs-generated __init__ method.

added_repr bool

Whether the class has an attrs-generated __repr__ method.

added_eq bool

Whether the class has attrs-generated equality methods.

added_ordering bool

Whether the class has attrs-generated ordering methods.

hashability Hashability

How hashable <hashing> the class is.

added_match_args bool

Whether the class supports positional match <match> over its fields.

added_str bool

Whether the class has an attrs-generated __str__ method.

added_pickling bool

Whether the class has attrs-generated __getstate__ and __setstate__ methods for pickle.

on_setattr_hook Callable[[Any, Attribute[Any], Any], Any] | None

The class's __setattr__ hook.

field_transformer Callable[[Attribute[Any]], Attribute[Any]] | None

The class's field transformers <transform-fields>.

.. versionadded:: 25.4.0

__doc__ = 'Simple video writer using imageio and FFMPEG.\n\nAttributes:\n filename: Path to output video file.\n fps: Frames per second. Defaults to 30.\n pixelformat: Pixel format for video. Defaults to "yuv420p".\n codec: Codec to use for encoding. Defaults to "libx264".\n crf: Constant rate factor to control lossiness of video. Values go from 2 to 32,\n with numbers in the 18 to 30 range being most common. Lower values mean less\n compressed/higher quality. Defaults to 25. No effect if codec is not\n "libx264".\n preset: H264 encoding preset. Defaults to "superfast". No effect if codec is not\n "libx264".\n keyframe_interval: Interval between keyframes in seconds. If None, uses encoder\n default. Lower values improve seeking but increase file size. Defaults to\n None.\n no_audio: If True, strips audio from the output. Defaults to False.\n output_params: Additional output parameters for FFMPEG. This should be a list of\n strings corresponding to command line arguments for FFMPEG and libx264. Use\n `ffmpeg -h encoder=libx264` to see all options for libx264 output_params.\n\nNotes:\n This class can be used as a context manager to ensure the video is properly\n closed after writing. For example:\n\n ```python\n with VideoWriter("output.mp4") as writer:\n for frame in frames:\n writer(frame)\n ```\n' class-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__firstlineno__ = 46 class-attribute

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer iteral.

int('0b100', base=0) 4

__match_args__ = ('filename', 'fps', 'pixelformat', 'codec', 'crf', 'preset', 'keyframe_interval', 'no_audio', 'output_params', '_writer') class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__module__ = 'sleap_io.io.video_writing' class-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__slots__ = ('filename', 'fps', 'pixelformat', 'codec', 'crf', 'preset', 'keyframe_interval', 'no_audio', 'output_params', '_writer', '__weakref__') class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__static_attributes__ = ('_writer',) class-attribute

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

__weakref__ property

list of weak references to the object

__call__(frame)

Write a frame to the video.

Parameters:

Name Type Description Default
frame ndarray

Frame to write to video. Should be a 2D or 3D numpy array with dimensions (height, width) or (height, width, channels).

required
Source code in sleap_io/io/video_writing.py
def __call__(self, frame: np.ndarray):
    """Write a frame to the video.

    Args:
        frame: Frame to write to video. Should be a 2D or 3D numpy array with
            dimensions (height, width) or (height, width, channels).
    """
    self.write_frame(frame)

__enter__()

Context manager entry.

Source code in sleap_io/io/video_writing.py
def __enter__(self):
    """Context manager entry."""
    return self

__eq__(other)

Method generated by attrs for class VideoWriter.

Source code in sleap_io/io/video_writing.py
This preserves coordinate alignment by only adding padding to the bottom and right
edges of the frame. Without this, encoding with x264 may scale or pad symmetrically,
causing coordinate shifts.

Args:
    frame: Frame to pad. Should be a 2D or 3D numpy array with dimensions
        (height, width) or (height, width, channels).
    macro_block_size: Block size to align to. Defaults to 16 for x264.

Returns:
    Padded frame with dimensions divisible by macro_block_size, or the original
    frame if no padding is needed.
"""
h, w = frame.shape[:2]

__exit__(exc_type, exc_value, traceback)

Context manager exit.

Source code in sleap_io/io/video_writing.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> bool | None:
    """Context manager exit."""
    self.close()
    return False

__init__(filename, fps=30, pixelformat='yuv420p', codec='libx264', crf=25, preset='superfast', keyframe_interval=None, no_audio=False, output_params=NOTHING, writer=None)

Method generated by attrs for class VideoWriter.

Source code in sleap_io/io/video_writing.py
# Calculate padding needed (only bottom/right)
pad_h = (macro_block_size - (h % macro_block_size)) % macro_block_size
pad_w = (macro_block_size - (w % macro_block_size)) % macro_block_size

if pad_h == 0 and pad_w == 0:
    return frame

# Pad only bottom and right
if frame.ndim == 2:
    return np.pad(frame, ((0, pad_h), (0, pad_w)), mode="constant")
else:
    return np.pad(frame, ((0, pad_h), (0, pad_w), (0, 0)), mode="constant")

__repr__()

Method generated by attrs for class VideoWriter.

Source code in sleap_io/io/video_writing.py
"""Utilities for writing videos."""

from __future__ import annotations

from pathlib import Path
from types import TracebackType

import attrs
import imageio
import imageio.v2 as iio_v2
import numpy as np


def _pad_to_macro_block(frame: np.ndarray, macro_block_size: int = 16) -> np.ndarray:
    """Pad frame to be divisible by macro_block_size, padding only bottom/right.

__setattr__(name, val)

Method generated by attrs for class VideoWriter.

build_output_params()

Build the output parameters for FFMPEG.

Source code in sleap_io/io/video_writing.py
def build_output_params(self) -> list[str]:
    """Build the output parameters for FFMPEG."""
    output_params = []
    if self.codec == "libx264":
        output_params.extend(
            [
                "-crf",
                str(self.crf),
                "-preset",
                self.preset,
            ]
        )
    # Add keyframe interval (GOP size)
    if self.keyframe_interval is not None:
        gop_size = max(1, int(self.fps * self.keyframe_interval))
        output_params.extend(["-g", str(gop_size)])
    # Strip audio if requested
    if self.no_audio:
        output_params.extend(["-an"])
    return output_params + self.output_params

close()

Close the video writer.

Source code in sleap_io/io/video_writing.py
def close(self):
    """Close the video writer."""
    if self._writer is not None:
        self._writer.close()
        self._writer = None

open()

Open the video writer.

Source code in sleap_io/io/video_writing.py
def open(self):
    """Open the video writer."""
    self.close()

    self.filename.parent.mkdir(parents=True, exist_ok=True)
    self._writer = iio_v2.get_writer(
        self.filename.as_posix(),
        format="FFMPEG",
        fps=self.fps,
        codec=self.codec,
        pixelformat=self.pixelformat,
        output_params=self.build_output_params(),
        # Disable imageio's auto-scaling for non-divisible frame sizes.
        # We handle padding manually in write_frame() to preserve coordinates.
        macro_block_size=1,
    )

write_frame(frame)

Write a frame to the video.

Parameters:

Name Type Description Default
frame ndarray

Frame to write to video. Should be a 2D or 3D numpy array with dimensions (height, width) or (height, width, channels).

required
Notes

For libx264 codec, frames are automatically padded to dimensions divisible by 16 (the macro block size). Padding is only added to the bottom and right edges to preserve coordinate alignment.

Source code in sleap_io/io/video_writing.py
def write_frame(self, frame: np.ndarray):
    """Write a frame to the video.

    Args:
        frame: Frame to write to video. Should be a 2D or 3D numpy array with
            dimensions (height, width) or (height, width, channels).

    Notes:
        For libx264 codec, frames are automatically padded to dimensions divisible
        by 16 (the macro block size). Padding is only added to the bottom and right
        edges to preserve coordinate alignment.
    """
    if self._writer is None:
        self.open()

    if self.codec == "libx264":
        frame = _pad_to_macro_block(frame, macro_block_size=16)

    self._writer.append_data(frame)