diff --git a/README.md b/README.md index 9b3130c..e0a7abd 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,57 @@ # 8-cut -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/ethanfel/8-cut/blob/master/LICENSE) +

+ 8-cut — 8-second clips for SELVA datasets +

-**Source:** https://github.com/ethanfel/8-cut +

+ License: GPL v3 +

A desktop tool for cutting 8-second clips from video files, designed for building [SELVA](https://github.com/google-deepmind/selva) datasets. ## Overview -8-cut lets you scrub through a video, mark a cut point, and export a fixed 8-second clip with one keypress. It tracks every export in a local SQLite database so you can resume a session or switch between resolution variants of the same source without duplicating work. +8-cut lets you scrub through a video, mark a cut point, and export a batch of overlapping 8-second clips with one keypress. It tracks every export in a local SQLite database so you can resume a session or switch between resolution variants of the same source without duplicating work. -All clips are exactly 8 seconds — this is a hard constraint of the SELVA format. +All clips are exactly 8 seconds — a hard constraint of the SELVA format. ## Features -- **Frame-accurate scrubbing** — click or drag the timeline; arrow keys and J/K/L for frame-by-frame stepping -- **Keyboard shortcuts** — J/L step one frame, Shift+J/L step one second, Space/P play/pause, K pause and return to cursor, E export, M jump to next marker -- **Two export formats** — H.264/AAC MP4 or lossless WebP image sequence (frames + `.wav` audio extracted alongside) -- **Portrait crop** — crop to 9:16, 4:5, or 1:1 before export; adjustable horizontal crop position -- **Resize** — scale short side to a fixed pixel size (e.g. 256) -- **Export history** — timeline markers show previously exported clips; fuzzy filename matching detects resolution variants of the same file (e.g. `_2160p` vs `_1080p`) -- **Mask generation** — generate binary foreground masks per-frame using SAM2 (segmentation) or Depth Anything V2 (depth-based), via a bundled venv -- **Playlist** — drag-and-drop multiple files; duplicates are ignored +- **Frame-accurate scrubbing** — click or drag the timeline; arrow keys and J/L for frame-by-frame, Shift for 1-second steps +- **Batch export** — export multiple overlapping clips per cut point with configurable count and spread offset +- **Two export formats** — H.264 MP4 with lossless PCM audio, or WebP image sequence (frames + `.wav`) +- **Portrait crop** — crop to 9:16, 4:5, or 1:1 before export; click the video or crop bar to reposition +- **Random portrait** — optionally apply a random portrait crop to a subset of each batch +- **Resize** — scale short side to a fixed pixel size (e.g. 512) +- **SELVA annotation** — label and category fields saved to `dataset.json` and the clip database +- **Export history** — timeline markers show previously exported clips; double-click to enter overwrite mode; right-click to delete +- **Fuzzy matching** — detects resolution variants of the same file (`_2160p` vs `_1080p`) and shares markers between them +- **End-frame preview** — floating window shows the last frame of the selection region +- **Playlist** — drag-and-drop or use the Open Files button; right-click to remove items +- **Playback loop** — plays the exact selection region on loop so you can preview what will be exported +- **Group operations** — delete or overwrite acts on all sub-clips in a batch, not just one + +## Keyboard shortcuts + +| Key | Action | +|-----|--------| +| `Left` / `J` | Step back 1 frame | +| `Right` / `L` | Step forward 1 frame | +| `Shift+Left` / `Shift+J` | Step back 1 second | +| `Shift+Right` / `Shift+L` | Step forward 1 second | +| `Space` / `P` | Toggle play/pause | +| `K` | Pause and snap to cursor | +| `E` | Export | +| `M` | Jump to next marker (wraps) | +| `N` | Next file in playlist | + +Shortcuts are suppressed when a text field has focus. ## Requirements - Python 3.11+ -- `ffmpeg` in `PATH` +- `ffmpeg` on `PATH` - PyQt6 - python-mpv (requires libmpv) @@ -34,15 +59,15 @@ All clips are exactly 8 seconds — this is a hard constraint of the SELVA forma pip install -r requirements.txt ``` -For mask generation tools, additional dependencies (PyTorch, transformers, segment-anything-2, opencv) are installed into `~/.8cut/venv/` via the Settings dialog. - ### Platform notes -**Linux** — install libmpv via your package manager (`apt install libmpv-dev` / `pacman -S mpv`). +| Platform | libmpv | +|----------|--------| +| **Linux** | `apt install libmpv-dev` or `pacman -S mpv` | +| **macOS** | `brew install mpv` | +| **Windows** | Download `mpv-2.dll` from [mpv Windows builds](https://sourceforge.net/projects/mpv-player-windows/files/libmpv/) and place it in `PATH` or next to `main.py` | -**macOS** — install libmpv via Homebrew: `brew install mpv`. - -**Windows** — `python-mpv` requires `mpv-2.dll` in `PATH` or in the same directory as `main.py`. Download it from the [mpv Windows builds](https://sourceforge.net/projects/mpv-player-windows/files/libmpv/) page (pick the latest `mpv-dev-x86_64-*.7z`, extract `mpv-2.dll`). Also ensure `ffmpeg.exe` is in `PATH` (e.g. via [winget](https://winget.run/): `winget install ffmpeg`). +Windows also needs `ffmpeg.exe` on `PATH` (e.g. `winget install ffmpeg`). ## Usage @@ -50,49 +75,52 @@ For mask generation tools, additional dependencies (PyTorch, transformers, segme python main.py ``` -Drop a video onto the playlist or use the file picker. Scrub to your cut point, set the output folder and clip name, then press **Export** (or `E`). +Drop videos onto the queue or click **+ Open Files**. Scrub to your cut point, then press **Export** (or `E`). -### Export formats +### Export layout -| Format | Output | -|--------|--------| -| MP4 | `/_NNN.mp4` — H.264 video + AAC audio | -| WebP sequence | `/_NNN/frame_%04d.webp` — lossless WebP frames + `_NNN.wav` PCM audio | - -### Keyboard shortcuts - -| Key | Action | -|-----|--------| -| `←` / `J` | Step back 1 frame | -| `→` / `L` | Step forward 1 frame | -| `Shift+←` / `Shift+J` | Step back 1 second | -| `Shift+→` / `Shift+L` | Step forward 1 second | -| `Space` / `P` | Toggle play/pause | -| `K` | Pause and snap video to cursor | -| `E` | Export clip | -| `M` | Jump to next export marker (wraps) | - -Arrow keys and J/K/L are ignored when a text field has focus. - -### Mask generation tools - -> **Warning:** The mask generation feature is untested and may not work reliably. For production use, consider [ComfyUI](https://github.com/comfyanonymous/ComfyUI) instead. - -Two standalone scripts live in `tools/`. They are run by the app via a managed venv but can also be called directly: +Each export creates a group subfolder containing the overlapping sub-clips: ``` -python tools/sam_masks.py --input clip.mp4 --output masks_dir/ -python tools/depth_masks.py --input clip.mp4 --output masks_dir/ +output/ + clip_001/ + clip_001_0.mp4 # starts at cursor + clip_001_1.mp4 # starts at cursor + spread + clip_001_2.mp4 # starts at cursor + 2 * spread + clip_002/ + ... ``` -Both output one binary PNG per frame (`frame_0000.png`, …) where white = foreground. +With WebP sequence format, each sub-clip becomes a directory of frames plus a `.wav`: -- **SAM2** (`sam_masks.py`) — uses `facebook/sam2-hiera-large`; center-point prompt propagated across all frames -- **Depth Anything V2** (`depth_masks.py`) — uses `depth-anything/Depth-Anything-V2-Large-hf`; Otsu threshold on the depth map +``` +output/ + clip_001/ + clip_001_0/ + frame_0001.webp + frame_0002.webp + ... + clip_001_0.wav +``` + +### SELVA annotation + +Set a **Label** (e.g. "dog barking") and **Category** (Human / Animal / Vehicle / Tool / Music / Nature / Sport / Other) before exporting. These are saved to: + +- `dataset.json` in the export folder — one entry per clip with `path` and `label` +- The SQLite database — for recall when you revisit a marker + +Labels persist between exports so you can cut many clips of the same class without retyping. + +### Overwrite and delete + +- **Double-click** a timeline marker to enter overwrite mode — the next export re-encodes all clips in that group to their original paths +- **Right-click** a marker to delete it from the database +- The **Delete** button removes all clips in a group from disk, database, and `dataset.json` ## Database -Export history is stored in `~/.8cut.db` (SQLite). The database records filename, start time, and output path for every clip. When you open a file, 8-cut checks whether a similar filename has been processed before (stripping resolution tags like `_2160p`, `_1080p`, codec tags, etc.) and pre-populates the timeline with existing markers. +Export history is stored in `~/.8cut.db` (SQLite). The database records filename, start time, output path, label, category, and all encoding settings for every clip. When you open a file, 8-cut fuzzy-matches the filename (stripping resolution tags like `_2160p`, codec tags, etc.) and pre-populates the timeline with existing markers. ## Testing @@ -100,7 +128,7 @@ Export history is stored in `~/.8cut.db` (SQLite). The database records filename pytest tests/ -v ``` -38 unit tests covering path builders, ffmpeg command generation, time formatting, and the processed-clips database. +49 unit tests covering path builders, ffmpeg command generation, time formatting, database operations, group queries, and annotation handling. ## License diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..9408366 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + 8 + + + -cut + + + + + + + + + + + 8-second clips for SELVA datasets + + + 8.0s + + + 0:00 + 1:15 + 5:00 +