Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine Sammlung, die sich von allein fuellt. Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert — ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten, gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte, Stufen und 15 Abzeichen. Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto, keine Telemetrie; die Bilder verlassen das eigene Netz nicht. Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und braucht deshalb weder eine erreichbare Adresse noch einen offenen Port — er laeuft hinter einem gewoehnlichen Router. Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs. AGPL-3.0.
6.7 KiB
Betrieb
← zurück zum Überblick · alle Kapitel
Live zuschauen, mehrere Kameras, der Detector auf einem eigenen Host und das Deployen der Web-App. Also alles, was nach dem ersten erkannten Vogel kommt.
Live zuschauen
Eine Kamera liefert einen Stream. Wer direkt bei ihr zuschaut, nimmt ihn der Erkennung weg — auf einem Pi Zero W ist das keine Einstellung, sondern Physik: die Funkstrecke trägt rund 2 Mbit/s, ein zweiter Client sättigt sie.
Der Detector liest den Stream aber ohnehin rund um die Uhr. Also verteilt er ihn weiter:
python -m detector watch --source http://kamera:8080/stream --live-port 8131
Damit schauen beliebig viele Leute zu, und die Kamera merkt nichts davon — sie hat weiterhin genau einen Zuschauer. Solange niemand hinsieht, wird auch nichts umkodiert: bei MJPEG-Quellen gehen die JPEG-Bytes unverändert durch, und das Kodieren für andere Quellen passiert im HTTP-Thread, einmal je Bild statt einmal je Zuschauer. Die Erkennung wird davon nicht langsamer.
| Adresse | wozu |
|---|---|
http://<detector>:8131/live |
MJPEG, für mehrere Zuschauer |
http://<detector>:8131/bild.jpg |
Einzelbild |
http://<detector>:8131/ |
Seite mit Livebild |
In der Web-App
Eingetragen wird das im Django-Admin bei der Kamera: Live-Adresse auf
http://<detector>:8131/live, dann Live-Bild zeigen ankreuzen. Danach gibt
es die Seite /live/ und einen Eintrag in der Navigation. Ohne Freigabe
existiert beides nicht — kein Link, keine Seite, 404.
Das Bild lädt der Browser direkt beim Verteiler; die Web-App reicht nichts durch. Sonst hinge an jedem Zuschauer eine offene Verbindung im Webserver.
Für eine öffentliche Instanz:
VOGELALBUM_LIVE_ERLAUBT=0. Dann ist Live nicht einschaltbar, auch nicht mit einem Fehlklick im Admin, und die Admin-Liste zeigt „gesperrt". Ein Livebild aus einem Privatgarten soll nicht an einem Häkchen hängen — für einen Showcase ist die Sammlung ohnehin das Interessantere.
Der Verteiler selbst kennt keine Anmeldung. Er gehört ins Heimnetz.
Mehrere Kameras
Jede Kamera bekommt einen eigenen Token. Der Token identifiziert sie — die Web-App muss nie wissen, wo sie steht, und ein kompromittierter Standort lässt sich einzeln stilllegen, ohne die anderen anzufassen.
manage.py camera add futterhaus --name "Futterhaus Garten" --location "Garten"
manage.py camera add nistkasten --name "Nistkasten Apfelbaum"
manage.py camera list
manage.py camera rotate futterhaus # Token erneuern
manage.py camera disable nistkasten
Der Token wird genau einmal ausgegeben; gespeichert ist nur sein SHA-256. Bei 256 Bit Entropie braucht es kein Salt, und wer die Datenbank in die Hand bekommt, kann damit trotzdem nichts einliefern.
Pro Kamera läuft eine eigene detector watch-Instanz. Sie können auf
demselben Host laufen; rechne mit rund 40 % eines Kerns je Kamera bei
--classify-every 2 und 4 fps.
Die Sammlung ist dabei gemeinsam: Findet die Gartenkamera die erste Amsel, ist sie im Dex freigeschaltet — die Nistkastenkamera bekommt für dieselbe Art keinen zweiten Erstfang. Das ist Absicht, es ist die Sammlung eines Menschen und nicht die einer Kamera. Die Sichtungsliste lässt sich trotzdem nach Kamera filtern.
manage.py camera list markiert Kameras, die sich über eine Stunde nicht
gemeldet haben — der schnellste Weg zu „warum kommt da nichts mehr".
Betrieb
Detector auf einem eigenen Host
Ein frischer Debian- oder Ubuntu-Container, dann:
sudo ./scripts/setup_detector_host.sh
Das Skript legt den Dienstbenutzer vogelalbum an, klont nach /opt/vogelalbum,
baut das venv, holt die Modelle, schreibt /etc/vogelalbum.env und installiert
die systemd-Unit. Idempotent — ein zweiter Lauf aktualisiert nur.
Danach /etc/vogelalbum.env ausfüllen und systemctl enable --now vogelalbum-detector.
Dimensionierung
| CPU | 1 Kern je Kamera (bei --classify-every 2 und ~4 fps rund 40 % Last) |
| RAM | 512 MB je Instanz, 1 GB für Luft |
| Platte | 4 GB fürs System, plus ~150 kB je gespeicherter Sichtung |
Netz: ausgehend zur Kamera und zur Web-App. Eingehend nichts.
Achtung, wenn Kamera und Erkennungs-Host in verschiedenen Netzsegmenten liegen: Die Route muss stehen. Bei uns hängt die Kamera im WLAN-Segment und der Server im LAN — ein Scan des falschen Segments findet sie schlicht nicht.
Zweite Kamera: eigene /etc/vogelalbum-<name>.env, die Unit als
vogelalbum-detector@.service ablegen, dann systemctl enable --now vogelalbum-detector@nistkasten.
Die Web-App deployen
Nur die Web-App wird deployt. Der Detector bleibt im Heimnetz — er braucht Sichtverbindung zur Kamera.
- DNS auf den Host zeigen lassen.
- Anwendung aus dem Repo anlegen, Build über
docker-compose.yml. ⚠️ Der Eingang istmedienauf Port 8080, nichtwebauf 8000. Das compose startet zwei Dienste: Gunicorn und einen nginx davor, der die Vogelfotos vom Volume ausliefert. Zeigt der Reverse Proxy direkt aufweb, lädt die Seite — aber jede Karte der Sammlung hat ein kaputtes Bild, denn Django liefert unterDJANGO_DEBUG=0keine Medien aus. Begründung ausführlich in deploy/nginx-media.conf. - Persistentes Volume auf
/datamounten. Das ist der Schritt, den man vergisst — ohne ihn löscht jeder Deploy Sammlung und Fotos. - Umgebungsvariablen setzen (siehe Einstellungen).
- Denselben Token auf dem Erkennungs-Host eintragen und
VOGELALBUM_APIauf die öffentliche Adresse setzen. - Auf einer öffentlichen Instanz
VOGELALBUM_LIVE_ERLAUBT=0setzen — steht imdocker-compose.ymlschon drin. Ohne die Angabe wäre das Livebild aus dem eigenen Garten erlaubt, denn der Standard im Code ist1.
Der erste Start dauert ein paar Minuten: Der Entrypoint holt einmalig die
Referenzfotos. Danach kostet der Schritt nichts mehr. migrate,
collectstatic und seed_dex erledigt er ebenfalls — neue Arten im Katalog
landen also mit dem nächsten Deploy automatisch in der Datenbank.
📘 Schritt für Schritt mit Coolify, inklusive Umzug einer bestehenden Sammlung: deploy/COOLIFY.md. Dort steht auch, wie Datenbank und Fotos als 1:1-Kopie auf das Volume kommen — Kamera-Tokens, Handbestimmungen und Abzeichen bleiben dabei erhalten, und die Kameras zu Hause liefern mit ihrem bisherigen Token weiter.
Einbinden auf einer anderen Seite
/widget/ ist dafür da — keine Navigation, transparenter Hintergrund:
<iframe src="https://vogelalbum.example.org/widget/"
style="width:100%;height:110px;border:0" loading="lazy"></iframe>
Wer lieber selbst gestaltet, nimmt /api/stats/ — Level, Artenzahl und die
letzte Sichtung als JSON.