-
v1.5.0 — Train-till-Plateau Stable
released this
2026-05-27 12:02:49 +00:00 | 53 commits to main since this releaseBrush trains for
--total-steps(default 30 000) regardless of whether the splat already converged. v1.5.0 adds an opt-in patience-stop: hold out ~10 % of frames, monitor PSNR while training, and SIGTERM Brush when the curve flattens. On a typical converging capture this can save 30-50 % of the Brush stage.Added
PlateauMonitorintrain.py— pure-logic class that polls<output_dir>/eval_<step>/directories, computes mean PSNR viacompute_eval_psnr, maintains the(step, psnr)history, and decidesshould_stoponce the lastpatienceconsecutive Δ-PSNR values are all belowmin_delta_psnrand the last step is ≥min_steps. Idempotent; missing/in-progress eval dirs are handled gracefully.compute_eval_psnr(eval_dir, frames_dir)— cv2-based mean PSNR across rendered eval images vs originals. Pairs by filename stem so Brush's<orig>.pnglines up with ourframes/<orig>.jpg. Originals get downscaled (cv2.INTER_AREA) to the render resolution before MSE.- Six new
[brush]config fields:plateau_enabled(bool, defaultfalse),plateau_eval_split_every(2-50, default 10),plateau_eval_every(100-10000, default 1000),plateau_min_steps(default 5000),plateau_patience(1-20, default 3),plateau_min_delta_psnr(0 < x ≤ 5, default 0.05). Cross-field validator rejectsplateau_min_steps > max_stepsonly when the feature is enabled (so a CI-config loweringmax_stepsdoesn't have to touch plateau fields). build_brush_commandextension: whenplateau_enabled, appends--eval-split-every,--eval-every,--eval-save-to-disk, and ties--export-everytoplateau_eval_everyso every eval checkpoint has a fresh PLY for the SIGTERM-safe stop mechanism.run_brushorchestration: spawns a daemonPlateauMonitorthread, polls every 5 s, sendsproc.terminate()onshould_stop. The non-zero returncode from our own SIGTERM is recognised and not re-raised asCalledProcessError. Edge case: SIGTERM before any export →RuntimeErrorwith an actionable hint.
Discovery (in spec doc)
A 250-step Brush probe against
max_strasse/brush_datasetconfirmed three load-bearing assumptions, all documented indocs/superpowers/specs/2026-05-27-v15-train-till-plateau-design.md:- Brush emits no stdout/stderr in subprocess mode (TUI suppressed) — stdout-PSNR parsing is not viable, hence the filesystem-watch approach.
--eval-save-to-diskis reliable and writeseval_<step>/<orig_filename>.png.--eval-split-every Nis deterministic — every Nth frame, sorted by filename.
Tests
- 13 new unit tests (5 in
test_config.py, 6 intest_train.pyfor PSNR helper, 6 intest_train.pyforPlateauMonitor, 2 intest_train.pyforbuild_brush_commandbranches). All purely synthetic — no Brush invocation, no real-world frames. - 347 tests passing (up from 328 at v1.4.6), ruff clean, mypy clean on
train.py.
UX
In the default config (
plateau_enabled = false), v1.5.0 changes nothing. Enable via your user config:[brush] plateau_enabled = true # defaults are fine; tune patience / min_delta_psnr if you want aggressive stopsWhen triggered,
pipeline.logwill contain atrain.plateau_detectedevent with the full history oftrain.evalevents leading up to the stop. The final scene.ply is whichever export was written most recently before SIGTERM (one perplateau_eval_everysteps).Default-on candidacy
v1.5.0 ships opt-in. After real-world validation on 2-3 captures, a follow-up release will likely flip the default to
true.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads