feat: auto-capture scrie direct în calibration/frames/ (elimină pasul manual)
Live loop-ul dumpa frame-uri pe schimbare de culoare în samples/, iar userul copia manual cele utile în calibration/frames/ pentru labelling și regresie. Pas inutil — acum scrie direct în corpus. - samples_dir → calibration/frames/ (mkdir parents=True) - stub capture (ATM_STUB_CAPTURE pentru smoke test Linux) citește din aceeași locație - 8 PNG-uri orfane din samples/ (20260421_*) mutate în corpus - CLAUDE.md clarifică: filename = culoarea detectată (poate fi greșită); calibration_labels.json rămâne singurul ground truth (manual) Impact zero pe validate-calibration (iterează peste labels.json, ignoră fișiere extra) și test_scenarios_regression.py (referă doar frame-uri curate din scenarios.json). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -19,12 +19,12 @@ pytest tests/test_scenarios_regression.py -v # FSM pe imagini reale
|
||||
|
||||
## Calibration corpus
|
||||
|
||||
`calibration/` — persistent, auto-suficient, safe to keep când `samples/` și `logs/fires/` se golesc. Conține:
|
||||
- `frames/` — PNG-uri `{ts}_{color}.png` (ground truth în nume)
|
||||
- `calibration_labels.json` — gate offline pentru `atm validate-calibration`
|
||||
`calibration/` — persistent, auto-suficient. Conține:
|
||||
- `frames/` — PNG-uri raw `{ts}_{color}.png` scrise **automat** de live loop la fiecare schimbare de culoare (filename = culoarea detectată, poate fi greșită)
|
||||
- `calibration_labels.json` — ground truth **manual** (gate offline pentru `atm validate-calibration`)
|
||||
- `scenarios.json` — secvențe FSM pentru `tests/test_scenarios_regression.py`
|
||||
|
||||
Când adaugi un frame: copiezi din `logs/fires/` → redenumești `{ts}_{color}.png` → adaugi entry în JSON. Validare după orice recalibrare.
|
||||
Workflow după sesiune: review frame-urile noi din `frames/`, adaugi entry-uri în `calibration_labels.json` cu culoarea pe care ai văzut-o TU pe chart (nu neapărat cea din filename), rulezi `atm validate-calibration`.
|
||||
|
||||
## Telegram commands (live)
|
||||
|
||||
|
||||
BIN
calibration/frames/20260421_164210_gray.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
calibration/frames/20260421_164452_gray.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
calibration/frames/20260421_165209_gray.png
Normal file
|
After Width: | Height: | Size: 236 KiB |
BIN
calibration/frames/20260421_170045_dark_green.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
calibration/frames/20260421_174502_yellow.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
calibration/frames/20260421_174804_gray.png
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
calibration/frames/20260421_220346_dark_red.png
Normal file
|
After Width: | Height: | Size: 248 KiB |
BIN
calibration/frames/20260421_222108_gray.png
Normal file
|
After Width: | Height: | Size: 243 KiB |
@@ -1382,8 +1382,12 @@ async def run_live_async(cfg, duration_s=None, capture_stub: bool = False) -> No
|
||||
|
||||
start = time.monotonic()
|
||||
heartbeat_due = time.monotonic() + cfg.heartbeat_min * 60
|
||||
samples_dir = Path("samples")
|
||||
samples_dir.mkdir(exist_ok=True)
|
||||
# Dump raw frames direct în corpusul de calibrare — fără pas manual
|
||||
# de copy-paste din samples/. calibration_labels.json rămâne manual
|
||||
# (filename-ul folosește culoarea detectată, care poate fi greșită;
|
||||
# curarea labels.json e ce face validate-calibration truthful).
|
||||
samples_dir = Path("calibration") / "frames"
|
||||
samples_dir.mkdir(parents=True, exist_ok=True)
|
||||
fires_dir = Path("logs") / "fires"
|
||||
fires_dir.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
@@ -1496,7 +1500,7 @@ def _build_capture(cfg, capture_stub: bool = False):
|
||||
|
||||
if use_stub:
|
||||
import itertools
|
||||
samples_dir = Path("samples")
|
||||
samples_dir = Path("calibration") / "frames"
|
||||
pngs = sorted(samples_dir.glob("*.png")) if samples_dir.exists() else []
|
||||
_cycle = itertools.cycle(pngs) if pngs else None
|
||||
|
||||
|
||||