Annie visualizes a video dataset alongside its frame-wise annotations and lets you curate and (soon) author them — all in one local, browser-based UI that runs on a single machine. The motivating datasets are CMU-MOSEI and First Impression V2 (face detections + derived face tracks), but Annie is dataset-agnostic.
Screenshots
Home tab — landing page with a summary card for each tab.
Browse tab — per-video rows with original clip, annotated frame strip, and label tags, above collapsible Manipulate / Filter / View panels. Click a row's top-right corner to queue it for the Annotator.
Annotator tab — where supervision is entered, one task at a time. The task switch offers only the tasks your sources support: Curation (shown), Protagonist review, and Segment review.
Features
| Tab | What it does |
|---|---|
| Home | A landing page (the default) summarising each tab; click a card to jump there. |
| Convert | Re-encode an audio/video dataset to a consistent, torchcodec-validated form: uniform audio (format/rate/channels) and constant-frame-rate H.264 video, with explicit audio muxing (or black-frame videos for audio-only). A background batch shows live X/Y progress, %, elapsed, and ETA. |
| Dataset | Build the dataset from a list of data sources (videos folder, vdet/track folders, and any number of label/protagonist/segmentation CSVs). Add a source and it scans in place — no Scan button — with live counts and an Available/Unavailable chip per source, plus a Tasks panel showing which Annotator task each source set makes ready. |
| Browse | Scrollable, per-video visualizer with an always-visible filter bar (name, video/audio/vdet/track presence, review, labels): an ORIGINAL placeholder, five-frame strip, on-the-fly annotated render, and media/annotation/label tags. A read-only viewer — clicking a row selects it (a check tick) for the Annotator rather than recording review. Row height is configurable. |
| Annotator | The supervision surface, offering only the tasks whose sources are ready: Protagonist review (correct the active track and export a _manual CSV), Curation (like/dislike/note per video), Segment review (accept/drop per-clip segments of long videos, comparing competing start/end bands, exported as two files), and Event annotation (a video-editor timeline for marking labelled time-interval events across parallel tracks, exported as JSON and CSV). |
| Settings | Browse/Annotator row height, render-cache TTL, and review-status export/import (CSV/JSON). |
See the playbooks for screen-by-screen walkthroughs of the main flows.
Highlights
- Extensible data sources — a dataset is an ordered set of sources; a CSV joins
to videos by a chosen key column, exposing its value columns as Browse tags and
filter facets (e.g.
Sentiment: negative,Angry: 0.33). Dataset-agnostic by construction. - Nested video layouts — videos do not have to sit in one flat folder. On the Videos-folder card
turn on Nested layout and describe where they live with a path template relative to the folder,
e.g.
{group}/{subject}/clip_{part}.mp4, plus an optional video id such as{subject}_{part}. Annie scans below the folder, builds a unique id per video, and turns each{field}into a Browse tag/filter (and an export column), with a live preview of what was found. The layout is stored in your saved config, so it is set up once. Without a layout a flat folder is expected, as before. - Stem matching — videos pair with vdet/track files by filename stem (exact + prefix, longest-stem-first), aggregated into one row per video.
- Composable filtering — filter by vdet/track coverage, review verdict, notes, annotator selection, and label values; facets combine with AND, label values OR.
- Frame-accurate decode — torchcodec
with
exactmode for the annotator andapproximatefor fast scrubbing. - Render-on-demand — a background job burns annotations into a browser-playable clip (libx264 via FFmpeg, audio muxed back); temp clips auto-purge on a TTL.
- Protagonist correction — pick the true protagonist; the fix is written
to a separate
_manualfile (resolutionmanual ▸ source ▸ -1) so human judgement never overwrites the pristine heuristic record, and the resolved datasource exports to a standalone CSV. - Segment review — for a long video cut into per-clip segments, accept or drop
each clip while comparing competing start/end bands (e.g. a
cutprediction vs aGTreference) side by side; decisions persist for resumable passes and export as separate accepted / dropped CSVs. - Event annotation — a video-editor-style timeline: watch one video, scrub it
frame-by-frame (±5 s, frame step,
I/Oin-out marks), and draw labelled events on stacked participant lanes (categories like Mother / Baby, defined once per dataset and picked — never re-typed — so labelling stays consistent; press1–9to switch the active participant). Each event carries a name, note, colour, and arbitrary key/value attributes; everything saves to the session DB as you go and exports to nested JSON (for code) and flat CSV (for a spreadsheet), per video or per session. Export opens your system's Save as… dialog (starting in Documents, name prefilled) so you choose the folder and file name — it works the same locally and in Docker; browsers without that dialog (Firefox, Safari) download the file under the chosen name. Zoom the timeline and pan with the scrollbar or trackpad; during playback the window follows the red playhead (page-flip), and panning by hand pauses following until you re-enable it.
Installation
Windows (for non-technical users)
If you just want to use Annie on Windows — no terminal, no Python, no FFmpeg — use the
one-click launcher in launcher/windows/: install Docker Desktop once,
double-click Annie, and pick your video folder when asked. It opens in your browser and
updates itself on each launch. See
launcher/windows/README-Windows.txt for the three-step
guide. The developer install paths below are not needed for this.
Prerequisites
Required for the source and PyPI install paths. Docker bundles everything automatically.
| Tool | Purpose | Install |
|---|---|---|
| uv | Python package manager | curl -LsSf https://astral.sh/uv/install.sh | sh |
| FFmpeg (4–8) | Frame decode, render pipeline, audio probe | brew install ffmpeg |
| ffprobe | Audio-stream detection (ships with FFmpeg) | included with FFmpeg |
From PyPI
uv pip install "annie[all]" # core + torch / torchcodec for frame decode & render
annie # starts the UI at http://127.0.0.1:8080
From source (development)
git clone https://github.com/fodorad/Annie
cd Annie
make dev # installs annie[all,dev] in editable mode (torch + torchcodec included)
make run # starts the UI at http://127.0.0.1:8080
Docker
No Python, uv, or FFmpeg required on the host — Docker bundles everything.
git clone https://github.com/fodorad/Annie
cd Annie
cp .env.example .env # fill in your HDD paths
make docker-build # ~5–10 min first time (downloads CPU torch)
make docker-run # starts the UI at http://localhost:8080
Or pull the pre-built image without cloning:
curl -O https://raw.githubusercontent.com/fodorad/Annie/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/fodorad/Annie/main/.env.example
cp .env.example .env # fill in your HDD paths
docker compose up # pulls fodorad/annie:latest automatically
Annie state (logs, session DBs, render cache, saved configs) is stored in
./annie-home/ next to docker-compose.yml by default, so all files are
directly accessible from Finder / Explorer. Exported review CSVs land in
annie-home/tmp/. Override the path with ANNIE_HOME_HOST in .env.
macOS + external HDDs: If
/Volumesis already in Docker Desktop → Settings → Resources → File Sharing, individual drives under/Volumeswork without any additional configuration.
Extras
| Extra | What it adds |
|---|---|
media |
torch, torchcodec — frame decoding & rendering |
dev |
ruff, ty, coverage, pre-commit |
docs |
sphinx, furo, sphinx-autoapi, myst-parser |
all |
media |
Architecture
Annie is a single process with a strict layered architecture; each layer calls only the layer directly beneath it, enforced by import direction:
UI layer annie/app.py, annie/pages/* (NiceGUI tabs)
│ calls down only
Service layer annie/dataset/*, annie/media/* (scanning, rendering, filtering, …)
│
Domain layer annie/core/models.py, annie/parsers/* (pure data, no I/O frameworks)
│
Infrastructure annie/core/config.py, annie/core/theme.py,
annie/dataset/storage.py (SQLite), annie/media/decode.py (torchcodec)
The UI never imports sqlite3 or torchcodec directly — it calls a service
function. The core layers install without a heavyweight torch dependency; only the
frame decode and render pipeline need the media extra.
Annotation formats
Both .vdet and .track files share one 17-column CSV schema (CRLF, with header):
frame_id, source, score, x, y, w, h,
left_eye_x, left_eye_y, right_eye_x, right_eye_y,
nose_x, nose_y, left_mouth_x, left_mouth_y, right_mouth_x, right_mouth_y
.vdet— all raw detections for a video; may have several rows per frame.{video_id}__track{N}.csv— one tracked face across frames (one row/frame).- protagonist CSV — e.g.
protagonist_track_heuristic.csvwithuuid,track_id, the active protagonist per video (-1= none). The key and track-id columns are chosen when the source is added; manual corrections go to the_manualsibling. - label CSV — any CSV; pick a key column (joined to the video id) and value columns to surface as Browse tags and filter facets.
- segmentation CSV — e.g.
review_band.csv; one row per clip keyed byvideo_id+segment_id, with one or more named start/end bands (in seconds) and an optionalvideo_pathcolumn. Drives the Annotator's Segment-review task.
Development
git clone https://github.com/fodorad/Annie
cd Annie
make dev # install in editable mode with all extras
make check # lint + type-check + test + docs (mirrors CI)
make run # start the app at http://127.0.0.1:8080
Every screenshot in this README and in the docs is generated from the running UI, so it
never drifts from what Annie actually looks like. After changing a screen, refresh them
with make screenshots (needs make install-docs once) and commit the updated PNGs.
Tip — skip re-clicking paths on every restart: set ANNIE_* environment
variables and Annie will seed those sources automatically at launch. This is
equivalent to manually adding sources on the Dataset tab, but saves time during
development when you always work with the same dataset.
export ANNIE_VIDEO_DIR=/path/to/video
export ANNIE_VDET_DIR=/path/to/vdet
export ANNIE_TRACK_DIR=/path/to/track
export ANNIE_PROTAGONIST_CSV=/path/to/protagonist_track_heuristic.csv
export ANNIE_LABEL_CSV=/path/to/label.csv
make run
Sources seeded via env vars are session-only; curation and corrections always
persist. Save your source configuration from the Dataset tab to a named config
file to reload it in one click without relying on env vars. Every setting has a
corresponding ANNIE_* variable (see annie/core/config.py).
See CONTRIBUTING.md for the commit convention and release flow.
Documentation
Related Projects
- exordium — multimodal feature extraction (the detectors/trackers that produce Annie's
.vdet/.trackinputs).
Contact
Ádám Fodor — adamfodor.com · fodorad201@gmail.com
License
Metadata
Release files for annie 1.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| annie-1.11.0.tar.gz | 47.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| annie-1.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.4 MB
Release files / annie-1.11.0.tar.gz
| Download URL | annie-1.11.0.tar.gz |
|---|---|
| Size | 47.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79d8a044a23c3ee45015fb7bd9ecf30aea3cd8f1d7d9af1cfe543c84d7e586a4
|
|
BLAKE2b-256 checksum How to use checksums |
82aa63d08752e28f26ab0c7cc46b91ed6723a7371825778960b71c02bb50d878
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / annie-1.11.0-py3-none-any.whl
| Download URL | annie-1.11.0-py3-none-any.whl |
|---|---|
| Size | 213.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
97187f8b77e8c70ad553a6f256049a58f83b7bc1ee0dc1edad6fe314de3a4303
|
|
BLAKE2b-256 checksum How to use checksums |
4a168fc6cb0f27e62b453484c35997f26c093f17ae9ad397963e9c1c1932c0e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency log