Metadata-Version: 2.5
Name: shot-grammar
Version: 0.1.0
Summary: Measure a flat-art video's composition grammar from pixels: composition-change timing, element placement bands, caption hold/drop.
Project-URL: Homepage, https://github.com/EdwardPhang/shot-grammar
Project-URL: Repository, https://github.com/EdwardPhang/shot-grammar
Project-URL: Issues, https://github.com/EdwardPhang/shot-grammar/issues
Author: Edward Phang
License: MIT
License-File: LICENSE
Keywords: captions,composition,measurement,shot-detection,video
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.12
Requires-Dist: numpy>=1.26
Requires-Dist: opencv-python>=4.9
Requires-Dist: pillow>=10.0
Requires-Dist: scenedetect>=0.6.4
Description-Content-Type: text/markdown

# shot-grammar

**shot-grammar measures a reference video's composition instead of describing it.**

Point it at a flat-art short and it reports, from pixels alone: how long each composition
holds before it changes, where every drawn element sits (left/centre/right by
top/mid/lower), how big it is, and exactly how long each caption holds before it drops.
Output is one JSON composition formula you can hand to a human editor or to an LLM
writing the next shot list.

Shot-boundary detectors give you cuts; this gives you the grammar — reported as
**composition changes**, not "shots per minute", because on flat-art footage a shot
boundary isn't a well-posed thing to promise (a hard cut and a mid-shot element entry
produce overlapping pixel signals; no threshold separates them, so this tool doesn't
claim to).

**Flat art on a flat white background only.** It runs a purity gate first — flatness
*and* whiteness — and tells you no when your footage doesn't qualify, with the measured
numbers so you know why.

## Why measure instead of describe

Copying a reference channel by eye fails in ways you only find after you've built the
thing. On the internal replication job that seeded this tool, vision-model estimates
carried ~2% placement error plus four hard data errors (elements listed that weren't on
screen, an object stack labelled as a character). Measuring pixels instead produced a
rebuild at 0.87% mean pixel difference on the first try — with a human correcting
cutouts along the way; see [docs/method.md](docs/method.md) for what that number does
and doesn't promise.

## Install

Requires Python 3.12+ and `ffmpeg`/`ffprobe` on PATH.

```bash
uv tool install shot-grammar   # or: pip install shot-grammar
```

From a checkout:

```bash
uv sync
uv run shot-grammar --help
```

## Use

```bash
# 1. does this footage qualify? (exit 0 GO, exit 2 STOP with a plain reason)
shot-grammar gate input.mp4

# 2. measure it
shot-grammar measure input.mp4 -o FORMULA.json
shot-grammar measure input.mp4 -o FORMULA.md --format md   # human-readable summary

# 3. the schema, or validate a formula file
shot-grammar schema
shot-grammar schema --validate FORMULA.json
```

What you get in `FORMULA.json` (full schema in [docs/schema.md](docs/schema.md)):

- `composition_changes` — every event with frame/second boundaries and hold length,
  plus min/median/max and events-per-minute
- per event, anonymous elements (`el_1..el_n`): pixel bbox, horizontal band
  (left/centre/right), vertical band (top/mid/lower), size as % of frame area,
  ambiguity flags
- `captions` — hold durations, drop frames, and blank gaps from the caption band
  (presence only, no OCR)
- probed `geometry` (never assumed) and the `gate` reading

## What v0.1 says no to

- Non-flat or non-white backgrounds — the gate stops you (alpha recovery
  `alpha = 1 - min(R,G,B)` is only exact over near-pure white; see
  [docs/limits.md](docs/limits.md))
- Motion vs redraw classification (planned v0.2)
- Semantic element naming — elements are anonymous by design
- Caption OCR, transcription, speech
- Rebuild/re-render verification of a reconstruction
- Multi-video catalog banding (planned v0.2)
- Any GUI or hosted service

## Verifier-first

The test suite was written red-first against synthetic ground-truth fixtures whose
expected values are hand-written from the generator's parameters, and CI proves the
suite still **fails** against a stubbed measurement (≥8 assertions) before requiring it
to pass against the real one. A suite that passes a stub proves nothing. The one real
committed clip is self-authored (rendered with [Manim](https://www.manim.community/));
every committed video's origin is recorded in `tests/fixtures/PROVENANCE.md` and
enforced by CI.

## License

MIT.
