Video
Upload videos, probe metadata, and extract frames as images into a dataset.
Pictograph annotates frames, not videos. The video resource handles upload and frame extraction; once extracted, frames are regular images you annotate with the standard SAM3 / annotation workflows.
from pictograph import Client
client = Client()
upload
Three-step upload (signed URL → PUT → register), same pattern as images.
from pathlib import Path
info = client.video.upload(
file_path=Path("./recording.mp4"),
dataset_id="proj-uuid",
folder_path="/raw-footage",
)
print(info.gcs_path, info.video_id)
| Arg | Type | Default | Notes |
|---|---|---|---|
file_path | str | Path | required | Local video file |
dataset_id | str | required | Destination dataset |
folder_path | str | "/" | Virtual folder |
Supported codecs: anything ffmpeg can demux (H.264, H.265, VP9, AV1, etc.).
probe
Inspect a video’s metadata without extracting frames. Pass the
gcs_path returned by upload().
meta = client.video.probe(info.gcs_path)
print(meta.duration_seconds, meta.fps, meta.width, meta.height)
print(meta.codec, meta.frame_count)
Returns VideoMetadata from a server-side ffprobe invocation.
extract_frames
Extract frames from a video into the destination dataset as images.
job = client.video.extract_frames(
gcs_path=info.gcs_path,
dataset_id="proj-uuid",
folder_path="/raw-footage/frames",
fps=2.0, # extract 2 frames per second of source video
start_seconds=10.0,
end_seconds=120.0,
max_frames=200, # cap on output count
wait=True,
poll_interval=5.0,
timeout=1800.0,
)
print(job.status, job.frames_extracted)
Frames are written as {video_basename}_{frame_index:06d}.jpg in the
target folder. Each becomes a regular Image row — ready for
annotation, search, training.
| Arg | Type | Default | Notes |
|---|---|---|---|
gcs_path | str | required | Source video |
dataset_id | str | required | Destination dataset |
folder_path | str | "/" | Virtual folder for the extracted frames |
fps | float | 1.0 | Frames per source second |
start_seconds | float | None | None | Skip the first N seconds |
end_seconds | float | None | None | Stop at second N |
max_frames | int | None | None | Cap on output count |
wait | bool | True | Poll until terminal |
fps=1.0 is the cheapest setting; fps=30.0 extracts every frame of a
30 fps source. Frame extraction does not consume credits — you pay
only for the storage of the resulting images.
get_extraction / wait_for_extraction
job = client.video.get_extraction(job_id)
job = client.video.wait_for_extraction(job_id, timeout=600.0)
Common errors
| Status | Exception | Cause |
|---|---|---|
| 404 | NotFoundError | gcs_path missing or dataset_id invalid |
| 415 | ValidationError | Unsupported codec |
| 408 | PollTimeoutError | Long videos may exceed default timeout |
| 413 | ApiError | Video file exceeds upload limit (10 GB) |