A video: the frames one physical camera recorded.
One file, one sorted set of parts, or a folder of stills per camera; no geometry and
no name (the layout and the rig say which views it carries, the project's manifest
what it is called). A video holds no decoder, so there is nothing to close
(ADR-0101): open hands back a reader the caller owns,
and read opens one for a single frame. One instance per
video, hashed by identity, so views of one video share it; paths arrive resolved
(ADR-0042).
Video
The frames one camera recorded, as one stream.
Attributes:
| Name |
Type |
Description |
paths |
|
The files it is made of, in stream order.
|
Source code in src/deeperfly/video.py
| class Video:
"""The frames one camera recorded, as one stream.
Attributes:
paths: The files it is made of, in stream order.
"""
def __init__(self, paths: Sequence[str | Path]) -> None:
"""Wrap `paths`, resolved and in stream order, as one video (never empty)."""
self.paths = tuple(Path(p) for p in paths)
if not self.paths:
raise ValueError("a video has no files")
self._n_frames: int | None = None
self._fps: float | None = None
self._size: tuple[int, int] | None = None
def __repr__(self) -> str:
"""One line: the files it is made of, elided past the first few."""
shown = ", ".join(p.name for p in self.paths[:3])
if len(self.paths) > 3:
shown += f", ... ({len(self.paths)} files)"
return f"Video({shown})"
def open(self) -> FrameReader:
"""A new reader over this video, for a forward pass; the caller closes it."""
return open_reader(list(self.paths))
def read(self, index: int = 0) -> UInt8[np.ndarray, "H W 3"]:
"""Frame `index`, through a reader opened for it alone, so a seek per call.
More than a few frames read faster through one reader from
[open][deeperfly.video.Video.open].
"""
with self.open() as reader:
return reader[int(index)]
@property
def n_frames(self) -> int:
"""How many frames the video holds, read once.
Raises:
ValueError: If the source carries no frame count.
"""
if self._n_frames is None:
self._probe()
assert self._n_frames is not None
return self._n_frames
@property
def fps(self) -> float | None:
"""The frame rate the source declares, or `None` (an image sequence)."""
if self._n_frames is None:
self._probe()
return self._fps
@property
def size(self) -> tuple[int, int]:
"""The frame `(height, width)` in pixels, read from the first frame."""
if self._size is None:
self._probe()
assert self._size is not None
return self._size
def _probe(self) -> None:
"""Read the count, the rate and the frame size, through one reader."""
with self.open() as reader:
count = reader.count()
if count is None:
raise ValueError(f"cannot determine the frame count of {self.paths[0]}")
rate = reader.fps()
h, w = reader[0].shape[:2]
self._n_frames = int(count)
self._fps = float(rate) if rate else None
self._size = (int(h), int(w))
|
fps
property
The frame rate the source declares, or None (an image sequence).
n_frames
property
How many frames the video holds, read once.
Raises:
| Type |
Description |
ValueError
|
If the source carries no frame count.
|
size
property
The frame (height, width) in pixels, read from the first frame.
__init__
__init__(paths: Sequence[str | Path]) -> None
Wrap paths, resolved and in stream order, as one video (never empty).
Source code in src/deeperfly/video.py
| def __init__(self, paths: Sequence[str | Path]) -> None:
"""Wrap `paths`, resolved and in stream order, as one video (never empty)."""
self.paths = tuple(Path(p) for p in paths)
if not self.paths:
raise ValueError("a video has no files")
self._n_frames: int | None = None
self._fps: float | None = None
self._size: tuple[int, int] | None = None
|
__repr__
One line: the files it is made of, elided past the first few.
Source code in src/deeperfly/video.py
| def __repr__(self) -> str:
"""One line: the files it is made of, elided past the first few."""
shown = ", ".join(p.name for p in self.paths[:3])
if len(self.paths) > 3:
shown += f", ... ({len(self.paths)} files)"
return f"Video({shown})"
|
open
A new reader over this video, for a forward pass; the caller closes it.
Source code in src/deeperfly/video.py
| def open(self) -> FrameReader:
"""A new reader over this video, for a forward pass; the caller closes it."""
return open_reader(list(self.paths))
|
read
read(index: int = 0) -> UInt8[np.ndarray, 'H W 3']
Frame index, through a reader opened for it alone, so a seek per call.
More than a few frames read faster through one reader from
open.
Source code in src/deeperfly/video.py
| def read(self, index: int = 0) -> UInt8[np.ndarray, "H W 3"]:
"""Frame `index`, through a reader opened for it alone, so a seek per call.
More than a few frames read faster through one reader from
[open][deeperfly.video.Video.open].
"""
with self.open() as reader:
return reader[int(index)]
|