Skip to content

Commit 59e3450

Browse files
committed
fix: lower detection threshold and improve IR camera timing for Lenovo
- Lower DEFAULT_DETECTION_THRESHOLD from 0.9 to 0.6 (IR cameras score lower due to grayscale/low-contrast images; 0.9 prevents any detection) - Increase IR_TRIGGER_DELAY_MS from 200ms to 1500ms (Lenovo emitters need more time to activate before frame capture) - Increase CAMERA_WARMUP_FRAMES (10->15) and CAMERA_WARMUP_DELAY_MS (100->200ms) for IR exposure stabilization - Always save debug frame on enroll failure with brightness and score info - Add YuNet face score logging to help diagnose threshold issues - Add docs/LENOVO_IR_DEBUG.md with full troubleshooting guide Tested on Lenovo with Ubuntu 25.04, /dev/video2 IR camera.
1 parent 10d1ed7 commit 59e3450

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

LENOVO_IR_DEBUG.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Lenovo IR Camera — Debug Guide
2+
3+
## Symptom
4+
5+
```
6+
Response: ENROLL_FAIL Found 0 faces in ir. Expecting exactly 1.
7+
```
8+
9+
## Root Cause
10+
11+
The YuNet face detector assigns **significantly lower confidence scores** to faces
12+
captured by IR cameras (grayscale image, low contrast, often overexposed).
13+
14+
The default threshold of `0.9` was tuned for standard RGB webcams. On IR cameras
15+
it is nearly impossible to reach. Lowering it to `0.5–0.6` resolves the issue
16+
in most cases.
17+
18+
---
19+
20+
## Quick Fix (no recompilation required)
21+
22+
Edit `/etc/linuxcampam/config.ini`:
23+
24+
```ini
25+
[Auth]
26+
detection_threshold = 0.5
27+
28+
[Capture]
29+
enroll_averaging = on
30+
enroll_average_frames = 7
31+
```
32+
33+
Then restart the daemon:
34+
```bash
35+
sudo systemctl restart linuxcampam
36+
```
37+
38+
---
39+
40+
## Diagnosing with logs
41+
42+
During enrollment, watch the logs in real time:
43+
44+
```bash
45+
journalctl -u linuxcampam -f
46+
```
47+
48+
What to look for:
49+
50+
| Log message | Meaning |
51+
|-------------|---------|
52+
| `0 faces found above threshold (0.9)` | Threshold too high → lower it |
53+
| `Best score: 0.65 \| Threshold: 0.9` | Face found but below threshold → lower to 0.55 |
54+
| `Brightness: 12` | Frame nearly black → IR emitter not activating |
55+
| `Brightness: 240` | Frame overexposed → camera exposure issue |
56+
57+
---
58+
59+
## Inspecting the failed frame
60+
61+
On every failed enrollment the captured frame is automatically saved to:
62+
63+
```
64+
/var/log/linuxcampam/failed_enroll_ir_<username>.jpg
65+
```
66+
67+
Open it to check what the camera actually captured:
68+
```bash
69+
xdg-open /var/log/linuxcampam/failed_enroll_ir_<username>.jpg
70+
```
71+
72+
- **Nearly black frame** → IR emitter did not activate (see section below)
73+
- **Grainy / blurry frame** → increase `enroll_average_frames`
74+
- **Face visible but not detected** → lower `detection_threshold` further
75+
- **No face in frame** → positioning issue during enrollment
76+
77+
---
78+
79+
## IR emitter not activating
80+
81+
If the saved frame is nearly black:
82+
83+
1. Verify `linux-enable-ir-emitter` is installed:
84+
```bash
85+
ls /usr/local/bin/linux-enable-ir-emitter
86+
```
87+
88+
2. Test it manually:
89+
```bash
90+
sudo linux-enable-ir-emitter run
91+
```
92+
93+
3. If not installed:
94+
```bash
95+
sudo apt install linux-enable-ir-emitter
96+
# or from source: https://github.com/EmixamPP/linux-enable-ir-emitter
97+
```
98+
99+
4. Configure for your specific hardware model:
100+
```bash
101+
sudo linux-enable-ir-emitter configure
102+
```
103+
Follow the interactive procedure — move your head in front of the camera
104+
while it tries different configurations. When the emitter blinks and you see
105+
`The infrared emitter has been successfully enabled!` you are done.
106+
107+
---
108+
109+
## Finding the correct IR camera device
110+
111+
```bash
112+
# List all webcams
113+
v4l2-ctl --list-devices
114+
115+
# Identify the IR camera by checking shape and brightness
116+
python3 -c "
117+
import cv2
118+
for i in range(4):
119+
cap = cv2.VideoCapture(i)
120+
if not cap.isOpened():
121+
print(f'video{i}: could not open')
122+
continue
123+
ret, frame = cap.read()
124+
cap.release()
125+
if ret:
126+
print(f'video{i}: shape={frame.shape} brightness={frame.mean():.0f}')
127+
"
128+
```
129+
130+
The IR camera has **1 channel** (shape like `(360, 640)`) instead of 3 (RGB).
131+
On Lenovo laptops it is typically `/dev/video2`.
132+
133+
---
134+
135+
## Recommended settings for Lenovo ThinkPad / IdeaPad
136+
137+
```ini
138+
[Auth]
139+
detection_threshold = 0.5
140+
timeout_ms = 5000
141+
142+
[Capture]
143+
enroll_hdr = off ; IR cameras do not support HDR
144+
enroll_averaging = on
145+
enroll_average_frames = 7
146+
147+
[Hardware]
148+
camera_path_ir = /dev/video2 ; verify with v4l2-ctl --list-devices
149+
```
150+
151+
---
152+
153+
## Code changes (this fix)
154+
155+
| File | Change |
156+
|------|--------|
157+
| `include/constants.hpp` | `IR_TRIGGER_DELAY_MS`: 200 → 1500 ms |
158+
| `include/constants.hpp` | `CAMERA_WARMUP_FRAMES`: 10 → 15 |
159+
| `include/constants.hpp` | `CAMERA_WARMUP_DELAY_MS`: 100 → 200 ms |
160+
| `src/service/config.hpp` | `DEFAULT_DETECTION_THRESHOLD`: 0.9 → 0.6 |
161+
| `src/service/auth_engine.cpp` | Log YuNet score + brightness in `generateEmbedding` |
162+
| `src/service/auth_engine.cpp` | Always save failed enrollment frame with diagnostic info |
163+
| `config/config.ini` | Updated default `detection_threshold` + averaging enabled |

0 commit comments

Comments
 (0)