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.
This commit is contained in:
commit
c11fe9ab04
189 files changed
+29806
No files matched your search
@@ -0,0 +1,149 @@
|
||||
{% extends "dex/base.html" %}
|
||||
{% load dex_extras i18n %}
|
||||
|
||||
{% block title %}{{ species.name }} — Vogelalbum{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{# Die Typfarbe faerbt die ganze Seite: Hero, Balken, Initiale, Diagramm. #}
|
||||
<div style="{{ species.typ_vars }}">
|
||||
|
||||
<div class="blaettern">
|
||||
{% if vorher %}<a href="{% url 'species' vorher.slug %}">← {{ vorher.dex_nr }} {{ vorher.name }}</a>{% else %}<span></span>{% endif %}
|
||||
{% if nachher %}<a href="{% url 'species' nachher.slug %}">{{ nachher.dex_nr }} {{ nachher.name }} →</a>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="species-hero">
|
||||
<div class="hero-grid">
|
||||
<div>
|
||||
<div class="hero-photo">
|
||||
{% if entry and entry.best_sighting and entry.best_sighting.image %}
|
||||
<img src="{{ entry.best_sighting.image.url }}" alt="{{ species.name }}">
|
||||
{% elif species.reference_image %}
|
||||
<img src="{{ species.reference_image.url }}" alt="{{ species.german }}"
|
||||
style="filter: saturate(.7) brightness(.72)">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="sub" style="margin-top:9px">
|
||||
{% if entry and entry.best_sighting and entry.best_sighting.image %}
|
||||
{% trans "Bestes eigenes Foto" %} · {{ entry.best_sighting.started_at|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% elif species.reference_image %}
|
||||
{% trans "Referenzfoto — diese Art war noch nicht vor unserer Kamera." %}
|
||||
{% if species.reference_credit %}
|
||||
{% trans "Foto" %}: <a href="{{ species.reference_source }}" rel="nofollow noopener" target="_blank"
|
||||
style="text-decoration:underline">{{ species.reference_credit }}</a>{% if species.reference_license %}, {{ species.reference_license }}{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dexnr">{{ species.dex_nr }}</div>
|
||||
<h1>{{ species.name }}</h1>
|
||||
{% if species.kategorie_lokal %}<div class="kategorie">{{ species.kategorie_lokal }}</div>{% endif %}
|
||||
<div class="sci">{{ species.scientific }}{% if species.zweitname %} · {{ species.zweitname }}{% endif %}</div>
|
||||
|
||||
<div class="badge-row">
|
||||
{% if species.typ %}<span class="pill">{{ species.typ_label }}</span>{% endif %}
|
||||
<span class="pill rar" style="background:{{ species.rarity_color }}">{{ species.rarity_label }}</span>
|
||||
{% if species.zug %}<span class="pill ghost">{{ species.zug_label }}</span>{% endif %}
|
||||
<span class="pill ghost">{{ species.xp }} XP</span>
|
||||
{% if entry %}<span class="pill ghost">{% blocktrans count n=entry.count %}{{ n }}× seen{% plural %}{{ n }}× seen{% endblocktrans %}</span>{% endif %}
|
||||
</div>
|
||||
|
||||
{% if species.balken %}
|
||||
<div class="werte">
|
||||
{% for b in species.balken %}
|
||||
<div class="wert">
|
||||
<div class="k">{{ b.label }}</div>
|
||||
<div class="track"><div class="fill" style="width:{{ b.pct }}%"></div></div>
|
||||
<div class="v">{{ b.wert }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if species.absaetze %}
|
||||
<h2>{% trans "Steckbrief" %}</h2>
|
||||
<div class="prose">
|
||||
{% for p in species.absaetze %}<p>{{ p }}</p>{% endfor %}
|
||||
</div>
|
||||
{% elif species.fact %}
|
||||
<h2>{% trans "Steckbrief" %}</h2>
|
||||
<div class="prose"><p>{{ species.fact }}</p></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="fakten">
|
||||
{% if species.lebensraum_lokal %}
|
||||
<div class="fakt"><div class="k">{% trans "Lebensraum" %}</div><div class="v">{{ species.lebensraum_lokal }}</div></div>
|
||||
{% endif %}
|
||||
{% if species.nahrung_lokal %}
|
||||
<div class="fakt"><div class="k">{% trans "Nahrung" %}</div><div class="v">{{ species.nahrung_lokal }}</div></div>
|
||||
{% endif %}
|
||||
{% if species.stimme_lokal %}
|
||||
<div class="fakt"><div class="k">{% trans "Stimme" %}</div><div class="v">{{ species.stimme_lokal }}</div></div>
|
||||
{% endif %}
|
||||
{% if species.zug %}
|
||||
<div class="fakt"><div class="k">{% trans "Zugverhalten" %}</div><div class="v">{{ species.zug_label }}</div></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if aehnliche %}
|
||||
<h2>{% trans "Verwechslungsgefahr" %} <span class="count">{% trans "Arten, die man nicht auf Anhieb unterscheidet" %}</span></h2>
|
||||
<div class="verwechslung">
|
||||
{% for a in aehnliche %}
|
||||
<a class="vw" href="{% url 'species' a.slug %}">
|
||||
{% if a.reference_image %}<img src="{{ a.reference_image.url }}" alt="" loading="lazy">{% endif %}
|
||||
<span class="n">{{ a.name }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if entry %}
|
||||
<h2>{% blocktrans with n=species.name %}Wann ist {{ n }} da?{% endblocktrans %} <span class="count">{% trans "Besuche über den Tag" %}</span></h2>
|
||||
<div class="hours">
|
||||
{% for h in hours %}<div style="height:{{ h.pct }}%" title="{{ h.h }} Uhr: {{ h.n }}"></div>{% endfor %}
|
||||
</div>
|
||||
<div class="hours-axis"><span>{% trans "0 Uhr" %}</span><span>6</span><span>12</span><span>18</span><span>23</span></div>
|
||||
{% endif %}
|
||||
|
||||
{% if sightings %}
|
||||
<h2>{% trans "Alle Besuche" %} <span class="count">{% blocktrans with n=entry.count %}{{ n }} insgesamt{% endblocktrans %}</span></h2>
|
||||
<div class="rows">
|
||||
{% for s in sightings %}
|
||||
<div class="row">
|
||||
{% if s.kachel_url %}
|
||||
<img class="shot" src="{{ s.kachel_url }}" alt="" loading="lazy"
|
||||
{% if s.image %}data-gross="{{ s.image.url }}"
|
||||
data-beschriftung="{{ species.name }} · {{ s.started_at|date:"SHORT_DATETIME_FORMAT" }}{% if s.camera %} · {{ s.camera.name }}{% endif %}"
|
||||
style="cursor:zoom-in"{% endif %}>
|
||||
{% else %}<div class="shot"></div>{% endif %}
|
||||
<div>
|
||||
<div class="who">
|
||||
{{ s.started_at|date:"DATETIME_FORMAT" }}
|
||||
{% if s.is_first_of_species %}<span class="tag-new">{% trans "Erstfang" %}</span>{% endif %}
|
||||
</div>
|
||||
<div class="when">
|
||||
{{ s.duration_s|floatformat:0 }}s · {% blocktrans with a=s.frames_with_bird b=s.frames_total %}{{ a }} von {{ b }} Bildern mit Vogel{% endblocktrans %}
|
||||
{% if s.camera %} · {{ s.camera.name }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="conf">{{ s.confidence|prozent }}%</div>
|
||||
{% if s.xp_awarded %}<div class="xp">+{{ s.xp_awarded }} XP</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif not entry %}
|
||||
<div class="empty" style="margin-top:24px">
|
||||
<strong style="color:var(--text); font-size:17px">{% trans "Noch nicht gesammelt." %}</strong><br>
|
||||
{% blocktrans with n=species.name %}Sobald {{ n }} vor der Kamera landet, wird dieser Eintrag freigeschaltet — mit eurem eigenen Foto.{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user