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:
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 |
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
|
collected_fields_by_mro |
bool
|
Whether the class fields were collected by method resolution order.
That is, correctly but unlike |
added_init |
bool
|
Whether the class has an attrs-generated |
added_repr |
bool
|
Whether the class has an attrs-generated |
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 |
added_match_args |
bool
|
Whether the class supports positional |
added_str |
bool
|
Whether the class has an attrs-generated |
added_pickling |
bool
|
Whether the class has attrs-generated |
on_setattr_hook |
Callable[[Any, Attribute[Any], Any], Any] | None
|
The class's |
field_transformer |
Callable[[Attribute[Any]], Attribute[Any]] | None
|
The class's |
.. 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__()
¶
__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)
¶
__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()
¶
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
write_frames(frames)
¶
Write multiple frames to the MJPEG video.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frames
|
list[ndarray]
|
List of frames to write. |
required |
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
|
Notes
This class can be used as a context manager to ensure the video is properly closed after writing. For example:
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 |
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
|
collected_fields_by_mro |
bool
|
Whether the class fields were collected by method resolution order.
That is, correctly but unlike |
added_init |
bool
|
Whether the class has an attrs-generated |
added_repr |
bool
|
Whether the class has an attrs-generated |
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 |
added_match_args |
bool
|
Whether the class supports positional |
added_str |
bool
|
Whether the class has an attrs-generated |
added_pickling |
bool
|
Whether the class has attrs-generated |
on_setattr_hook |
Callable[[Any, Attribute[Any], Any], Any] | None
|
The class's |
field_transformer |
Callable[[Attribute[Any]], Attribute[Any]] | None
|
The class's |
.. 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 |
__enter__()
¶
__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)
¶
__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()
¶
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)