Compare commits

..

10 commits

Author SHA1 Message Date
watsonove
1ba943c76c
Update README.md 2026-02-10 18:36:15 +01:00
watsonove
af06acba60
Update README.md 2026-02-10 18:35:37 +01:00
watsonove
d0108d5290
Update README.md 2026-02-10 18:33:27 +01:00
watsonove
c20859f0d4
Update README.md 2026-02-10 18:30:36 +01:00
watsonove
678ea96e26
Add files via upload 2026-02-10 18:27:50 +01:00
watsonove
934f221e0d
Update renderer.py 2026-02-10 17:27:26 +01:00
watsonove
a733aca3eb
Update README.md 2026-02-10 17:13:13 +01:00
watsonove
345299afa9
Update app.py 2026-02-10 15:04:27 +01:00
watsonove
bb9e358c10
Add files via upload 2026-02-10 15:02:21 +01:00
watsonove
909aaa71c6
Update README.md 2026-02-09 15:39:25 +01:00
5 changed files with 73 additions and 9 deletions

View file

@ -8,6 +8,8 @@ Diese Anwendung ist eine interaktive Bildungssoftware für den Raspberry Pi 4/5
Wo Standard-Anwendungen die Objekterkennung oft als Blackbox behandeln, bricht diese Software die komplexen Prozesse auf und visualisiert sie verständlich. Der Nutzer wird Schritt für Schritt durch die Pipeline geführt beginnend bei der Rohdatenerfassung des IMX500-Sensors bis hin zur grafischen Darstellung der Erkennungsergebnisse. Um unterschiedlichen Wissensständen gerecht zu werden, bietet die UI zwei Modi ("Schüler:innen" und "Student:innen"), was sie zu einem idealen Werkzeug für Lehrende an Schulen, Universitäten sowie für Exponate auf Fachmessen macht.
Einen ausführlichen Projektbericht finden Sie unter: [Projektbericht](https://github.com/watsonove/IMX500-Object-Detection-UI/blob/main/M2_Projektbericht_Interaktive_Visualisierung_Objekterkennung.pdf).
## 🚀 Features
1. **Live-Objektdetektion:** Nutzt den Hardware-Beschleuniger des IMX500 Sensors.
@ -38,6 +40,13 @@ Wo Standard-Anwendungen die Objekterkennung oft als Blackbox behandeln, bricht d
* Darstellung von Bounding Boxes und Confidence Scores.
## 📹 Walkthrough
Durch Anklicken des Bildes können Sie einen Walkthrough der Anwendung abrufen.
[![Video-Titel](https://img.youtube.com/vi/wMuOgq-UNXw/0.jpg)](https://www.youtube.com/watch?v=wMuOgq-UNXw)
## 🛠 Hardware-Voraussetzungen
* **Raspberry Pi 4 oder 5**
@ -64,7 +73,7 @@ Stelle sicher, dass alle Projektdateien (`app.py`, `detector.py`, `steps.py`, Or
Alle Befehle werden im Terminal ausgeführt.
Das System benötigt Python 3, `picamera2` (vorinstalliert auf Bookworm) und `pygame`, sowie die IMX500 firmware `imx500`.
Das System benötigt `Python 3.11`, `picamera2` (vorinstalliert auf Bookworm) und `pygame`, sowie die IMX500 firmware `imx500`.
Zuerst sicher gehen, dass der Raspberry PI die aktuelle Software hat:
@ -104,6 +113,8 @@ Stelle sicher, dass die Ordnerstruktur korrekt ist (siehe unten "Projektstruktur
Starte die Anwendung über das Terminal. Du musst den Pfad zu deiner Model-Datei angeben (z. B. ein MobileNet oder EfficientDet Modell, das für den IMX500 kompiliert ist).
Hier mit mobilenetv2:
```bash
python3 app.py --model=/usr/share/imx500-models/imx500_network_ssd_mobilenetv2_fpnlite_320x320_pp.rpk
@ -119,7 +130,7 @@ Die Anwendung ist für Tastatur- und Mausbedienung optimiert.
| Taste / Aktion | Funktion |
| :----------------- | :---------------------------------------------------------------------- |
| **LEERTASTE** | **Freeze / Unfreeze:** Wechselt zwischen Live-Kamera und Analyse-Modus. |
| **ENTER** | **Weiter:** Geht zum nächsten Schritt oder bestätigt das "Gate". |
| **ENTER** | **Weiter:** Geht zum nächsten Schritt oder bestätigt das Gate. |
| **BACKSPACE** | **Zurück:** Geht zum vorherigen Schritt oder zurück zum Gate. |
| **Mausklick** | Bedienung der UI-Buttons (Sprache, Home, Audio, Level-Wahl). |
| **Mausbewegung** | Im "Schritt 1" (Analyse): Zeigt RGB-Werte unter dem Mauszeiger an. |

61
app.py
View file

@ -45,12 +45,16 @@ class App:
self.landing_bg_path = "assets/landingpagebg.jpg"
self.font_path = "assets/Kanit-Bold.ttf"
self.audio_dir = os.path.join(self.base_dir, "assets", "audio")
self.qr_path = os.path.join(self.base_dir, "assets", "qr.png")
# Background caches
# Background & Image caches
self._landing_bg_original = None
self._landing_bg_scaled = None
self._landing_bg_scaled_size = None
self._qr_code_img = None
self._load_landing_bg()
self._load_qr_code()
# Application State
self.running = True
@ -242,6 +246,13 @@ class App:
self._landing_bg_scaled = None
self._landing_bg_scaled_size = None
def _load_qr_code(self) -> None:
try:
self._qr_code_img = pygame.image.load(self.qr_path).convert_alpha()
except Exception as e:
print(f"Warning: QR Code not found at {self.qr_path} ({e})")
self._qr_code_img = None
def _get_landing_bg_scaled(self) -> pygame.Surface | None:
if self._landing_bg_original is None:
return None
@ -421,15 +432,59 @@ class App:
self.renderer.draw_card(self.screen, panel_rect, fill=self.theme.PANEL_2, outline=self.theme.LINE, radius=self.theme.RADIUS)
header_h = int(60 * self._scale())
self.renderer.draw_text(self.screen, self.font_header, "Ergebnisse", (panel_rect.x + 20, panel_rect.y + 20), self.theme.TEXT)
# 1. Definiere den Bereich für das Diagramm
chart_top_y = panel_rect.y + header_h
# Wir reservieren ca. 35% der Panel-Höhe für das Diagramm (oder fix 160px skalierbar)
chart_height = int(160 * self._scale())
content_rect = pygame.Rect(
panel_rect.x + 14,
panel_rect.y + header_h,
chart_top_y,
panel_rect.w - 28,
panel_rect.h - header_h - 14
chart_height
)
# Zeichne Diagramm
chart_font = self._load_font(self._fsize(26))
self.renderer.draw_bar_chart(self.screen, content_rect, self.snapshot.top3, self.args.threshold, chart_font, self.font_ui)
# Draw QR Code & Bias Info only in Student Step 7
if self.level == "STUDENT" and self.step == 7:
# Layout Cursor: Wo sind wir jetzt? (Unterhalb des Diagramms)
cursor_y = content_rect.bottom + int(30 * self._scale())
# 1. Info Text (Bias/Fehler)
bias_text = "Hinweis: KI ist nicht objektiv (Bias). Fehler sind möglich!" if self.lang == "DE" else "Note: AI is not objective (Bias). Errors are possible!"
txt_surf = self.font_small.render(bias_text, True, self.theme.WARN)
txt_rect = txt_surf.get_rect(centerx=panel_rect.centerx, top=cursor_y)
self.screen.blit(txt_surf, txt_rect)
# Update Cursor (Text-Höhe + Padding)
cursor_y = txt_rect.bottom + int(20 * self._scale())
# 2. QR Code (Nur wenn Platz ist)
if self._qr_code_img:
available_h = panel_rect.bottom - cursor_y - 20
if available_h > 80:
# Maximal verfügbare Breite/Höhe nutzen, aber nicht riesig werden
size = min(content_rect.w - 40, available_h)
size = min(size, int(300 * self._scale())) # Cap size
# Label above QR
lbl = self.font_small.render("Source Code (GitHub)", True, self.theme.TEXT_MUTED)
lbl_rect = lbl.get_rect(centerx=panel_rect.centerx, top=cursor_y)
self.screen.blit(lbl, lbl_rect)
cursor_y = lbl_rect.bottom + 5
# Draw QR Code
scaled_qr = pygame.transform.smoothscale(self._qr_code_img, (int(size), int(size)))
qr_rect = scaled_qr.get_rect(centerx=panel_rect.centerx, top=cursor_y)
self.screen.blit(scaled_qr, qr_rect)
def _draw_home_button(self) -> None:
pad = self.pad
w, h = self.home_btn_w, self.home_btn_h

BIN
assets/qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -101,7 +101,6 @@ class Renderer:
pygame.draw.line(surface, self.t.BG, (x, rect.y), (x, rect.bottom), 2)
# Draw Text Indicator above
# Hier wurde das Padding erhöht (rect.y - 12 statt -4)
label = f"{step} / {total_steps}"
txt = font_small.render(label, True, self.t.TEXT_MUTED)
txt_rect = txt.get_rect(centerx=rect.centerx, bottom=rect.y - 12)
@ -139,7 +138,6 @@ class Renderer:
pygame.draw.line(surface, self.t.ACCENT, (thr_x, chart_y - 8), (thr_x, chart_y + chart_h + 8), 2)
if not top3:
# self.draw_text(surface, body_font, "...", (chart_x, chart_y), self.t.TEXT_MUTED)
return
for i, (lab, conf) in enumerate(top3[:3]):
@ -153,6 +151,6 @@ class Renderer:
bw = int(chart_w * clamp01(conf))
pygame.draw.rect(surface, bar_col, pygame.Rect(chart_x, y, bw, row_h), border_radius=10)
# Label Text
lbl_surf = body_font.render(f"{lab} {int(conf*100)}%", True, self.t.TEXT)
# Label Text - JETZT IN SCHWARZ (0, 0, 0)
lbl_surf = body_font.render(f"{lab} {int(conf*100)}%", True, (0, 0, 0))
surface.blit(lbl_surf, (chart_x + 8, y + (row_h - lbl_surf.get_height()) // 2))