pvio.io — Video I/O
Low-level functions for reading and writing video files using imageio/FFmpeg.
pvio.io
VideoMetadata
Bases: NamedTuple
Basic video metadata returned by :func:get_video_metadata.
A lightweight typed record (tuple-compatible) with three fields:
Attributes:
| Name | Type | Description |
|---|---|---|
n_frames |
int
|
Total frame count. |
frame_size |
tuple[int, int]
|
|
fps |
float | None
|
Frames per second, or |
read_frames_from_video
read_frames_from_video(
video_path: Path | str,
frame_indices: list[int] | None = None,
) -> tuple[list[np.ndarray], float | None]
Read specific frames from a video file.
Frames are decoded with PyAV at the source's native bit depth and channel
layout: an 8-bit RGB video yields uint8 (H, W, 3) arrays, a 16-bit
source (e.g. lossless FFV1) yields uint16 arrays, a source with an alpha
channel yields 4-channel (H, W, 4) arrays, and a grayscale source yields
2-D (H, W) arrays. This preserves the real pixel values; decoding such
sources to 8-bit RGB (as imageio's reader does) would discard the low byte of
every 16-bit sample and collapse small values to near-zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path to the video file. |
required |
frame_indices
|
list[int] | None
|
Frame indices to read, in the order returned (duplicates
allowed). If |
None
|
Returns:
| Type | Description |
|---|---|
list[ndarray]
|
A 2-tuple |
float | None
|
dtype and channel count match the source (see above). fps is the |
tuple[list[ndarray], float | None]
|
average frame rate reported by the container, or |
Raises:
| Type | Description |
|---|---|
IndexError
|
If a requested frame index is out of range. |
Source code in src/pvio/io.py
iter_frames_from_video
Yield every frame of a video in order, without buffering the whole file.
Same per-frame output contract as :func:read_frames_from_video (native bit
depth and channel layout: uint8/uint16, with alpha or grayscale preserved),
but frames are decoded and yielded one at a time instead of being collected
into a list. Use this for large sequential-only workloads (e.g. piping
straight into an encoder, or streaming feature extraction) where holding
every decoded frame in memory at once is unnecessary or infeasible -- a
6000-frame 16-bit RGBA video, for example, is ~28 GB fully buffered.
Use :func:read_frames_from_video instead when random access (specific
frame indices) or the full in-memory list is actually needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path to the video file. |
required |
Yields:
| Type | Description |
|---|---|
ndarray
|
One numpy array per frame, in decode order, dtype/shape matching the |
ndarray
|
source as described above. |
Source code in src/pvio/io.py
write_frames_to_video
write_frames_to_video(
video_path: Path | str,
frames: list[ndarray],
fps: float,
*,
mode: str = "auto",
quality: int = _accel.DEFAULT_QUALITY,
preset: str | None = None,
extra_ffmpeg_params: list[str] | None = None,
log_interval: int | None = None,
quiet: bool = False,
) -> None
Write a sequence of frames to an H.264 MP4 file.
The encoder is chosen by mode: "gpu" encodes on the GPU (H.264 NVENC),
"cpu" on the CPU (libx264), and "auto" (default) uses the GPU when a
CUDA device and NVENC-capable FFmpeg are available, else the CPU. Either way
the encode always falls back to libx264 if NVENC fails (e.g. frames below
NVENC's minimum size), so output is always produced.
Quality is controlled by the single quality knob, applied as libx264's CRF on the CPU path and as NVENC's constant QP on the GPU path — both on the same 0-51 H.264 quantiser scale where lower means higher quality and larger files. The default of 20 is visually lossless and conservative, suitable for scientific data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path for the output video file. |
required |
frames
|
list[ndarray]
|
Frames as uint8 numpy arrays in |
required |
fps
|
float
|
Frames per second of the output video. |
required |
mode
|
str
|
Encoder selection — |
'auto'
|
quality
|
int
|
Encode quality on the 0-51 H.264 quantiser scale (lower = higher quality, larger files). Applied as libx264's CRF or NVENC's QP depending on the chosen encoder, so it behaves consistently across the CPU and GPU paths. |
DEFAULT_QUALITY
|
preset
|
str | None
|
Encoder preset. |
None
|
extra_ffmpeg_params
|
list[str] | None
|
Optional raw FFmpeg parameters appended after the quality/preset flags, as an escape hatch for advanced options. |
None
|
log_interval
|
int | None
|
If set, log progress every log_interval frames at
|
None
|
quiet
|
bool
|
Suppress the encoder-parameters log line and the progress bar.
When |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If frames is empty, contains frames with mismatched
dimensions, or mode is not one of |
Source code in src/pvio/io.py
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | |
write_image_paths_to_video
write_image_paths_to_video(
video_path: Path | str,
image_paths: list[Path | str],
fps: float,
*,
mode: str = "auto",
quality: int = _accel.DEFAULT_QUALITY,
preset: str | None = None,
extra_ffmpeg_params: list[str] | None = None,
log_interval: int | None = None,
quiet: bool = False,
) -> None
Combine on-disk image files into an H.264 MP4 file.
Like :func:write_frames_to_video, but the frames are given as paths to
image files (PNG, JPEG, TIFF, …) instead of in-memory numpy arrays. Images
are read lazily, one at a time, so an arbitrarily long sequence can be
encoded without holding every frame in memory at once. The encoder selection,
quality semantics, and NVENC→libx264 fallback are identical to
:func:write_frames_to_video.
Frames are encoded in the order given by image_paths; sort the paths beforehand if a particular ordering is required. The first image's spatial dimensions define the output size, and every subsequent image must match it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path for the output video file. |
required |
image_paths
|
list[Path | str]
|
Paths to the image files to combine, in output order. Each
file is read with imageio and must decode to an |
required |
fps
|
float
|
Frames per second of the output video. |
required |
mode
|
str
|
Encoder selection — |
'auto'
|
quality
|
int
|
Encode quality on the 0-51 H.264 quantiser scale (lower = higher
quality, larger files). See :func: |
DEFAULT_QUALITY
|
preset
|
str | None
|
Encoder preset. |
None
|
extra_ffmpeg_params
|
list[str] | None
|
Optional raw FFmpeg parameters appended after the quality/preset flags. |
None
|
log_interval
|
int | None
|
If set, log progress every log_interval frames at
|
None
|
quiet
|
bool
|
Suppress the encoder-parameters log line and the progress bar.
When |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If image_paths is empty, an image's dimensions differ from
the first image's, or mode is not one of
|
A missing or unreadable image path surfaces whatever error imageio raises
while reading it (typically :class:FileNotFoundError).
Source code in src/pvio/io.py
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
check_num_frames
Return the number of frames in a video file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path to the video file. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Total frame count. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the file cannot be opened. |
Source code in src/pvio/io.py
get_video_metadata
get_video_metadata(
video_path: Path | str,
cache_metadata: bool = True,
use_cached_metadata: bool = True,
metadata_suffix: str = ".metadata.json",
) -> VideoMetadata
Return frame count, frame size, and FPS for a video file.
Results are cached to a sidecar JSON file alongside the video to avoid
re-reading on subsequent calls. The cache stores the video's (size,
mtime) signature and is automatically invalidated (the metadata is re-read
and the cache rewritten) whenever that signature changes, i.e. whenever the
video has been modified, so using the cache is safe in practice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video_path
|
Path | str
|
Path to the video file. |
required |
cache_metadata
|
bool
|
Write metadata to a cache file after reading. |
True
|
use_cached_metadata
|
bool
|
Return cached metadata when the sidecar file exists
and its |
True
|
metadata_suffix
|
str
|
Suffix appended to the video filename to form the
cache path. Default: |
'.metadata.json'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
VideoMetadata
|
class: |
VideoMetadata
|
|
|
VideoMetadata
|
|