Demo 05 — Segmentation benches: vignetting and glare
Reproduce the two failures that cost the most time — labels instead of the card, and lamp reflections as parts — and the fixes, with figures the script draws itself.
Needs: laptop, numpy, OpenCV. No hardware.
Bench A — vignetting
The Pi camera darkens towards the edges. A single global belt colour taken from the frame border then sits far from the bright centre: the threshold rises (to ~31 in our scene) and a black card body falls below it, while its white label stays above. Result on the real machine: three label frames and "unknown" instead of one card.
VORSA_BENCH_BILDER=bench python3 tools/test_farbmaske.py
Output we got:
ok parabel: leeres Band, Vordergrund % 0.00
ok parabel: Karte IoU 0.98
ok parabel: foreground_mask gesamt IoU 0.97
ok geklippt: leeres Band, Vordergrund % 1.85
ok geklippt: Karte IoU 0.87
ok geklippt: foreground_mask gesamt IoU 0.86
ok cos4: leeres Band, Vordergrund % 0.08
ok cos4: Karte IoU 0.98
ok cos4: foreground_mask gesamt IoU 0.97
ok flach: leeres Band, Vordergrund % 0.00
ok flach: Karte IoU 0.98
ok flach: foreground_mask gesamt IoU 0.97
ok cos4: Karte IoU mit 2. Teil am Rand 0.95
ok cos4: grosse Karte (45 %) IoU 1.00
ok klein 220x300: Vordergrund % (Soll ~14.7) 15.04
ok Laufzeit 640x480 ms (x86) 38.31
ok cos4 Rauschen 8: leer Vordergrund % 0.07
FEHLER: 0

Left: the scene (dark-green belt, cos⁴ vignetting, black card with a white and a red label). Middle: the old method — the bright centre becomes foreground, the card becomes background, only the labels survive. Right: the local belt model — a smooth 4th-order surface per Lab channel fitted robustly over five rounds of shrinking tolerance; the card is one clean region (IoU 0.98 vs. 0.07 before). "geklippt" is an artificial profile with a kink; real lenses are smooth (cos⁴), and that row is allowed 3 % false foreground.
Bench B — glare
Point-like lamps leave saturated spots with a soft halo that flickers with the LED driver and the exposure control. A single-frame empty-belt reference cannot know the flicker; the halo exceeded the fixed threshold of 18 and became a "part" every few frames.
VORSA_BENCH_BILDER=bench python3 tools/test_glanz.py
Output we got:
ok ohne Leerbild: leeres Band mit Blendflecken, Vordergrund % 0.00
ok ohne Leerbild: gefundene Teile auf leerem Band 0.00
ok ohne Leerbild: Karte neben Blendflecken IoU 0.97
ok Leerbild ohne Rauschkarte: Vordergrund % (Info) 0.00
ok Leerbild MIT Rauschkarte: leeres Band Vordergrund % 0.00
ok Leerbild MIT Rauschkarte: Teile auf leerem Band 0.00
ok Leerbild MIT Rauschkarte: Karte IoU 1.00
ok Karte ueber Blendfleck: IoU 0.95
ok grosses weisses Teil bleibt (ohne Leerbild) IoU 0.98
FEHLER: 0

Middle: without the filter, three lamp reflections are parts. Right: saturated spots (all channels ≥ 246) in small blobs (< 2 % of the frame) are removed together with a halo of 1.8 × their radius; the card next to them keeps 97 % of its area, a card over a spot 95 %, and a large white part (> 2 %) is not touched (98 %).

The second mechanism: "Empty belt" in the UI records 16 frames over ~2 s; the median is the reference, the largest per-pixel deviation the noise map, and the difference threshold becomes per pixel (max(18, 1.5 × noise + 6)). Flickering spots get a high threshold, the calm belt a low one. Recorded with the belt running, the noise map also contains the belt texture in every phase.
Proves
Both algorithms do what they are designed to do on controlled scenes, and the empty-belt reference removes the residual failures.
Does not prove
Line accuracy. On the machine the effect was confirmed visually (labels → card; glare → nothing), not measured against ground truth.
Failure modes
None expected; runtime ~40 ms per 640 × 480 frame on x86 (Laufzeit line), a few ms for zone crops.
Time: 2 minutes.
