annie.media.preview¶
Build Browse preview images for a video (service; needs the media extra).
Produces the static thumbnail (clean first frame) and the annotated five-frame
strip (vdet boxes in blue, the protagonist track in green) shown on each Browse
row. Decoding is lazy via annie.decode, so importing this module never
requires torch; call annie.decode.media_available() before using it.
Attributes¶
Functions¶
|
Encode a PIL image as a self-contained |
|
Evenly sample |
|
Decode a video's strip and draw its annotations. |
|
Decode a video's middle (½) frame with its annotations drawn on it. |
|
Decode a plain strip spanning one band's |
|
Log a failed frame decode once, and return the reason to show on the frame box. |
Module Contents¶
- annie.media.preview.HIDPI_SCALE = 2¶
- annie.media.preview.to_data_uri(image: PIL.Image.Image, box: tuple[int, int] | None = None) str[source]¶
Encode a PIL image as a self-contained
data:WebP URI.Embedding the pixels in the element (rather than serving a per-client temp file) means a thumbnail/strip frame never produces an orphaned static route that 404s after a reconnect. The flip side is that every embedded frame is held in memory twice — server-side in the element’s props, and again in the browser tab — for as long as its row is on the page. So the image is first downscaled to the box it will actually be displayed in (times
HIDPI_SCALE) and encoded as lossy WebP rather than lossless PNG, which cuts a Browse row’s payload by roughly 15x.- Parameters:
image – The image to encode.
box – The
(width, height)CSS size of the element that will show the image. The image is shrunk to fitHIDPI_SCALEtimes this, preserving aspect ratio.Noneencodes at full resolution.
- Returns:
A
data:image/webp;base64,…string usable as aui.imagesource.
- annie.media.preview.band_frame_indices(start_sec: float, end_sec: float, fps: float, num_frames: int, count: int = 5) list[int][source]¶
Evenly sample
countframe indices across the span[start_sec, end_sec).Converts the band’s seconds to frame indices via
fpsand pickscountpositions spread across the span (inclusive of both ends whencount > 1), so a clip’s preview strip shows that span rather than the whole video. Indices are clamped into[0, num_frames - 1]and a degenerate or reversed span collapses to the single start frame.- Parameters:
start_sec – Span start, in seconds.
end_sec – Span end, in seconds.
fps – Frames per second of the source video.
num_frames – Total frame count, used to clamp indices.
count – Number of frames to sample.
- Returns:
count(or fewer, when the span is a single frame) frame indices, ascending.
- annie.media.preview.build_preview(entry: annie.core.models.VideoEntry, count: int = 5) tuple[PIL.Image.Image, list[PIL.Image.Image], int][source]¶
Decode a video’s strip and draw its annotations.
- Parameters:
entry – The video to preview;
entry.video_pathmust be set.count – Number of strip frames (first / ¼ / ½ / ¾ / last by default).
- Returns:
the clean first frame as a thumbnail, a list of annotated strip frames (vdet blue, active track green), and the video’s total frame count.
- Return type:
A
(thumbnail, strip, num_frames)triple- Raises:
ValueError – If the entry has no video to decode.
annie.decode.MediaUnavailableError – If the
mediaextra is absent.
- annie.media.preview.build_grid_preview(entry: annie.core.models.VideoEntry) tuple[PIL.Image.Image, int][source]¶
Decode a video’s middle (½) frame with its annotations drawn on it.
The Browse grid view shows a single static frame per video, so this is the fast counterpart to
build_preview(): it decodes only three sample frames in one pass and keeps the middle one."approximate"seek is safe here because the middle index — unlike the strip’s last frame — never risks the tail overshoot thatannie.media.decode.read_strip()guards against with"exact"mode.- Parameters:
entry – The video to preview;
entry.video_pathmust be set.- Returns:
the annotated middle frame (vdet blue, active track green) and the video’s total frame count.
- Return type:
An
(image, num_frames)pair- Raises:
ValueError – If the entry has no video to decode.
annie.media.decode.MediaUnavailableError – If the
mediaextra is absent.
- annie.media.preview.build_band_strip(video_path: str | pathlib.Path, start_sec: float, end_sec: float, count: int = 5) list[PIL.Image.Image][source]¶
Decode a plain strip spanning one band’s
[start_sec, end_sec)window.Used by the Segment-review task to preview a clip’s span. Unlike
build_preview()this draws no track/vdet overlay — the reviewer is judging the temporal cut, so the raw frames of the span are what matters — and it decodes only the sampled frames of that span rather than the whole video.- Parameters:
video_path – The long video the clip belongs to.
start_sec – Span start, in seconds.
end_sec – Span end, in seconds.
count – Number of frames to sample across the span.
- Returns:
The sampled span frames as RGB images (empty if nothing could be decoded).
- Raises:
annie.media.decode.MediaUnavailableError – If the
mediaextra is absent.
- annie.media.preview.report_decode_failure(video: pathlib.Path | str | None, exc: BaseException) str[source]¶
Log a failed frame decode once, and return the reason to show on the frame box.
Every Browse row and Annotator strip decodes on its own, so one broken decoder would otherwise fail silently dozens of times, or flood the Log tab. A decoder that cannot load at all is one problem for every video and is logged once per process; any other failure is logged once per video.
- Parameters:
video – The video that failed to decode.
exc – The exception the decode raised.
- Returns:
The short reason, for a tooltip on the broken-frame icon.