Files
vogelalbum/docs/kamera.md
T
christian cc9e0786c2 Vogelalbum 1.0.0
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.
2026-10-14 18:55:23 +00:00

201 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Die Kamera anschließen
<sub>[← zurück zum Überblick](../README.md) · [alle Kapitel](README.md)</sub>
Vogelalbum braucht von der Kamera nur einen Stream. Hier steht, welche Formen
davon gelesen werden, wie man die Schwellwerte einstellt und — die Frage, an
der ein Aufbau wirklich scheitert — **wie groß ein Vogel im Bild sein muss**.
Der Aufbau der Kameraseite selbst steht getrennt: [Raspberry
Pi](../pi/README.md) oder [ESP32-S3](../esp32cam/README.md).
**Noch keine Kamera aufgebaut?** Es gibt zwei fertige Kameraseiten, beide mit
denselben Pfaden — der Detector merkt den Unterschied nicht:
- **[pi/README.md](../pi/README.md)** — Raspberry Pi mit Kameramodul, MJPEG-Server
als Dienst (`pi/vogelkamera.py`). Die bessere Optik.
- **[esp32cam/README.md](../esp32cam/README.md)** — ESP32-S3 mit Kameramodul
(`esp32cam/`). Ein Bruchteil des Preises, externe Antenne möglich, und
`/status` liefert Bildrate, Funkstärke und Temperatur, ohne den Stream
anzufassen.
Beide Anleitungen nennen die drei Dinge, die man dabei einmal falsch macht —
zweiter Zuschauer, Hitze im Kasten, Bildrate am Licht statt an der CPU.
Der Detector spricht MJPEG, RTSP, Einzelbild-URLs und Ordner:
```bash
# MJPEG (mjpg-streamer, motion, picamera2-Beispielserver)
python -m detector watch --source "http://kamera.local:8080/stream"
# Einzelbild im Sekundentakt — genügsamer, für einen Nistkasten meist genug
python -m detector watch --source "http://kamera.local/snapshot.jpg" --interval 1
# RTSP (braucht ffmpeg auf dem Host)
python -m detector watch --source "rtsp://kamera.local:8554/cam"
# Ordner mit Bildern, zum Ausprobieren ohne Kamera
python -m detector watch --source ./testbilder
```
Mit Anbindung an die Web-App:
```bash
python -m detector watch \
--source "http://kamera.local:8080/stream" \
--api http://127.0.0.1:8000 \
--token "$VOGELALBUM_TOKEN" \
--save-to /var/lib/vogelalbum/rohdaten
```
`--save-to` schreibt Bild und JSON zusätzlich auf Platte. Das kostet fast
nichts und ist später das Trainingsmaterial fürs eigene Modell.
### Schwellwerte einstellen
Die Voreinstellungen passen zu *unserer* Kamera. An einer anderen gehören sie
gemessen:
```bash
python -m detector tune --source "http://kamera.local:8080/stream"
```
Zeigt live den Anteil bewegter Pixel und die Bildhelligkeit. `--motion-ratio`
gehört knapp über den Ruhepegel — hoch genug, dass Wind und Lichtwechsel nicht
auslösen, niedrig genug für eine Blaumeise am Bildrand. Liegt der Ruhepegel
ohnehin hoch, ist `--gate detector` die bessere Wahl.
Das ist aber nur die Bewegungsschwelle. Ob die Kamera überhaupt an der
richtigen Stelle hängt, beantwortet [`detector karte`](#hängt-die-kamera-richtig-detector-karte)
— und zwar bevor man Tage auf den ersten Vogel wartet.
### Abschaltung bei Dunkelheit
Eine Kamera ohne Infrarot liefert nachts ein schwarzes Bild. Es durch zwei
neuronale Netze zu schicken kostet Strom und bringt nichts, deshalb pausiert
der Detector von selbst.
Entschieden wird über die **Bildhelligkeit**, nicht über Sonnenauf- und
-untergang. Das braucht keine Koordinaten und misst genau das, worauf es
ankommt: ob man etwas sieht. Ein Futterhaus unter einer Straßenlaterne bleibt
nutzbar, ein zugewachsenes ist schon vor Sonnenuntergang zu dunkel.
Zwischen Abschalt- und Einschaltschwelle liegt bewusst Abstand — ohne diese
Hysterese flattert die Entscheidung in der Dämmerung im Sekundentakt.
Eine Infrarotkamera liefert nachts ein graues, aber helles Bild und läuft
damit von selbst weiter. `--nachtsicht` ist nur nötig, wenn die Automatik
danebenliegt.
Gemessen im Betrieb: statt rund 40 % eines Kerns bleiben nachts 9 %, und die
gehen fast vollständig für das Dekodieren des MJPEG-Stroms drauf.
### Dauerprotokoll
Sechs Minuten zuschauen beantwortet die Frage „übersehen wir Vögel?" nur für
diese sechs Minuten. Über Tage beantwortet sie ein Protokoll:
```bash
python -m detector watch --source ... --protokoll /var/lib/vogelalbum/protokoll.csv
```
Geschrieben werden zwei Sorten Zeilen:
* **jede Erkennung ab `--protokoll-ab`** (Standard 0.08, weit unter der
Betriebsschwelle von 0.4) — auch die, die für eine Sichtung nicht gereicht
hat. Genau daran sieht man, ob die Schwelle zu hoch steht oder die Vögel
schlicht zu klein im Bild sind.
* **ein Lebenszeichen je Viertelstunde**, immer, auch wenn nichts passiert
ist. Diese Zeilen sind die wichtigeren: Ein Protokoll, das nur bei Erfolg
schreibt, sieht bei einem Ausfall genauso aus wie bei einem ruhigen Tag.
### Wie groß muss ein Vogel im Bild sein
Gemessen, indem ein echter Vogelausschnitt in verschiedenen Größen in ein
Bild derselben Kamera montiert wurde:
| Anteil am Bild | Detektor-Score | |
|---|---|---|
| 15 % | 0.93 | ✅ |
| 10 % | 0.77 | ✅ |
| 5 % | 0.77 | ✅ |
| 3 % | 0.23 | ❌ unter der Schwelle |
| 1 % | 0.07 | ❌ |
Die Grenze liegt bei rund **5 % der Bildfläche** — bei 1280×720 also etwa
260×175 Pixel.
Aufschlussreich ist, was dabei *nicht* zusammenbricht: Die Artbestimmung
liegt selbst bei 1 % Fläche noch bei 100 %. Der Engpass ist also nicht, die
Art zu erkennen, sondern überhaupt zu merken, dass da ein Vogel ist. Für den
Aufbau heißt das: **näher heran schlägt höhere Auflösung.**
Die 5 % sind allerdings **kein fester Wert, sondern hängen am Hintergrund.**
Dieselbe Messung nach einem Umsetzen der Kamera — statt ruhigem Grund nun ein
Baum im Wind hinter dem Futter — ergab erst ab **8 %** eine zuverlässige
Erkennung. Deshalb misst man an der eigenen Kamera nach, statt eine Zahl aus
diesem README zu übernehmen. Genau dafür gibt es das nächste Kommando.
### Hängt die Kamera richtig? (`detector karte`)
Die Größe ist nur die halbe Antwort. Die andere ist, **wo** im Bild der Vogel
sitzt — und da gibt es Zonen, in denen der Detektor auch einen großen Vogel
nicht findet.
```bash
python -m detector karte --source http://vogelhaus.local:8080/stream \
--vogel testdata/amsel.jpg --anteil 0.08 --ziel karte.jpg
```
Ein echtes Vogelfoto wird mit weicher Maske über ein Raster von Positionen in
aktuelle Bilder der eigenen Kamera montiert und jedes Mal durch den Detektor
geschickt. Heraus kommt eine Ampel über dem eigenen Kamerabild — grün erkannt,
rot nicht — plus dieselbe Karte als Tabelle und ein Satz, was zu tun ist.
**Das beantwortet vor dem ersten echten Besuch, ob die Kamera richtig hängt.**
Ohne diese Karte wartet man Tage auf eine leere Sammlung und weiß danach immer
noch nicht, ob keine Vögel kamen oder ob sie an der falschen Stelle im Bild
saßen.
Am Vogelhaus in Herzogenrath sah das so aus:
```
Fuss y x=0.18 x=0.32 x=0.46 x=0.60 x=0.74 x=0.88
0.55 OK 0.84 -- 0.44 OK 0.67 OK 0.67 OK 0.44 -- 0.44
0.65 OK 0.77 -- 0.44 OK 0.80 OK 0.56 -- 0.28 OK 0.62
0.75 OK 0.67 -- 0.38 OK 0.80 OK 0.72 -- 0.38 OK 0.56
0.85 OK 0.84 OK 0.67 OK 0.77 OK 0.84 OK 0.56 OK 0.72
0.95 -- 0.38 -- 0.16 -- 0.38 OK 0.62 -- 0.23 -- 0.20
```
Die unterste Reihe fällt fast geschlossen durch — und genau dort steht ein
Vogel, der auf dem Futterbrett landet, wenn die Kamera flach darüber hinweg
schaut. Derselbe Vogel in derselben Größe zehn Prozent höher im Bild: 0.84.
Daraus die Bauregel, die für jedes Futterhaus gilt: **die Landefläche gehört
in die untere Bildmitte, nicht an den unteren Bildrand.** Kamera so weit nach
unten neigen, bis unter dem Futter noch Bild übrig ist.
### Nachsehen, was angekommen ist
Nach den ersten Tagen ist die Frage nicht „läuft es", sondern „sehen wir Vögel,
und taugen die Bilder etwas". Dafür gibt es eine Auswertung der Rohdaten:
```bash
python -m detector report /var/lib/vogelalbum/rohdaten
python -m detector report /var/lib/vogelalbum/rohdaten --kontaktbogen bogen.jpg
```
Sie misst je Sichtung nach, statt zu schätzen: **Schärfe** (Varianz der
Laplace-Antwort, unter ~50 sichtbar verwackelt), **Fläche** des Vogels im Bild
und **Helligkeit**.
Die Fläche ist dabei der wichtigste Wert. Ein Vogel, der unter 3 % des Bildes
einnimmt, ist ein Fleck — daran ändert kein Schwellwert etwas, da muss die
Kamera näher heran oder enger eingestellt werden. Die Auswertung sagt das
ausdrücklich, wenn es auf mehr als ein Drittel der Sichtungen zutrifft.
Der Kontaktbogen legt alle Ausschnitte als ein Bild nebeneinander. Ob die
Qualität reicht, sieht man damit in zehn Sekunden statt in sechzig einzeln
geöffneten Dateien.