Repository navigation
Python API Reference
Everything below is importable as shown. from vidlabel import ... gives the names listed in vidlabel.__all__; the rest live in the module named in each heading. Concepts and examples are in the other pages (start with concepts and getting started).
Dataset(path: str | Path)A vidlabel dataset: a folder of plain files (see docs/SPEC.md). Make one with Dataset.create(path), open one with
Dataset.open(path). There is no server and no lock: one writer per video at a time.
add_annotations(
rows: Iterable[Mapping[str, Any]],
video_id: str | None = None,
*,
create_labels: bool = False,
validate: bool = True,
coords: str = 'source',
) -> list[int]Append annotations. Rows are dicts using the column names (plus box=(x0,y0,x1,y1) shorthand).
A mask row may pass mask=<bool ndarray HxW>; it is encoded and its box filled in automatically.
Returns the new annotation ids. Raises ValidationError (writing nothing) if any row is invalid.
coords="source" (default): boxes/masks are in full source-video pixels.
coords="view": boxes/masks are in the pixels of the row's view_id (the crop as the pipeline saw it) and are
converted to source coordinates here (offset, plus scaling if the view has output_size). Masks must be
boolean arrays the size of the view region or of its output_size.
add_labels(labels: Iterable[str | Label | Mapping]) -> list[str]Add labels to the shared vocabulary (existing names are left alone). Returns the names actually added.
add_video(
meta: VideoMetadata | Mapping[str, Any],
exist_ok: bool = False,
*,
frame_index: FrameIndex | None = None,
) -> VideoMetadataRegister a video from a VideoMetadata (or a dict of its fields), e.g. from probe_video. The video file itself
is never copied. Raises FileExistsError if the id exists, unless exist_ok=True (which replaces the metadata).
frame_index (from probe_frame_index) is stored next to the metadata; see set_frame_index.
add_view(view: View | Mapping[str, Any], exist_ok: bool = False) -> ViewDefine a named crop and/or frame range of a registered video (see docs/views.md). The region must fit inside the
video, frame_range inside its length, and allowed_labels must already be in the vocabulary.
annotations(
video_id: str | None = None,
*,
kind: str | Sequence[str] | None = None,
label: str | Sequence[str] | None = None,
origin: str | Sequence[str] | None = None,
status: str | Sequence[str] | None = None,
view_id: str | Sequence[str | None] | None = None,
track_id: int | Sequence[int] | None = None,
frames: tuple[int, int] | None = None,
columns: Sequence[str] | None = None,
) -> pa.TableQuery annotations as a pyarrow Table. frames=(a, b) keeps annotations overlapping [a, b] inclusive.
view_id keeps rows labelled in that view (or any of several). Include None in a list to also (or only)
get whole-frame rows that have no view, e.g. view_id=[None]. track_id keeps rows of those tracks; a track id means
one object only within one video and view, so combine it with view_id.
build(**kwargs)Compile a training release: build(**kwargs) forwards to vidlabel.build.build and returns (Release, path).
create(path: str | Path, exist_ok: bool = False) -> 'Dataset'Make an empty dataset folder. Raises FileExistsError if one is already there, unless exist_ok=True.
delete_annotations(video_id: str, annotation_ids: Iterable[int], *, missing: str = 'raise') -> intRemove annotations of one video by id. Returns how many rows were removed. Ids that are not in this video raise
KeyError (and nothing is removed) unless missing="ignore".
delete_track(video_id: str, track_id: int, *, view_id: str | None = None) -> intRemove every row of one track. To keep it out of releases without losing it, use update_track(..., status="rejected").
derive_view_rows(
video_id: str,
view_id: str,
*,
labels: Sequence[str] | None = None,
origins: Sequence[str] | None = None,
statuses: Sequence[str] = ('unreviewed', 'approved'),
dry_run: bool = False,
) -> dict[str, Any]Copy whole-frame rows into a view, for the rows that belong there: frames inside the view's time windows, box or mask
entirely inside its region, label allowed in the view. Each copy keeps its columns (including track_id), gets the
view's view_id and a new annotation_id, and records its source in extra["derived_from"], so running this again
copies only what is new. The originals stay. Rows that only partly overlap the region are not copied (cutting a box
or mask would change it): they are counted in skipped_partial.
Returns copied, ids, already_derived, and the skip counts skipped_partial, skipped_outside, skipped_label,
skipped_frames. With dry_run=True nothing is written and copied is how many rows would be. Whole-frame samples (no view_id) then see both the original and the copy: build whole-frame releases
with build(view_ids=[None]) to leave the copies out.
frame_index(video_id: str) -> FrameIndex | NoneThe stored frame index of a video (timestamps and keyframes), or None if it has none.
The parsed manifest.json: spec version, video ids and view ids.
open(path: str | Path) -> 'Dataset'Open an existing dataset. Raises FileNotFoundError if the folder is not a vidlabel dataset.
resolve_video_path(video_id: str, roots: Sequence[str | Path] = ()) -> Path | NoneFind the video file: try each recorded source, then search roots for the same file name.
set_frame_index(video_id: str, index: FrameIndex) -> NoneStore (or replace) the frame index of a registered video. Raises ValueError if it has a different number of frames than the metadata says, or was made from a different file (hash mismatch).
stats() -> dict[str, Any]Counts for vidlabel info: totals, and breakdowns by kind, label, origin, status and view, plus one entry per video.
tracks(video_id: str, *, view_id: str | None = None) -> list[dict[str, Any]]One summary row per track of this video and view (view_id=None: whole-frame rows), sorted by track id: track_id,
labels, n_rows, first_frame, last_frame, mean_confidence (None if unscored), statuses (counts) and origins.
update_annotations(
video_id: str,
annotation_ids: Iterable[int],
*,
missing: str = 'raise',
**values: Any,
) -> intSet columns (e.g. status='approved', label='x') on the given annotations. Returns rows changed. Ids that are not in
this video raise KeyError (and nothing changes) unless missing="ignore"; giving no column to change is an error.
mask= (a boolean array of the frame's size, or encoded bytes) replaces the mask of mask rows and sets their box from it;
the result is checked like any write (box, size, kind), so a mask on a box row is refused.
update_track(video_id: str, track_id: int, *, view_id: str | None = None, **values: Any) -> intChange columns on every row of one track at once, e.g. update_track("clip", 7, status="rejected"). A track id means
one object only within one video and view, so pass view_id for a track made in a view. KeyError if there is no such track.
validate(deep: bool = False) -> list[Issue]Check the whole dataset against the spec and return a list of Issues (empty = valid). Never raises for bad data.
deep=True also decodes every mask and checks it against its box and the frame size.
video(video_id: str) -> VideoMetadataMetadata of one registered video. Raises KeyError for an unknown id.
video_ids() -> list[str]Ids of all registered videos, sorted.
view(view_id: str) -> ViewOne view definition. Raises KeyError for an unknown id.
view_ids() -> list[str]Ids of all views, sorted.
The parsed vocab.json: the one shared label list.
build(
ds: Dataset,
*,
statuses: Sequence[str] = ('unreviewed', 'approved'),
origins: Sequence[str] | None = None,
kinds: Sequence[str] | None = None,
video_ids: Sequence[str] | None = None,
view_ids: Sequence[str | None] | None = None,
row_group_size: int = 20000,
check: bool = True,
) -> tuple[Release, Path]Write compiled/<release_id>/{annotations.parquet, vocab.json, release.json}. Rejected annotations are excluded by default.
vocab.json is a snapshot of the label vocabulary, so class indices derived from a pinned release cannot change when labels are added to the dataset later.
The release id is the hash of the file's bytes: building an unchanged dataset again gives the same id, so a training run can record it and later read exactly the same table. (Annotation ids are random, so re-creating the data gives a new id; share the folder.)
vidlabel.build.Compiled(path: str | Path)Reader for a compiled release. Fast clip lookups use Parquet row-group statistics (the table is sorted).
clip(
video_id: str,
start: int,
end: int,
columns: Sequence[str] | None = None,
view_id: str | None = None,
) -> pa.TableAnnotations of one video overlapping frames [start, end] inclusive, reading only the row groups that can match.
view_id additionally keeps only rows labelled in that view.
read(columns: Sequence[str] | None = None) -> pa.TableThe whole release table (or only columns) as a pyarrow Table, sorted by video, start frame and annotation id.
The release id, e.g. r3fa91c0b2d44: derived from the table's contents.
row_groups_for(video_id: str, start: int, end: int) -> list[int]Indices of the Parquet row groups that can contain annotations of video_id overlapping frames [start, end].
The label vocabulary as it was when this release was built (None for releases made before it was saved).
vidlabel.samples.FrameSamples(
ds: Dataset,
*,
release: str | None = 'latest',
view_id: str | None = None,
videos: Sequence[str] | None = None,
kinds: Sequence[str] = ('box', 'mask'),
labels: Sequence[str] | None = None,
origins: Sequence[str] | None = None,
min_confidence: float | None = None,
frames: str = 'annotated',
stride: int = 1,
with_masks: bool = False,
box_format: str = 'xyxy',
normalized: bool = False,
label_offset: int = 0,
roots: Sequence[str | Path] = (),
cache: FrameCache | str | Path | None = None,
)Which frames to train on, and what the targets for each are. Build it once, then read samples by (video, frame).
Parameters (all keyword-only except ds):
-
release:"latest", a release id such as"r3fa91c0b2d44"(pinned: the same rows and class indices every time), orNoneto read the live annotations (unreviewed and approved), which can change between runs. -
view_id: train on that view's crop with only that view's annotations.None: full frames, all annotations. -
videos: restrict to these video ids (default: all). -
kinds: which kinds define the samples and targets, from"box","mask","marker". Markers always fillframe_labels; add"marker"to also sample frames that only have a marker. -
labels: keep only these labels; class indices then follow this order (default: the vocabulary's order). -
origins: keep only rows with these origins, e.g.["human"]. -
min_confidence: drop rows whose confidence is below this (rows with no confidence are kept). -
frames:"annotated"(frames with a selected annotation) or"all"(every frame). -
stride: keep every Nth frame of that list. -
with_masks: decode masks into the sample (masks, aligned withboxes). -
box_format:"xyxy","xywh"or"cxcywh";normalized=Truegives fractions of the image size. -
label_offset: added to class indices (e.g.1for detectors that reserve 0 for background). -
roots: folders to search for videos whose recorded path is stale. -
cache: aFrameCache(or the folder of one made byvidlabel cache). Frames it holds are read as image files instead of decoded from video; anything missing falls back to the video. See docs/training.md.
See docs/training.md for what a sample contains.
describe() -> dictWhat to log next to a training run so it can be reproduced.
frames_of(video_id: str) -> list[int]The frame numbers sampled from one video, in order.
iter_plan(
plan: ReadPlan,
*,
epoch: int = 0,
shard: int = 0,
num_shards: int = 1,
shuffle_buffer: int = 0,
max_open: int = 4,
) -> Iterator[dict]Yield the samples of one shard of one epoch of plan (NumPy samples, like sample), reading each keyframe interval once.
Chunks come in the plan's order for this epoch; frames inside a chunk are read in increasing order. A frame requested
several times is decoded once and yields one sample per request. shuffle_buffer (samples held in memory, each a whole
decoded frame) mixes neighbouring frames; 0 yields them as read. max_open videos are kept open at a time. For several
workers or ranks, give each its own shard out of num_shards: together they yield every sample exactly once.
plan_reads(requests: Iterable, *, seed: int = 0, block: int = 250) -> ReadPlanArrange the reading of a list of frames you chose, so each keyframe interval is decoded once (see ReadPlan).
requests holds (video_id, frame) pairs or integer positions in self.index (what numpy.random.Generator.choice(len(samples), ...)
returns), in any order, with repeats allowed. Which frames to request, and how likely each is, is entirely up to you.
Videos without a frame index are cut into blocks of block frames. Run the plan with iter_plan or PlannedStream.
reader(video_id: str)A reader for one video (read(frame), read_many(frames), close()), already set up with this object's view.
It uses the video's frame index when it has one, and the frame cache if there is one that matches the video.
Close it when done (or use with).
sample(video_id: str, frame: int, image: np.ndarray) -> dictThe full sample for a frame whose pixels you already have.
targets(video_id: str, frame: int, image_size: tuple[int, int]) -> dictBoxes, labels, masks and frame labels for one frame. image_size is (width, height) of the image returned.
video_ids() -> list[str]Videos that contribute at least one sample.
video_path(video_id: str) -> PathWhere the video file is on this machine (recorded paths first, then roots). Raises FileNotFoundError.
vidlabel.readplan.ReadPlan(
requests: Iterable[tuple[str, int]],
*,
indexes: Mapping[str, FrameIndex | None] | None = None,
frame_counts: Mapping[str, int] | None = None,
block: int = 250,
seed: int = 0,
)A reading plan for a list of requests. Build it with FrameSamples.plan_reads(...), or directly from (video_id, frame) pairs.
-
requests:(video_id, frame)pairs, in any order, repeats allowed (each repeat gives one more sample). -
indexes: video id -> itsFrameIndex(or None). Videos without one are cut into blocks ofblockframes, which is only an approximation of the real keyframes, so costs for them are estimates. -
frame_counts: video id -> number of frames; if given, requests outside the video raise ValueError. -
seed: makes the chunk order and its sharding reproducible; together with the epoch it fixes the order of every epoch.
order(epoch: int = 0) -> list[Chunk]All chunks in this epoch's order: the same for a given seed and epoch, different between epochs.
shard(epoch: int = 0, shard: int = 0, num_shards: int = 1) -> list[Chunk]The chunks one worker (or rank) reads in this epoch, in reading order. Every chunk belongs to exactly one shard,
whatever num_shards is, and shards get nearly the same number of samples (they differ by at most one chunk's worth).
shard_sizes(epoch: int = 0, num_shards: int = 1) -> list[int]Samples per shard, to see how even the split is (distributed training wants equal numbers).
summary() -> dict[str, int]samples, distinct_frames, videos, chunks, and the estimated frames decoded per epoch under this plan
(est_decoded_frames) against reading the same requests one at a time (est_decoded_frames_one_by_one).
vidlabel.readplan.Chunk(
video_id: str,
start: int,
frames: tuple[int, ...],
repeats: tuple[int, ...],
cost: int,
) -> NoneThe requested frames of one video inside one keyframe interval.
VideoStream, VideoFrames and PlannedStream are documented in training; collate_frames(batch) batches samples (stacking images that share a size) and to_tensors(sample) converts a NumPy sample to tensors (image as uint8 CHW).
read_frames(
path: str | Path,
frame_indices: Iterable[int],
meta: VideoMetadata,
*,
view: View | None = None,
) -> Iterator[tuple[int, np.ndarray]]Yield (frame_index, RGB frame) for each requested index, in increasing order.
Raises ValueError if the file's frame size does not match meta (wrong or re-encoded file) and IndexError if a
requested frame is past the end of the video.
read_frame(
path: str | Path,
index: int,
meta: VideoMetadata,
*,
view: View | None = None,
) -> np.ndarrayOne frame, RGB, displayed orientation. Decodes from the start of the video: use read_frames or FrameReader for many.
check_video(path: str | Path, meta: VideoMetadata, *, verify_hash: bool = False) -> list[str]Compare a video file with the metadata registered for it. Returns a list of problems (empty = consistent).
crop_to_view(frame: np.ndarray, view: View | None) -> np.ndarrayCrop a displayed-orientation frame to a view's region and resize to its output_size (if any).
seek_mismatches(
path: str | Path,
meta: VideoMetadata,
positions: Iterable[int],
index: FrameIndex | None = None,
) -> list[int]Frames at positions for which seeking does NOT return exactly what sequential decoding returns.
Empty means seeking is frame-exact at those positions. OpenCV's seek is exact for ordinary constant-frame-rate
files but can land a few frames off on variable-frame-rate video or odd containers, which would silently misalign
every box, so without an index seeking is only used after this check (see FrameReader). With index, the reader
under test is the indexed one.
vidlabel.frames.FrameReader(
path: str | Path,
meta: VideoMetadata,
view: View | None = None,
*,
seek: str = 'auto',
seek_threshold: int = 100,
index: FrameIndex | None = None,
)Keeps one video open and reads frames by index.
Reading forwards only decodes what is in between. Asking for an earlier frame, or one far ahead, means decoding from
a keyframe; seek and index decide how that is done:
- With
index(aFrameIndex): the reader knows where the keyframes are, so it jumps only when that decodes fewer frames than reading on, and never inside a keyframe interval it is already in. After every frame it compares the decoder's timestamp with the index; if they ever disagree it warns and falls back to sequential decoding, so a wrong frame is never returned silently. This works on variable-frame-rate video too. An index for a different file (hash or frame count differs) is ignored with a warning. - Without an index,
seek="auto"(default) seeks only if it has been verified for this file: not variable frame rate, and the frames returned after seeking are identical to sequential decoding at a few test positions (checked once per file per process). Otherwise it decodes sequentially, with a warning. -
seek="off": never seek (the index, if any, is not used). -
seek="always": seek without the pixel check (for tests and forseek_mismatches).
read_many reads several frames at once in the cheapest order. For training prefer VideoStream, which reads each
video front to back. Not thread-safe; use one reader per process or thread.
close() -> NoneRelease the open video file. Safe to call twice; the reader reopens it on the next read.
read(index: int) -> np.ndarrayRGB frame index in displayed orientation, cropped to the view if there is one.
read_many(indices: Iterable[int]) -> list[np.ndarray]Frames for indices, in the order given. They are read in increasing order so each keyframe interval is
decoded once however the request is ordered; repeated indices are decoded once and copied.
Whether this reader will use seeking (without an index, runs the one-off check in "auto" mode).
render_preview(
ds: Dataset,
video_id: str,
out_dir: str | Path,
*,
frames: Sequence[int] | None = None,
video_path: str | Path | None = None,
view_id: str | None = None,
mp4: bool = False,
roots: Sequence[str | Path] = (),
style: PreviewStyle | None = None,
) -> list[Path]Write annotated frames (PNG, or one MP4) to out_dir and return the paths.
With view_id, shows that view's crop with only that view's annotations, as its pipeline would see them.
draw_annotations(
frame: np.ndarray,
rows: Iterable[dict],
frame_index: int,
*,
view: View | None = None,
style: PreviewStyle | None = None,
) -> np.ndarrayReturn a copy of an RGB frame with the annotations active at frame_index drawn on it.
Rejected rows are drawn dashed, dimmed and prefixed rej, under the others. Labels are kept inside the frame and moved
so that they do not cover each other. Span markers are listed as captions at the top left.
parse_color(text: str) -> tuple[int, int, int]'red', '#ff8800', 'ff8800' or '255,136,0' -> (r, g, b).
vidlabel.preview.PreviewStyle(
color: tuple[int, int, int] | None = None,
label_colors: Mapping[str, tuple[int, int, int]] = <factory>,
thickness: int | None = None,
font_scale: float | None = None,
show_labels: bool = True,
outline: bool = True,
mask_opacity: float = 0.5,
) -> NoneHow annotations are drawn. Anything left as None is chosen automatically from the frame size.
color_for(label: str) -> tuple[int, int, int]The colour used for a label: its entry in label_colors, else color, else a stable colour derived from the name.
probe_video(path: str | Path, video_id: str | None = None, *, hash: bool = True) -> VideoMetadataRead a video's facts with ffprobe (size, fps as a fraction, frame count, rotation, codec, bit rate) and hash the file
unless hash=False. Needs ffprobe on the PATH. video_id defaults to the file name without extension.
file_hash(path: str | Path) -> strsha256:<hex> of a file's contents.
probe_frame_index(path: str | Path, *, content_hash: str | None = None) -> FrameIndexRead a video's per-frame timestamps and keyframes from its packet table with ffprobe. Nothing is decoded, so it takes
seconds even for long videos. Pass content_hash (from the metadata) to record which file the index belongs to.
Raises ValueError if the container has no timestamps (some raw streams), and RuntimeError without ffprobe.
reconcile_frame_count(meta: VideoMetadata, index: FrameIndex) -> VideoMetadataMake meta.frame_count agree with index. If the count was only estimated when the video was probed, the index (an
exact count) replaces it; a real disagreement raises ValueError, because one of the two describes a different file.
vidlabel.cache.FrameCache(root: str | Path)Frames saved as <root>/<video_id>/<frame>.jpg|png, plus <root>/cache.json saying which video file each came from.
Open an existing cache with FrameCache(root); make or extend one with FrameCache.build(samples, root).
build(
samples,
root: str | Path,
*,
format: str = 'jpg',
quality: int = 95,
overwrite: bool = False,
) -> tuple['FrameCache', CacheStats]Decode every frame samples (a FrameSamples) will use, in order, and save it under root.
Frames already cached are kept unless overwrite=True. Returns the cache and what was done. Needs OpenCV.
has(video_id: str, frame: int) -> boolread(video_id: str, frame: int) -> np.ndarrayThe cached frame as an RGB uint8 array (whole frame, displayed orientation). KeyError if it is not cached.
usable(video_id: str, meta: VideoMetadata) -> boolWhether this cache has frames for the video as registered: same file (hash) and same size.
vidlabel.cache.CachedReader(
cache: FrameCache,
video_id: str,
meta: VideoMetadata,
view: View | None,
fallback,
)Same interface as FrameReader (read, read_many, close), but frames come from a FrameCache when it has them
and from the video file otherwise, so a partial cache is fine. Crops to view like FrameReader does.
close() -> Noneread(index: int)read_many(indices: Iterable[int]) -> listFrames for indices in the order given; frames missing from the cache are decoded together, in increasing order.
vidlabel.cache.CacheStats(written: int, skipped: int, bytes: int, seconds: float) -> NoneWhat FrameCache.build did.
encode(mask) -> bytesEncode a boolean (H, W) array: crop to its bounding box, bit-pack the rows, zstd-compress. Lossless.
decode(blob: bytes) -> np.ndarrayFull-frame boolean mask (H, W).
decode_crop(blob: bytes) -> tuple[np.ndarray, int, int]Decode only the cropped region: (crop[h, w] bool, y0, x0). Skips allocating the full frame.
bbox(blob: bytes) -> tuple[int, int, int, int] | None(x0, y0, x1, y1) of the mask, or None if empty.
info(blob: bytes) -> dictRead only the header: frame size, crop rectangle. Cheap.
place_in_frame(mask, x: int, y: int, frame_w: int, frame_h: int) -> np.ndarrayPaste a crop-sized mask into an empty full-frame canvas with its top-left at (x, y); parts outside are clipped.
resize_nearest(mask, h: int, w: int) -> np.ndarrayNearest-neighbour resize of a boolean mask to (h, w).
from_coco_rle(rle: dict) -> np.ndarrayDecode a COCO RLE dict {'size': [h, w], 'counts': list[int] | str} to a boolean (h, w) mask.
to_coco_rle(mask, compressed: bool = False) -> dictA boolean mask as a COCO run-length dict (counts, size); compressed=True gives the compact string form.
convert_boxes(
boxes,
src: str = 'xyxy',
dst: str = 'xyxy',
*,
size: tuple[int, int] | None = None,
src_normalized: bool = False,
dst_normalized: bool = False,
) -> np.ndarrayConvert an (N, 4) array of boxes. size is (width, height) of the image they refer to; it is required when
src_normalized or dst_normalized is set. Returns float32.
rotate_boxes(boxes, rotation: int, stored_size: tuple[int, int]) -> np.ndarrayMove (N, 4) xyxy boxes from the stored frame to the displayed orientation of a rotated video.
rotation is the video's clockwise rotation (0, 90, 180 or 270, as in VideoMetadata.rotation) and stored_size is
(width, height) of the stored frame. Annotations in a vidlabel dataset use the displayed orientation, so a source that
drew boxes on the unrotated stored frame needs this before add_annotations. x1/y1 stay exclusive.
validate_dataset(path: str | Path, deep: bool = False) -> list[Issue]Validate a whole dataset directory. Never raises for bad data; returns issues.
check_annotation_table(
table: pa.Table,
*,
frame_count: int | None = None,
size: tuple[int, int] | None = None,
vocab_names: set[str] | None = None,
views: Mapping[str, View] | None = None,
video_id: str | None = None,
deep_masks: bool = False,
) -> list[Issue]Check one annotation table. size is (width, height) in display orientation.
views maps view_id -> View; rows tagged with a view must lie inside it (region, frame range, allowed labels).
validate() returns a list of Issue(level, code, message, count, sample_ids); level is "error" or "warning". ValidationError is raised by writes and by build when there are errors; its .issues lists them. The codes are listed in the spec.
These pydantic models are the contract; unknown fields are rejected (custom data goes in extra).
Generic facts about one video. Lives in videos/<video_id>/metadata.json.
| field | type | default | description |
|---|---|---|---|
video_id |
str |
required | |
content_hash |
`str | None` | none |
duration_s |
`float | None` | none |
frame_count |
int |
required | |
fps |
Rational |
required | |
timebase |
`Rational | None` | none |
is_vfr |
bool |
False | |
width |
int |
required | |
height |
int |
required | |
rotation |
int |
0 | Clockwise degrees (0, 90, 180, 270) applied to the stored frame to display it upright. 90 and 270 swap width and height. |
codec |
`str | None` | none |
pix_fmt |
`str | None` | none |
bit_rate |
`int | None` | none |
sources |
list[VideoSource] |
empty | |
ingested_at |
`datetime.datetime | None` | none |
extra |
dict[str, Any] |
empty |
One known location of the video file. Several may be listed; paths move, hashes don't.
| field | type | default | description |
|---|---|---|---|
uri |
str |
required | |
note |
`str | None` | none |
Optional per-frame timing of one video, stored in videos/<video_id>/frame_index.json.
Read from the file's packet table (no decoding), so it is cheap to make. It lets a reader seek by timestamp, including in variable-frame-rate video, check where a seek really landed, and plan reads so each keyframe interval is decoded once.
pts holds one presentation timestamp per frame, in timebase ticks, in display order (so pts[i] belongs to frame
i) and strictly increasing. keyframes lists the frame numbers where decoding can start without earlier frames.
| field | type | default | description |
|---|---|---|---|
content_hash |
`str | None` | none |
timebase |
Rational |
required | |
pts |
list[int] |
required | |
keyframes |
list[int] |
empty |
| field | type | default | description |
|---|---|---|---|
num |
int |
required | |
den |
int |
required |
| field | type | default | description |
|---|---|---|---|
x |
int |
required | |
y |
int |
required | |
width |
int |
required | |
height |
int |
required |
A named crop and/or time range of a video. No new video is made; annotations stay in source coordinates.
region is in the video's displayed orientation. If the crop is resized before it reaches a pipeline, give
output_size (width, height) so coordinates can be converted back. allowed_labels optionally limits which
labels may be used in this view (e.g. a caption strip that only allows caption labels).
| field | type | default | description |
|---|---|---|---|
view_id |
str |
required | |
video_id |
str |
required | |
name |
`str | None` | none |
region |
`Region | None` | none |
output_size |
`tuple[int, int] | None` | none |
frame_range |
`tuple[int, int] | None` | none |
frame_ranges |
`list[tuple[int, int]] | None` | none |
allowed_labels |
`list[str] | None` | none |
extra |
dict[str, Any] |
empty |
| field | type | default | description |
|---|---|---|---|
name |
str |
required | |
parent |
`str | None` | none |
description |
`str | None` | none |
extra |
dict[str, Any] |
empty |
The one shared list of labels.
| field | type | default | description |
|---|---|---|---|
version |
str |
'0' | |
labels |
list[Label] |
empty |
Dataset-level index. Lives in manifest.json.
| field | type | default | description |
|---|---|---|---|
spec_version |
str |
'0.6.0' | |
videos |
list[str] |
empty | |
views |
list[str] |
empty | |
vocab_version |
`str | None` | none |
extra |
dict[str, Any] |
empty |
Describes one compiled (training) build. release_id is derived from content, so a training run can pin it.
| field | type | default | description |
|---|---|---|---|
release_id |
str |
required | |
spec_version |
str |
required | |
created_at |
datetime.datetime |
required | |
n_rows |
int |
required | |
n_videos |
int |
required | |
row_group_size |
int |
required | |
files |
dict[str, str] |
required | file name -> 'sha256:' |
extra |
dict[str, Any] |
empty | Build filters etc. |
Source: docs/python-api.md
vidlabel 0.6.0 · pages are generated from the repository's docs/ folder; to change one, edit docs/ and run python docs/build_wiki.py.
Start here
Tutorials
Guides
Reference
Background
- Architecture
- Design decisions
- Performance
- Compatibility and versioning
- Limitations and roadmap
- Security
- Web interface design
Project