Skip to content

feat(ingest): store extracted video frames at a chosen scale - #814

Merged
JArmandoAnaya merged 16 commits into
mainfrom
feat/ingest-scale
Aug 28, 2026
Merged

feat(ingest): store extracted video frames at a chosen scale#814
JArmandoAnaya merged 16 commits into
mainfrom
feat/ingest-scale

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

What changed

Ingest-time downscale for video sources: registration takes an optional scale_percent
(1–100, default 100), and every extracted frame is stored at that percent of the clip's
native size per side.

  • Identity. The scale is a decomposition parameter, so it joins the source's identity
    beside the rate and the clip ranges: the same clip at another scale is a second source.
    Migration 18 reshapes the origin index to
    uq_source_project_kind_path_fps_ranges_scale; an unscaled clip omits the JSON key, so
    rows written in any generation share one spelling and nothing forks retroactively.
    Migration 16 became drop-only in the same change — only the head reshape may execute the
    shared index declaration, because that declaration is always the current spelling.
  • Extraction. _filtergraph gains a final scale=W:H stage with dimensions computed
    in Python from the probe (max(1, (native·pct+50)//100), integer half-up — mirrored
    exactly by the screen so preview and stored size cannot drift). The whole-clip 100%
    command stays byte-identical, pinned by test.
  • Surfaces. REST scale_percent form field, CLI --scale, MCP scale — all
    video-only, refused on a directory of stills the way --fps is. VideoProvenanceOut
    publishes the value; openapi.json, the generated client, and the MCP tool reference
    are regenerated.
  • UI. The video step gains a "Stored size" block: an always-visible readout
    (1280×720 · native, or 1280×720 → 640×360 · 50%), a native range slider with
    endpoint labels, and a purpose line stating the pixel cost. The step-2 fact grid shows
    the same arrow. The ingest preview player is now always muted, with the volume control
    hidden — a vision dataset has no use for the audio track.

Per-image scaling for image batches was implemented and then withdrawn during review; the
history carries both halves, and the final tree scales video only. Image directories store
stills at their decoded size — a uniform size remains the export recipes' job.

Found, not fixed

Nothing — the one defect found along the way (an object URL revoked under StrictMode's
double-mount, breaking dev-server thumbnails) was in this branch's own code and is gone
with it.

Test plan

  • uv run pytest tests/kernel tests/server tests/cli tests/mcp — green.
  • uv run lint-imports, uv run mypy src/visionset/kernel, uv run ruff check . — green.
  • openapi.json / generated client / docs/content/mcp-tools.md drift — clean.
  • pnpm -r build && pnpm -r test && pnpm -r lint — green; real-server cycle suite green.
  • Verified live in Chromium against a real server: scaled registration, stored frame
    dimensions on disk (640×360 from a 1280×720 clip at 50%), identity forking, the muted
    player, and the images panel unchanged.

@JArmandoAnaya
JArmandoAnaya merged commit 234477e into main Aug 28, 2026
15 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the feat/ingest-scale branch August 28, 2026 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant