The visdeurbel (fish doorbell) is a fixed underwater camera at the Weerdsluis lock in Utrecht, where migrating fish gather every spring. Volunteers watch the stream and press a button when they spot a fish — which opens the lock gate to let them through.
I thought: what if a computer watched instead?
So I built fish-spotter — a Python pipeline that resolves the live HLS stream, runs detection, and saves annotated frames whenever a fish is confirmed.
Small problem: I haven’t seen a single fish on the visdeurbel.
Not one. The water is gray, murky, and thoroughly fishless — at least during my testing window. So I grabbed a recording of Miami’s Coral City Camera (a coral reef livestream packed with tropical fish) to validate the pipeline end-to-end. It works beautifully there.
The interesting engineering bits:
HLS is not a video stream — it’s a file delivery protocol. Segments arrive in 5-second bursts. A naive cap.read() loop causes stop-start playback. The fix: a dedicated reader thread fills a 300-frame queue; a worker thread drains it at exactly 30fps regardless of network timing.
Detection rate ≠ display rate. YOLOv8x (Open Images, RTX 4090) runs at 3fps; the display runs at 30fps. Bounding boxes from the last inference are overlaid on every frame. Smooth video, cheap GPU bill.
MOG2 background subtraction may still win on the real visdeurbel. A static gray canal is the ideal scene for classical CV. A model trained on colorful reef fish is not. The right detector depends on the scene, not the benchmark leaderboard.
Now I just need to wait for a fish to actually show up.