Add optical flow Auto Segment button

Annotators can now press Auto Segment to replace the current mask with
an automatic river segmentation based on dense optical flow magnitude
and frame brightness. The result is pushed onto the undo stack, so it
can be refined or reverted like any other mask operation.

Parameters (norm_squared_threshold, gaussian_kernel, brightness_range)
live in a separate config/optical_flow_config.yaml; the button is only
enabled when optical_flow_config_file is set in config.yaml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 15:13:10 +02:00
parent 47432cec4f
commit 67c9a1152c
6 changed files with 130 additions and 13 deletions

View File

@@ -95,6 +95,7 @@ max_frames: 100 # max frames to extract per clip
data_dir: data/clips # directory containing ZIP archives
out_dir: data/annotation_results
clips_file: config/clips.txt
# optical_flow_config_file: config/optical_flow_config.yaml # optional, enables Auto Segment
questions:
- section: River
@@ -108,6 +109,23 @@ questions:
Add, remove, or reorder questions directly in the YAML — the UI rebuilds automatically. `key` is what gets saved in `metadata.json`; `default` selects the pre-checked option (omit or set to `null` to leave unselected).
### Optical flow segmentation (optional)
Set `optical_flow_config_file` in `config.yaml` to point to a YAML file that enables 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:
- **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.
- **Brightness** — pixels outside a brightness window are excluded (removes sky, saturated glare, etc.).
```yaml
# config/optical_flow_config.yaml
enabled: true
norm_squared_threshold: 0.06 # fraction of max flow² that counts as moving
gaussian_kernel: [5, 5] # blur kernel applied to the reference frame before brightness check
brightness_range: [2, 253] # [min, max] greyscale brightness to keep
```
`enabled: false` disables the button without removing the config 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.
@@ -176,6 +194,7 @@ Polygons are drawn as overlays and do not affect the mask until you use **Fill**
| Toggle mask overlay | **Hide Mask / Show Mask** — button turns red when hidden; does not affect mask data |
| Mask transparency | **Mask Alpha** slider; click **↺** to reset |
| Load mask from previous clip | **Load Prev Mask** — copies the saved mask of the previous clip; undoable with **Undo** |
| Optical flow first guess | **Auto Segment** — runs automatic river segmentation and replaces the current mask; undoable with **Undo**. Only enabled when `optical_flow_config_file` is set in `config.yaml`. |
### Image display adjustments
@@ -263,18 +282,20 @@ When a clip is loaded that already has a saved `mask.png` and `metadata.json`, t
```
config/
config.yaml # Your local config (git-ignored, copy from example)
config.example.yaml # Example config to copy and edit
clips.txt # Your clip list (git-ignored, copy from example)
clips.example.txt # Example clip list
config.yaml # Your local config (git-ignored, copy from example)
config.example.yaml # Example config to copy and edit
clips.txt # Your clip list (git-ignored, copy from example)
clips.example.txt # Example clip list
optical_flow_config.yaml # Optional optical flow parameters (enable via config.yaml)
src/river_annotation_tool/
annotation_script.py # Entry point — argument parsing and app launch
annotator.py # Main QMainWindow — orchestrates all components
clip_selector.py # Reads the clip list and picks the next clip
mask_canvas.py # Drawing widget — brush, undo, erase, mouse events
video_loader.py # ZIP extraction and frame resizing
config.py # AppConfig dataclass and YAML loader
__init__.py # Package version
annotation_script.py # Entry point — argument parsing and app launch
annotator.py # Main QMainWindow — orchestrates all components
clip_selector.py # Reads the clip list and picks the next clip
mask_canvas.py # Drawing widget — brush, undo, erase, mouse events
video_loader.py # ZIP extraction and frame resizing
compute_optical_flow.py # Optical flow river segmentation (Auto Segment button)
config.py # AppConfig dataclass and YAML loader
__init__.py # Package version
pyproject.toml # Project metadata and dependencies
```