Selbst gehostete PWA, die Visitenkarten von einem Foto freistellt, ausliest und als Kontakt bereitstellt. - Stapelscan: OpenCV findet die Kartenrechtecke über mehrere Binärmasken, entzerrt sie perspektivisch und schneidet sie einzeln aus. Ohne Fund gilt das ganze Foto als eine Karte. - Extraktion: ein Aufruf je Zuschnitt an das Vision-Modell mit JSON-Schema. Kein vorgeschaltetes OCR - das würde Layout und Schriftgrößen wegwerfen, aus denen die Feldzuordnung entsteht. - Metadaten: Aufnahmezeit und GPS aus den EXIF-Daten des Fotos, Ortsname über Nominatim, Browserstandort nur als Rückfallebene. - Übersicht mit Volltextsuche und Filtern, Detailansicht mit Korrekturmaske. - Notizfeld je Karte, Erinnerungen per Mail inklusive Nachholen verpasster Termine nach einem Neustart. - vCard 3.0 einzeln und als Sammeldatei, Karte gilt danach als exportiert. - Anmeldung über ein Passwort, Sitzung als signiertes Cookie. - Deployment per Tag-Push nach den Konventionen in DEPLOY.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
1.2 KiB
Python
46 lines
1.2 KiB
Python
import numpy as np
|
|
|
|
from app import segment
|
|
from tests.helpers import photo_with_cards
|
|
|
|
|
|
def test_findet_alle_karten_im_stapel():
|
|
photo = photo_with_cards(count=6)
|
|
crops, fallback = segment.segment(photo)
|
|
|
|
assert fallback is False
|
|
assert len(crops) == 6
|
|
|
|
|
|
def test_zuschnitte_haben_kartenformat():
|
|
crops, _ = segment.segment(photo_with_cards(count=4))
|
|
|
|
for crop in crops:
|
|
height, width = crop.shape[:2]
|
|
assert (width, height) == (segment.OUT_WIDTH, segment.OUT_HEIGHT)
|
|
|
|
|
|
def test_zuschnitt_enthaelt_die_karte_und_nicht_den_tisch():
|
|
crops, _ = segment.segment(photo_with_cards(count=4))
|
|
|
|
for crop in crops:
|
|
# Die Karte ist weiss, der Untergrund dunkel. Ein Zuschnitt, der
|
|
# danebenliegt, waere im Mittel dunkel.
|
|
assert crop.mean() > 170
|
|
|
|
|
|
def test_grosser_stapel():
|
|
crops, fallback = segment.segment(photo_with_cards(count=12))
|
|
|
|
assert fallback is False
|
|
assert len(crops) == 12
|
|
|
|
|
|
def test_einzelne_formatfuellende_karte_faellt_auf_gesamtbild_zurueck():
|
|
# Randlose Karte: es gibt keine Kante zum Untergrund, also keinen Fund.
|
|
card = np.full((1080, 1712, 3), 245, dtype=np.uint8)
|
|
crops, fallback = segment.segment(card)
|
|
|
|
assert fallback is True
|
|
assert len(crops) == 1
|