Made project river-agnostic

This commit is contained in:
2026-05-27 10:02:20 +02:00
parent 23dbbc1555
commit 69eab7514f
13 changed files with 81 additions and 97 deletions

View File

@@ -1,6 +1,6 @@
# River Annotation Tool # Video Annotation Tool
A desktop GUI application for manually annotating river video clips as part of the [HydroScan](https://github.com/HydroScan) project. Annotators draw pixel-level water masks over river footage and answer structured survey questions about flow conditions, lighting, and scene quality. A desktop GUI application for manually annotating video clips. Annotators draw pixel-level segmentation masks over footage and answer structured survey questions defined in a config file.
## Requirements ## Requirements
@@ -24,7 +24,7 @@ cp config/clips.example.txt config/clips.txt
# Edit config/questions.yaml to customise survey questions (optional) # Edit config/questions.yaml to customise survey questions (optional)
# 4. Run # 4. Run
uv run python -m river_annotation_tool.annotation_script uv run python -m clip_annotator.annotation_script
``` ```
## Installation ## Installation
@@ -81,9 +81,9 @@ The `clips_file` (the list of clip filenames to annotate) is always read from th
## Usage ## Usage
```sh ```sh
uv run python -m river_annotation_tool.annotation_script uv run python -m clip_annotator.annotation_script
# or, if you have the venv activated: # or, if you have the venv activated:
python -m river_annotation_tool.annotation_script python -m clip_annotator.annotation_script
``` ```
### Arguments ### Arguments
@@ -94,7 +94,7 @@ python -m river_annotation_tool.annotation_script
| `--data` | *(from config)* | Override `data_dir` from config | | `--data` | *(from config)* | Override `data_dir` from config |
| `--out` | *(from config)* | Override `out_dir` from config | | `--out` | *(from config)* | Override `out_dir` from config |
| `--clips` | *(from config)* | Override `clips_file` from config | | `--clips` | *(from config)* | Override `clips_file` from config |
| `--clip` | *(first unannotated in list)* | Open a specific clip by stem name | | `--clip` | *(first unannotated in list)* | Open a specific clip by its stem name (filename without extension, e.g. `clip_20230501T120000`) |
| `--extras` | off | Also save GIFs and extra PNGs (see Output section) | | `--extras` | off | Also save GIFs and extra PNGs (see Output section) |
| `--no-skip` | off | Show already-annotated clips instead of skipping them | | `--no-skip` | off | Show already-annotated clips instead of skipping them |
@@ -102,16 +102,16 @@ python -m river_annotation_tool.annotation_script
```sh ```sh
# Annotate clips listed in config/clips.txt (default) # Annotate clips listed in config/clips.txt (default)
uv run python -m river_annotation_tool.annotation_script uv run python -m clip_annotator.annotation_script
# Use a different config file # Use a different config file
uv run python -m river_annotation_tool.annotation_script --config config/my_config.yaml uv run python -m clip_annotator.annotation_script --config config/my_config.yaml
# Override paths from the command line # Override paths from the command line
uv run python -m river_annotation_tool.annotation_script --data data/clips --out data/out uv run python -m clip_annotator.annotation_script --data data/clips --out data/out
# Annotate a single specific clip # Annotate a single specific clip
uv run python -m river_annotation_tool.annotation_script --clip left_20230615T120000 uv run python -m clip_annotator.annotation_script --clip clip_20230615T120000
``` ```
## Configuration ## Configuration
@@ -121,8 +121,8 @@ Main settings live in `config/config.yaml`. Copy `config/config.example.yaml` to
```yaml ```yaml
storage: local # required: 'local' or 's3' storage: local # required: 'local' or 's3'
data_dir: # required: directory containing ZIP archives (local path or bucket/prefix for S3) data_dir: # required: read-only source of ZIP archives (local path or bucket/prefix for S3)
out_dir: # required: where to write annotations out_dir: # required: write destination for annotations (can be same bucket as data_dir with a different prefix, or a separate location)
clips_file: config/clips.txt clips_file: config/clips.txt
optical_flow_config_file: config/optical_flow_config.yaml optical_flow_config_file: config/optical_flow_config.yaml
@@ -139,7 +139,7 @@ filenames:
zip_extension: .zip zip_extension: .zip
``` ```
Output filenames (`mask.png`, `metadata.json`, etc.) have sensible defaults and can be overridden in the `filenames:` block — see [`config.py`](src/river_annotation_tool/config.py) for the full list. Output filenames (`mask.png`, `metadata.json`, etc.) have sensible defaults and can be overridden in the `filenames:` block — see [`config.py`](src/clip_annotator/config.py) for the full list.
### Survey questions ### Survey questions
@@ -147,9 +147,9 @@ Survey questions are defined in `config/questions.yaml` (committed to the repo).
### Optical flow segmentation ### Optical flow segmentation
`config/optical_flow_config.yaml` controls the **Auto Segment** button. When pressed, the tool computes a river mask from the loaded frames and replaces the current mask (undoable). The segmentation combines two criteria: `config/optical_flow_config.yaml` controls the **Auto Segment** button. When pressed, the tool computes a segmentation mask from the loaded frames and replaces the current mask (undoable). The segmentation combines two criteria:
- **Optical flow magnitude** — pixels where the temporal median of frame-to-frame flow (scaled by FPS) exceeds a fraction of the maximum are considered moving water. - **Optical flow magnitude** — pixels where the temporal median of frame-to-frame flow (scaled by FPS) exceeds a fraction of the maximum are considered moving.
- **Brightness** — pixels outside a brightness window are excluded (removes sky, saturated glare, etc.). - **Brightness** — pixels outside a brightness window are excluded (removes sky, saturated glare, etc.).
```yaml ```yaml
@@ -164,41 +164,25 @@ brightness_range: [2, 253] # [min, max] greyscale brightness to keep
## Clip list file ## Clip list file
`config/clips.txt` lists the clip filenames to annotate, one per line. Lines starting with `#` are ignored. Clips are processed in order; already-annotated clips (those with an existing `mask.png`) are skipped automatically. Pass `--no-skip` to include them. When the last clip is reached, a dialog appears and the app exits. `config/clips.txt` lists bare clip filenames (not full paths) to annotate, one per line — the tool prepends `data_dir` automatically. Each entry must be the exact filename of the ZIP archive as it appears in `data_dir` (e.g. `clip_20230501T120000.zip`). Lines starting with `#` are ignored. Clips are processed in order; a clip is considered already-annotated when `<out_dir>/<clip_stem>/mask.png` exists and is skipped automatically. Pass `--no-skip` to include already-annotated clips. When the last clip is reached, a dialog appears and the app exits.
``` ```
# Example clips.txt # Example clips.txt
left_20230501T120000.zip clip_20230501T120000.zip
left_20230502T120000.zip clip_20230502T120000.zip
``` ```
Copy `config/clips.example.txt` as a starting point. Copy `config/clips.example.txt` as a starting point.
## Multi-annotator setup ## Multi-annotator setup
Pre-made clip lists for 7 annotators are included in `config/annotator_A.txt` through `config/annotator_G.txt`. Each annotator is assigned exactly 5 recording days (non-consecutive where possible), covering all 24 available days across the dataset. To distribute clips across multiple annotators, create one clips file per annotator (e.g. `config/annotator_A.txt`) listing their assigned clip filenames, then pass it via `--clips`:
To run the tool for a specific annotator, pass their file via `--clips`:
```sh ```sh
uv run python -m river_annotation_tool.annotation_script --clips config/annotator_A.txt uv run python -m clip_annotator.annotation_script --clips config/annotator_A.txt
``` ```
### Assignment Assigning non-overlapping clip lists lets each annotator work independently. Intentionally overlapping a subset of clips across annotators enables inter-annotator agreement checks.
11 of the 24 days are reviewed by two annotators (the theoretical maximum given 7 × 5 = 35 slots and 24 days), giving 11 days with double coverage for inter-annotator agreement checks.
| Annotator | Days | Clips |
|---|---|---|
| A | 2025-11-17 · 2025-12-03 · 2026-01-01 · 2026-01-09 · 2026-02-11 | 94 |
| B | 2025-11-18 · 2025-12-05 · 2026-01-06 · 2026-02-12 · 2026-03-02 | 128 |
| C | 2025-11-22 · 2025-12-12 · 2026-01-07 · 2026-02-16 · 2026-03-03 | 146 |
| D | 2025-11-18 · 2025-11-24 · 2025-12-16 · 2026-01-08 · 2026-03-02 | 102 |
| E | 2025-11-25 · 2025-12-03 · 2026-01-09 · 2026-01-12 · 2026-03-03 | 80 |
| F | 2025-11-25 · 2025-12-16 · 2026-01-10 · 2026-01-13 · 2026-03-11 | 93 |
| G | 2025-11-22 · 2025-12-05 · 2026-01-12 · 2026-02-11 · 2026-03-12 | 110 |
Days covered by two annotators: 2025-11-18 (B, D) · 2025-11-22 (C, G) · 2025-11-25 (E, F) · 2025-12-03 (A, E) · 2025-12-05 (B, G) · 2025-12-16 (D, F) · 2026-01-09 (A, E) · 2026-01-12 (E, G) · 2026-02-11 (A, G) · 2026-03-02 (B, D) · 2026-03-03 (C, E)
## Controls ## Controls
@@ -218,7 +202,7 @@ Three drawing tools are available in the tool row. The active tool is highlighte
| Action | How | | Action | How |
|---|---| |---|---|
| Draw water mask | Click and drag on the video | | Draw mask | Click and drag on the video |
| Erase mask | Toggle **Eraser** button (turns orange when active), then drag | | Erase mask | Toggle **Eraser** button (turns orange when active), then drag |
| Brush preview | A white circle follows the cursor showing the current brush size | | Brush preview | A white circle follows the cursor showing the current brush size |
| Adjust brush size | **Brush size** slider (250 px, default 5); click **↺** to reset | | Adjust brush size | **Brush size** slider (250 px, default 5); click **↺** to reset |
@@ -261,7 +245,7 @@ Polygons are drawn as overlays and do not affect the mask until you use **Fill**
| Action | How | | Action | How |
|---|---| |---|---|
| Load mask from previous clip | **Load Prev Mask** — copies the saved mask of the previous clip onto the current one; undoable | | Load mask from previous clip | **Load Prev Mask** — copies the saved mask of the previous clip onto the current one; undoable |
| Optical flow first guess | **Auto Segment** — replaces the current mask with an automatic river segmentation; undoable. Disabled when `enabled: false` in `config/optical_flow_config.yaml`. | | Optical flow first guess | **Auto Segment** — replaces the current mask with an automatic segmentation based on motion and brightness; undoable. Disabled when `enabled: false` in `config/optical_flow_config.yaml`. |
### Image display adjustments ### Image display adjustments
@@ -288,7 +272,7 @@ Click **↺** below any slider to restore its default value.
Each annotated clip produces a folder `<out_dir>/<clip_stem>/` with: Each annotated clip produces a folder `<out_dir>/<clip_stem>/` with:
``` ```
mask.png # Binary water mask at full source resolution (always) mask.png # Binary segmentation mask at full source resolution (always)
metadata.json # Survey answers as JSON (always) metadata.json # Survey answers as JSON (always)
frame.png # Middle frame of the clip (always) frame.png # Middle frame of the clip (always)
overlay.png # That frame with the mask blended in green (always) overlay.png # That frame with the mask blended in green (always)
@@ -323,7 +307,7 @@ Keys and values are determined by `config/questions.yaml`. With the default ques
### Clip format ### Clip format
Each clip is a ZIP archive containing a video file (default `left.mp4`, configurable via `filenames.video_in_zip`). The filename encodes the recording timestamp (e.g. `left_20230615T120000.zip`). Each clip is a ZIP archive containing a video file. The video inside the archive must be named `left.mp4` by default; if your archives use a different internal name, set `filenames.video_in_zip` in `config.yaml`. The ZIP filename becomes the output folder name (e.g. `clip_20230501T120000.zip``<out_dir>/clip_20230501T120000/`).
### Frame loading ### Frame loading
@@ -350,14 +334,14 @@ config/
clips.example.txt # Example clip list clips.example.txt # Example clip list
questions.yaml # Survey question definitions questions.yaml # Survey question definitions
optical_flow_config.yaml # Optical flow parameters (set enabled: false to disable Auto Segment) optical_flow_config.yaml # Optical flow parameters (set enabled: false to disable Auto Segment)
src/river_annotation_tool/ src/clip_annotator/
annotation_script.py # Entry point — argument parsing and app launch annotation_script.py # Entry point — argument parsing and app launch
annotator.py # Main QMainWindow — orchestrates all components annotator.py # Main QMainWindow — orchestrates all components
clip_selector.py # Reads the clip list and picks the next clip clip_selector.py # Reads the clip list and picks the next clip
filesystem.py # Storage backend — local passthrough or S3 via s3fs filesystem.py # Storage backend — local passthrough or S3 via s3fs
mask_canvas.py # Drawing widget — brush, undo, erase, mouse events mask_canvas.py # Drawing widget — brush, undo, erase, mouse events
video_loader.py # ZIP extraction and frame resizing video_loader.py # ZIP extraction and frame resizing
compute_optical_flow.py # Optical flow river segmentation (Auto Segment button) compute_optical_flow.py # Optical flow segmentation (Auto Segment button)
config.py # AppConfig dataclass and YAML loader config.py # AppConfig dataclass and YAML loader
__init__.py # Package version __init__.py # Package version
pyproject.toml # Project metadata and dependencies pyproject.toml # Project metadata and dependencies

View File

@@ -3,7 +3,7 @@ requires = ["setuptools>=61.0.0"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "river_annotation_tool" name = "clip_annotator"
authors = [ authors = [
# TODO configure authors # TODO configure authors
# { name = "Jane Smith", email = "jane.smith@example.com" }, # { name = "Jane Smith", email = "jane.smith@example.com" },
@@ -33,7 +33,7 @@ dev = [
] ]
[tool.setuptools.dynamic] [tool.setuptools.dynamic]
version = {attr = "river_annotation_tool.__version__"} version = {attr = "clip_annotator.__version__"}
[tool.ruff] [tool.ruff]
target-version = "py312" target-version = "py312"

View File

@@ -433,14 +433,14 @@ matplotlib==3.10.9 \
--hash=sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf \ --hash=sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf \
--hash=sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1 \ --hash=sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1 \
--hash=sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358 --hash=sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358
# via river-annotation-tool # via clip-annotator
matplotlib-inline==0.2.1 \ matplotlib-inline==0.2.1 \
--hash=sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76 \ --hash=sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76 \
--hash=sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe --hash=sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe
# via # via
# clip-annotator
# ipykernel # ipykernel
# ipython # ipython
# river-annotation-tool
mistune==3.2.0 \ mistune==3.2.0 \
--hash=sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a \ --hash=sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a \
--hash=sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1 --hash=sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1
@@ -527,7 +527,7 @@ opencv-contrib-python-headless==4.12.0.88 \
--hash=sha256:b183e2322468c9d3bd9cac4ba44b272d828ec22842395bcfa51df31765224c0a \ --hash=sha256:b183e2322468c9d3bd9cac4ba44b272d828ec22842395bcfa51df31765224c0a \
--hash=sha256:c57e32812fea2a542bb220088fb3ce8a210fe114c9454d1c9e8cd162e1a1fde8 \ --hash=sha256:c57e32812fea2a542bb220088fb3ce8a210fe114c9454d1c9e8cd162e1a1fde8 \
--hash=sha256:d60a12b915c55a50468c013fcd839e941b49ccc1f37b914b62543382c36bf81d --hash=sha256:d60a12b915c55a50468c013fcd839e941b49ccc1f37b914b62543382c36bf81d
# via river-annotation-tool # via clip-annotator
packaging==26.2 \ packaging==26.2 \
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
@@ -549,7 +549,7 @@ pandas==3.0.2 \
--hash=sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f \ --hash=sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f \
--hash=sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043 \ --hash=sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043 \
--hash=sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab --hash=sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab
# via river-annotation-tool # via clip-annotator
pandocfilters==1.5.1 \ pandocfilters==1.5.1 \
--hash=sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e \ --hash=sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e \
--hash=sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc --hash=sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc
@@ -576,8 +576,8 @@ pillow==12.2.0 \
--hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \ --hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \
--hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5 --hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5
# via # via
# clip-annotator
# matplotlib # matplotlib
# river-annotation-tool
platformdirs==4.9.6 \ platformdirs==4.9.6 \
--hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \
--hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917
@@ -663,7 +663,7 @@ pyside6==6.11.0 \
--hash=sha256:9092cb002ca43c64006afb2e0d0f6f51aef17aa737c33a45e502326a081ddcbc \ --hash=sha256:9092cb002ca43c64006afb2e0d0f6f51aef17aa737c33a45e502326a081ddcbc \
--hash=sha256:b15f39acc2b8f46251a630acad0d97f9a0a0461f2baffcd66d7adfada8eb641e \ --hash=sha256:b15f39acc2b8f46251a630acad0d97f9a0a0461f2baffcd66d7adfada8eb641e \
--hash=sha256:c642e2d25704ca746fd37f56feacf25c5aecc4cd40bef23d18eec81f87d9dc00 --hash=sha256:c642e2d25704ca746fd37f56feacf25c5aecc4cd40bef23d18eec81f87d9dc00
# via river-annotation-tool # via clip-annotator
pyside6-addons==6.11.0 \ pyside6-addons==6.11.0 \
--hash=sha256:413e6121c24f5ffdce376298059eddecff74aa6d638e94e0f6015b33d29b889e \ --hash=sha256:413e6121c24f5ffdce376298059eddecff74aa6d638e94e0f6015b33d29b889e \
--hash=sha256:8ffb40222456078930816ebcac2f2511716d2acbc11716dd5acc5c365179a753 \ --hash=sha256:8ffb40222456078930816ebcac2f2511716d2acbc11716dd5acc5c365179a753 \
@@ -697,7 +697,7 @@ python-discovery==1.2.2 \
python-dotenv==1.2.2 \ python-dotenv==1.2.2 \
--hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \
--hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3
# via river-annotation-tool # via clip-annotator
python-json-logger==4.1.0 \ python-json-logger==4.1.0 \
--hash=sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2 \ --hash=sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2 \
--hash=sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195 --hash=sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195
@@ -723,9 +723,9 @@ pyyaml==6.0.3 \
--hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
# via # via
# clip-annotator
# jupyter-events # jupyter-events
# pre-commit # pre-commit
# river-annotation-tool
pyzmq==27.1.0 \ pyzmq==27.1.0 \
--hash=sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28 \ --hash=sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28 \
--hash=sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113 \ --hash=sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113 \
@@ -811,7 +811,7 @@ ruff==0.15.0 \
s3fs==2026.4.0 \ s3fs==2026.4.0 \
--hash=sha256:5bdce0abb00b0435ee150807a45fea727451dbc22de4cbc116464f8504ab9d37 \ --hash=sha256:5bdce0abb00b0435ee150807a45fea727451dbc22de4cbc116464f8504ab9d37 \
--hash=sha256:de0d2a1f33cdf03831fd2382d278c6e4e31fe57c3bf2f703c61f8aec6b703e2a --hash=sha256:de0d2a1f33cdf03831fd2382d278c6e4e31fe57c3bf2f703c61f8aec6b703e2a
# via river-annotation-tool # via clip-annotator
send2trash==2.1.0 \ send2trash==2.1.0 \
--hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \ --hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \
--hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459 --hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459

View File

@@ -58,7 +58,7 @@ class Annotator(QMainWindow):
self.history: list[str] = [] self.history: list[str] = []
self.history_pos: int = -1 self.history_pos: int = -1
self.setWindowTitle("River Annotator") self.setWindowTitle("Clip Annotator")
self._load_clip(specific=clip) self._load_clip(specific=clip)
self._history_push() self._history_push()
self._init_ui() self._init_ui()

84
uv.lock generated
View File

@@ -304,6 +304,48 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
] ]
[[package]]
name = "clip-annotator"
source = { editable = "." }
dependencies = [
{ name = "matplotlib" },
{ name = "matplotlib-inline" },
{ name = "opencv-contrib-python-headless" },
{ name = "pandas" },
{ name = "pillow" },
{ name = "pyside6" },
{ name = "python-dotenv" },
{ name = "pyyaml" },
{ name = "s3fs" },
]
[package.dev-dependencies]
dev = [
{ name = "notebook" },
{ name = "pre-commit" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "matplotlib", specifier = ">=3.10.8" },
{ name = "matplotlib-inline", specifier = ">=0.2.1" },
{ name = "opencv-contrib-python-headless", specifier = "==4.12.0.88" },
{ name = "pandas", specifier = ">=2.3.3" },
{ name = "pillow", specifier = ">=12.2.0" },
{ name = "pyside6", specifier = ">=6.11.0" },
{ name = "python-dotenv", specifier = ">=1.0" },
{ name = "pyyaml", specifier = ">=6.0" },
{ name = "s3fs", specifier = ">=2024.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "notebook", specifier = "~=7.5" },
{ name = "pre-commit", specifier = "~=4.5" },
{ name = "ruff", specifier = "==0.15.0" },
]
[[package]] [[package]]
name = "colorama" name = "colorama"
version = "0.4.6" version = "0.4.6"
@@ -1528,48 +1570,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" },
] ]
[[package]]
name = "river-annotation-tool"
source = { editable = "." }
dependencies = [
{ name = "matplotlib" },
{ name = "matplotlib-inline" },
{ name = "opencv-contrib-python-headless" },
{ name = "pandas" },
{ name = "pillow" },
{ name = "pyside6" },
{ name = "python-dotenv" },
{ name = "pyyaml" },
{ name = "s3fs" },
]
[package.dev-dependencies]
dev = [
{ name = "notebook" },
{ name = "pre-commit" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "matplotlib", specifier = ">=3.10.8" },
{ name = "matplotlib-inline", specifier = ">=0.2.1" },
{ name = "opencv-contrib-python-headless", specifier = "==4.12.0.88" },
{ name = "pandas", specifier = ">=2.3.3" },
{ name = "pillow", specifier = ">=12.2.0" },
{ name = "pyside6", specifier = ">=6.11.0" },
{ name = "python-dotenv", specifier = ">=1.0" },
{ name = "pyyaml", specifier = ">=6.0" },
{ name = "s3fs", specifier = ">=2024.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "notebook", specifier = "~=7.5" },
{ name = "pre-commit", specifier = "~=4.5" },
{ name = "ruff", specifier = "==0.15.0" },
]
[[package]] [[package]]
name = "rpds-py" name = "rpds-py"
version = "0.30.0" version = "0.30.0"