Video search on top of the Review grid (rebase of #1743) - #1898
Video search on top of the Review grid (rebase of #1743)#1898mattdawkins wants to merge 54 commits into
Conversation
New top-level Review tab (desktop: after Training; web: after Models) showing many annotations at once as cropped image chips, so a whole class (or everything carrying an attribute) can be checked across several datasets and wrong types corrected in place, without opening each sequence in the viewer. - Datasets view / Grid view toggle: pick datasets through the platform picker or listing (multicam parents expand into their cameras), then page through matching annotations in a rows x columns grid (5x4 by default, settable, with zoom in/out keeping the shape) and a context slider for the margin around each box (30% by default). - Query by type above a confidence threshold, or by attribute key/value on tracks and/or detections; sort by dataset, confidence or frame. The grid keeps its entries until the query is re-run so edits never reshuffle it. - Chips are cropped client-side from image sequences or by seeking a hidden video element (shared frame->time mapping extracted from VideoAnnotator into videoSeek.ts), through a concurrency-limited queue that renders the first box of every visible entry before any track's extra frames. Track entries then cycle through up to 8 boxes sampled along the track, with the object kept centred. - Types are edited per cell (assign, or mark correct) or for a whole page, batched and saved through saveDetections; unsaved edits are counted and guarded on navigation. - Clicking a chip opens the viewer on that dataset, seeking to the frame and selecting the track (new initialFrame / initialTrackId Viewer props read from the route query on both platforms). - Api gains an optional peekConfig for reading dataset configs without the viewer bookkeeping (desktop recents, web browse location); the web dataset store now shares its config merge with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
A square chip letterboxed into a wide cell wasted most of the cell, so the crop region now extends the padded square around the box to the cell's width/height ratio (coarsened to a tenth so window resizes rarely force a re-render), keeping the object centred whatever the grid shape. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Move the review page's paging, zoom, chip-resolution and keyboard handling into useReviewGrid, its grid-shape/context/pager row into ReviewGridControls, and the persisted grid settings into gridSettings.ts, so another view showing ReviewItems (e.g. video search results) gets the same behaviour without copying the page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Per-dataset search index lifecycle (build via a process_video.py job, status from an index_meta.json sidecar + ITQ file check, delete) and a persistent QueryServiceManager wrapping viame.core.query_service over NDJSON stdio (open index, formulate from image chip + boxes, query with optional warm-start model, refine with +/- feedback, export the SVM as a runnable trained pipeline in DIVE_Pipelines). One index open at a time; the embedded postgres is stopped gracefully on app cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New in-viewer context panel (desktop only) for index-backed video search and rapid model generation: build/rebuild/delete the dataset's search index (via the GPU job queue), query from the selected annotation, an external image file, or a saved .svm model warm-start, adjudicate ranked results +/- with cropped chip thumbnails and click-to-seek, refine iteratively, and save good models as runnable trained pipelines. Shared request/response types live in apispec so web can implement later. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace per-dataset index directories with a single shared search database (DIVE_SearchIndex): every table keys rows on a per-video stream identifier, so datasets are added, updated, and removed independently while one IQR session searches everything at once. - Add/Update ingests a dataset into the shared database as a job (initializing it on first use), re-using stream identifiers derived from the dataset id (image sequences) or video filename stem. - Remove deletes the dataset's rows through the query service and drops it from the membership metadata; deleting a dataset from DIVE also removes it from the index automatically. - Query results attribute back to their source dataset via stream_id; the panel labels cross-dataset results, seeks within the current dataset, crops thumbnails from each result's own media, and can filter the display to the current dataset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New fullscreen 5x4 grid page, opened from the Video Search panel, showing the ranked results across every indexed video as cropped chips with accept/reject buttons; marks share the panel's session state and feed the same IQR refine loop. Chip rendering moves to a shared, concurrency-limited loader (useResultChips) with context padding and the result box outlined, and the cell (AdjudicationChip) is presentation-only so future annotation cluster rows can reuse it. Also: extracted video frames are now cached per source video (hash-keyed) instead of colliding across videos on frame number, and cross-dataset chip media resolves the file the media server actually serves, so transcoded copies work when originals have moved.
Track results load their first-state chip immediately, then the grid queues up to 8 frames evenly sampled along the track (visible page only, at lower priority than primary chips) and each cell cycles through whichever frames have loaded, with a filmstrip badge marking animated cells. Static detection results are unchanged.
Follow renames that landed on main since the branch was cut: the desktop project store type is JsonConfig and is read with loadJsonConfig from the project's datasetFileAbsPath, the frontend metadata loader is loadConfig, the typed IPC invoke is used in the GPU job queue, and a spawn without a pid now fails the index build instead of storing an undefined pid. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The full-window results grid is now the review chip grid: results map to ReviewItems (searchResultItems.ts), chips are cropped client-side through the review frame sources (a lazy per-dataset registry, so cross-dataset results need no backend frame extraction), and the grid shape, zoom, context and paging come from useReviewGrid / ReviewGridControls with the same persisted settings as the Review page. Cells keep the accept/reject actions through ReviewCell's slots and a highlight border; results without a box show the whole frame. useResultChips.ts and AdjudicationChip.vue are replaced by useSearchChips.ts, which also feeds the side panel's row thumbnails. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The shared search index no longer needs an embedded PostgreSQL server: run_bulk.py --index-backend files writes one set of files per stream (manifest, descriptor CSV, float32 descriptor array, uids, ITQ hash codes, tracks) that the query service reads directly. index_meta.json records the backend the index was built with, every later build joins it, and removing a dataset deletes its stream files. The PostgreSQL backend is still supported for indexes recorded (or predating) that backend, through the same code paths as before. Also follow the VIAME tool renames (run_bulk.py, database.py, index_generic.pipe) and drop the initdb requirement from the install check unless the postgres backend is in use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
|
Update: the index storage moved from embedded PostgreSQL to per-stream file bundles, following the storage comparison.
Verified against a VIAME main build: index build, open, query, refine and model export on both backends through |
The desktop backend now invokes the viame index applet (index.py add / remove) instead of assembling run_bulk.py arguments and issuing psql scripts itself: the applet initialises and starts PostgreSQL when that backend is in use, runs the ingest pipeline, and refreshes the hashes. The install check looks for index.py. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
|
Follow-up: DIVE now drives the new |
|
This branch has been merged into the VIAME fork's |
…ng, overlays and sharper chips The Results panel is now the first and default panel, with the Datasets panel second; with no dataset added it says so and links across. Cell type fields and captions scale up as the grid shows fewer entries, and the threshold and context sliders and fields are larger. Right clicking a cell (or its edit action) opens the frame it is showing for editing in place: the box gains drag handles, and polygon vertices and head/tail points can be dragged too; Enter/Apply keeps the edit, Esc/Cancel drops it, and the chip is re-cropped around the new box. Polygons and head/tail points are drawn over every chip. Opening the viewer from a cell seeks to the frame the cell is showing and selects the track. Paging quickly only loads the page landed on: queued work for pages passed over is dropped at once and new loads wait for paging to settle. Chips are rendered at the cell's resolution with high-quality resampling and stored losslessly when upscaled, so small objects are as sharp as the source allows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
…ame stepping and auto-save Polygon vertices now move while dragged (the draft arrays are replaced instead of index-assigned, which Vue 2 cannot see). Editing handles are the circles the annotator draws, in the type's colour and red while dragged, and a right click while editing locks the change in. A single click on a chip no longer opens the viewer (double click or the action does), so a slip after an edit does not raise the unsaved-changes prompt. Track cells gain arrows to step through their sampled frames, which pauses the cycling until resumed. When auto-save is enabled in the settings, review edits are saved after the same delay the annotator uses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
…zoom, live queries and toolbar polish Starting an edit pauses a track's cycling and it stays paused until resumed. Cycling runs at the dataset's real-time rate: consecutive frames advance every 1/fps seconds and sparser samples wait proportionally longer. The box is drawn over the chip instead of into it, so an edit no longer re-crops the chip and the view keeps its zoom. While editing, the mouse wheel zooms the chip about the cursor (up to 16x) and dragging empty space pans it. The type field's arrow closes its list on a second press, the hover actions grow under the mouse, entries sort by confidence (highest first) by default, and the tooltip over each chip is gone. Query changes apply as soon as they settle, so the Show button is gone; datasets loading or leaving refresh the grid on their own. A settings gear next to Save opens the annotator's settings dialog for auto-save. All toolbar fields read at the same size as the Results and Datasets buttons. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
… with add-box and delete actions The cameras of a multicamera dataset now share a grid entry per track, shown as one chip per camera side by side and labelled with the camera. The chips show the same sampled frames on every side; where a camera has no detection on a frame the track has elsewhere, that side is cropped at a position interpolated from its own neighbouring boxes, shows no box, and offers an add-box action that creates the detection there ready to be adjusted. Type edits apply to the track in every camera and opening the viewer opens the rig. The chip (image, overlay, editing) is split out of the cell into ReviewChip so a cell can hold several. A red X next to the accept action deletes the track, written on the next save. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
…ept clear of the camera label Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
…e or text A Query tab (left of Review) with two panels. Datasets lists the datasets queries search, shows each one's search index status and builds or drops indexes for several at once, tracking the build jobs. Query searches every indexed dataset from an exemplar image (with an optional dragged box), from a frame of a dataset or video file, or from a text prompt swept over sampled frames with the SAM3 text model, whose hits can seed a similarity search. Results reuse the video search results grid inline, with refine and saved models, and open the viewer on double click. The library's selection can be handed to it. Two fixes found on the way: a video dataset is now passed to the indexer as a video rather than an image list (which crashed decoding the mp4 as an image), and index build jobs are recognised by their real titles, so the side panel and this page see builds finish. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Review, Query, Training, Scoring and Pipelines each grew their own way of choosing datasets: an autocomplete, a searchable data table with a plus button, a checkbox table with select all. This adds one component that covers all of them (search field, list of the datasets on offer with an add button per row, select all for whatever the search lists, and an optional browse button for platforms without a listing) so the pages can adopt it in follow-up changes. Nothing uses it yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Both pages offered their datasets through their own data table with a plus button (Pipelines also had its own search and select all); they now share the picker, which keeps the fps column and Training's view button through the picker's headers and row-actions slot. Training's staged items are typed as the cache rows they always were. The picker's search field is clearable, which yields null; filter on an empty string in that case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Selected rows now show a clickable check that removes them, and a "Remove all (n)" button drops every listed selection, mirroring select all. Adding or removing keeps the picker at the same place on screen: the pages list the selection above it, so growing that list used to push the picker down under the user's pointer. Training and Pipelines section titles and descriptions lose their card padding so they line up with the tables and the picker under them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The available datasets no longer wait for a pipeline to be chosen; the run button stays disabled until one is. The selected datasets and the run button move below the picker, so adding to the selection no longer pushes the list the user is working in down the page. That makes the picker's scroll hold unnecessary, so it is removed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
getAvailableItems still returned nothing without a selected pipeline, which left the picker empty on entry despite the section now showing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Same order as the Pipelines page: configuration first (name, config file, labels, the annotated-frames and fine-tuning options), then the picker, then the selected datasets with the Train button. Interrupted runs follow the selection instead of splitting the configuration from the datasets. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The Datasets view drops its autocomplete for the picker used by Training and Pipelines: search, add or remove per row, select all and remove all, with the web's folder browser behind a Browse button. The selected datasets keep their status table underneath. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The Datasets view drops its autocomplete for the picker used by Review, Training and Pipelines. The selected datasets keep their index status table with the build controls underneath. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Adding a dataset used to read its annotations at once, so picking many on the Datasets view meant waiting for every one to load before doing anything. Picked datasets are now queued and load together when the Results view opens (or on reload). Datasets handed over by the library still load immediately, as results are shown right away. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
…tion The Datasets view asked for datasets twice: pick them for the list, then tick them again to build indexes. The checkboxes are gone; Build index covers every selected dataset not indexed yet, and each row has its own build button. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
… cameras Zooming a chip no longer requires edit mode: the wheel zooms about the cursor and dragging the zoomed image pans it, with the action buttons left alone. Multi-camera entries hold one view in the cell, so zooming or panning one camera moves the others with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
The type dropdown listed every type on any confidence pair of the selected datasets, including low-confidence secondary classes that a query at the current threshold would never match. It now lists the types some track carries at that confidence or above, so every choice has results; the cell type field keeps offering every known type. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
# Conflicts: # client/dive-common/components/DatasetPicker.vue # client/dive-common/datasetPicker.spec.ts # client/dive-common/datasetPicker.ts
The entry type field used a native datalist, whose arrow could open the list but never close it. A small dropdown of our own replaces it: the arrow toggles, typing filters, arrow keys and Enter pick, Escape reverts, and the list floats out of the clipped cell. The middle mouse button now pans a zoomed chip from anywhere on it, in or out of edit mode, with the browser's autoscroll suppressed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
Leaving the Review page hands its service, view and grid page to a held session that the next visit takes back, so navigating away and returning shows the same datasets and page, unsaved edits included. Starting Review from the library with a different selection begins a fresh session, unless the held one has unsaved edits, in which case it is resumed and the new datasets are added. The route-leave prompt goes away since nothing is lost; closing the app still warns. The entry type field also commits a typed type on Enter directly instead of relying on the blur that follows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu
# Conflicts: # docs/Review.md
Stacked on #1897 (base branch
dev/review-grid). Rebases the video search / IQR work from #1743 onto it and shows search results in the new Review chip grid.Rebase
The five commits of #1743 apply on top of the review branch with these adaptations to changes that landed on main in the meantime (one follow-up commit):
JsonConfig, read withloadJsonConfigfromdatasetFileAbsPathloadConfiginvoke<DesktopJob>undefinedResults in the Review grid
VideoSearchResultsGridis rebuilt on the shared review components: results map toReviewItems (dive-common/review/searchResultItems.ts), and the grid usesReviewGrid/ReviewCell/ReviewGridControlsanduseReviewGrid, so grid shape, zoom, context margin, paging and keyboard behaviour are the same as the Review tab and share its persisted settings.createFrameSourceRegistry), so cross-dataset results load their own media (image sequence or hidden<video>seek) with no backend frame extraction for display. Results without a box show the whole frame.ReviewCell's actions slot, get a green / red highlight border, show the relevancy score as the badge, and clicking a chip from the open dataset seeks the viewer to it.useSearchChips.tsreplacesuseResultChips.tsandAdjudicationChip.vue; it also feeds the side panel's row thumbnails from the same chip store.docs/Review.mdgains a short "Search results" section.Testing
npm run lint,npm run typecheckandnpm testpass.🤖 Generated with Claude Code
https://claude.ai/code/session_01W1C4QY6hxjHaUPJfWPfQuu