HEIC annehmen, und "Vogel ohne Art" nicht als "kein Vogel" melden

ZWEI FEHLER, BEIDE HEUTE ENTSTANDEN, BEIDE BEIM BENUTZEN GEFUNDEN.

1. 🔴 HEIC. Minuten nach dem Ausrollen der Bildpruefung wies die Instanz
   jedes Foto von Christians iPhone mit 400 ab. Die Pruefung war richtig --
   sie kannte nur das Format nicht, das die Haelfte aller Telefone erzeugt.
   Eine App fuer iOS, die kein HEIC annimmt, nimmt nichts an.

   pillow-heif in requirements-web.txt, Opener registriert, AVIF gleich mit.
   Fehlt das Paket, sagt die Meldung das jetzt ausdruecklich -- sonst sucht
   man den Fehler beim Telefon, und er liegt an einem fehlenden Paket auf
   dem Server.

2. 🔴 "VOGEL JA, ART NEIN" KAM ALS "KEIN VOGEL" AN. Pipeline.run gibt in
   drei Faellen kein Ergebnis zurueck: nichts gefunden, Beinahe-Treffer, und
   -- der interessante -- ein sicher erkannter Vogel, dessen Art keine
   heimische ist. Die Box unterscheidet die Faelle; die Warteschlange hat sie
   weggeworfen und 0.0 gemeldet.

   Folge: Ein Foto mit einem Vogel, den der Detektor mit 93 % gefunden hatte,
   kam in der App als "Kein Vogel erkannt. Auf dem Foto war nichts zu finden"
   an. Christian hat daraufhin zugeschnitten, wieder und wieder -- gegen ein
   Problem, das nie der Zuschnitt war.

   🔑 EINE FALSCHE AUSKUNFT IST TEURER ALS GAR KEINE. Sie schickt jemanden
   mit voller Ueberzeugung in die falsche Richtung.

   Jetzt traegt mean_bird_score den Wert der Box, und frames_with_bird haengt
   am Vogel statt an der Art.

7 neue Tests. 235 Web + 131 Detector gruen.
This commit is contained in:
Artemis committed 2026-10-14 18:09:06 +00:00
1 parent dbec78aa7a
commit f7184d193c
6 files changed
+161 -6

No files matched your search

+63
View File
@@ -579,3 +579,66 @@ class AbzeichenTests(AppTestBasis):
self.assertLessEqual(z["ist"], z["ziel"])
class VogelOhneArtTests(AppTestBasis):
"""🔴 „Vogel ja, Art nein" darf nicht als „kein Vogel" ankommen.
Am 14.10.2026 erkannte der Detektor auf einem Handyfoto mit 93 % einen
Vogel, konnte die Art aber nicht bestimmen (Gegenlicht, Silhouette). In
der App stand „Kein Vogel erkannt. Auf dem Foto war nichts zu finden" —
und Christian hat daraufhin zugeschnitten, gegen ein Problem, das nie der
Zuschnitt war. Eine falsche Auskunft ist teurer als gar keine.
"""
def setUp(self):
super().setUp()
self.kamera = make_camera(token="kamera-geheim", mobil=True)
def _wartendes_foto(self):
jetzt = timezone.now()
s = Sighting.objects.create(
camera=self.kamera, started_at=jetzt, ended_at=jetzt,
frames_total=1, wartet_auf_bestimmung=True,
)
return s
def melde(self, s, nutzlast):
return self.client.post(
f"/api/offen/{s.id}/",
{"data": json.dumps(nutzlast)},
HTTP_AUTHORIZATION="Bearer kamera-geheim",
)
def test_vogel_ohne_art_behaelt_seinen_vogelwert(self):
s = self._wartendes_foto()
resp = self.melde(s, {"species": None, "confidence": 0.0,
"mean_bird_score": 0.93})
self.assertEqual(resp.status_code, 200, resp.content)
daten = self.json(f"/api/app/sichtungen/{s.id}/")
self.assertFalse(daten["wartet"])
self.assertIsNone(daten["art"])
# Das ist der Unterschied, an dem die App ihren Text waehlt.
self.assertAlmostEqual(daten["vogelwert"], 0.93, places=3)
def test_wirklich_kein_vogel_bleibt_bei_null(self):
s = self._wartendes_foto()
self.melde(s, {"species": None, "confidence": 0.0, "mean_bird_score": 0.0})
daten = self.json(f"/api/app/sichtungen/{s.id}/")
self.assertIsNone(daten["art"])
self.assertEqual(daten["vogelwert"], 0.0)
def test_vogel_ohne_art_zaehlt_als_bild_mit_vogel(self):
"""``frames_with_bird`` hing an der ART statt am Vogel."""
s = self._wartendes_foto()
self.melde(s, {"species": None, "mean_bird_score": 0.93})
s.refresh_from_db()
self.assertEqual(s.frames_with_bird, 1)
def test_ohne_vogel_kein_bild_mit_vogel(self):
s = self._wartendes_foto()
self.melde(s, {"species": None, "mean_bird_score": 0.0})
s.refresh_from_db()
self.assertEqual(s.frames_with_bird, 0)