diff --git a/crates/visaged/src/engine.rs b/crates/visaged/src/engine.rs index 274c649..b92e600 100644 --- a/crates/visaged/src/engine.rs +++ b/crates/visaged/src/engine.rs @@ -164,12 +164,15 @@ pub fn spawn_engine( None }; - // Discard warmup frames for camera AGC/AE stabilization + // Discard warmup frames for camera AGC/AE stabilization — with the emitter + // active, in one continuous stream. Auto-gain only adapts while streaming; + // warming up against ambient light leaves it high, and the first lit + // capture after start is then saturated white and fails detection. if warmup_frames > 0 { tracing::info!(count = warmup_frames, "discarding warmup frames"); - for _ in 0..warmup_frames { - let _ = camera.capture_frame(); - } + activate_emitter(&emitter); + let _ = camera.capture_frames(warmup_frames); + deactivate_emitter(&emitter); } let (tx, mut rx) = mpsc::channel::(4); diff --git a/docs/operations-guide.md b/docs/operations-guide.md index 298e992..e6a0f9e 100644 --- a/docs/operations-guide.md +++ b/docs/operations-guide.md @@ -322,6 +322,7 @@ Environment=VARIABLE=value | `VISAGE_VERIFY_TIMEOUT_SECS` | `10` | Max seconds for a verify attempt | | `VISAGE_FRAMES_PER_VERIFY` | `3` | Frames captured per authentication | | `VISAGE_FRAMES_PER_ENROLL` | `5` | Frames captured per enrollment | +| `VISAGE_WARMUP_FRAMES` | `4` | Usable frames discarded at daemon start, captured with the IR emitter active so auto-gain settles against it. Raise it if the first verify after start fails and later ones work. | | `VISAGE_EMITTER_ENABLED` | `1` | Set to `0` to disable IR emitter | | `VISAGE_LIVENESS_ENABLED` | `1` | Set to `0` to disable passive liveness detection (development only) | | `VISAGE_LIVENESS_MIN_DISPLACEMENT` | `0.8` | Minimum eye landmark displacement (px) for liveness check |