Støtte for flere bibs per bilde, EXIF-metadata og zoom i gjennomgang
Build & Deploy / build-and-deploy (push) Successful in 46s
Build & Deploy / build-and-deploy (push) Successful in 46s
- OCR: ny read_all_bibs() returnerer alle unike startnumre (≥2 sifre) per bilde
- Ingest: oppretter én passering per bib (ikke bare beste), ingen bib → needs_review
- image_tagger.py: skriv/les bib-metadata som JSON i EXIF UserComment (piexif)
- Ingest + resolve: tagger bildefilen med bibs automatisk og ved manuell bekreftelse
- API: POST /api/passages/{id}/reanalyze — re-kjør OCR på eksisterende bilde
- API: POST /api/passages/{id}/resolve oppdaterer nå EXIF med bekreftet bib
- races: ny kolonne bib_filter_enabled (med automatisk migrering) + per-løp toggle
- ReviewPage: Re-analyser-knapp og klikk-for-zoom med scroll/drag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Skriv og les bib-metadata i bildefiler via EXIF UserComment (JSON).
|
||||
|
||||
Bruker piexif + Pillow som allerede er i requirements.txt.
|
||||
Metadata lagres i Exif.UserComment som:
|
||||
b"ASCII\x00\x00\x00" + JSON-bytes
|
||||
|
||||
Eksempel-innhold:
|
||||
{"bibs": ["42", "87"], "station": "finish", "confidence": 0.93, "confirmed": true}
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import piexif
|
||||
from PIL import Image
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_ASCII_PREFIX = b"ASCII\x00\x00\x00"
|
||||
|
||||
|
||||
def write_bib_tags(
|
||||
image_path: Path,
|
||||
bibs: list[str],
|
||||
*,
|
||||
station: Optional[str] = None,
|
||||
race_id: Optional[str] = None,
|
||||
confidence: Optional[float] = None,
|
||||
confirmed: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Skriv startnummer-metadata til bildefil som EXIF UserComment (JSON).
|
||||
Bevarer all eksisterende EXIF. Feiler stille — krasjer ikke ingest-prosessen.
|
||||
"""
|
||||
data: dict = {"bibs": bibs}
|
||||
if station:
|
||||
data["station"] = station
|
||||
if race_id:
|
||||
data["race_id"] = race_id
|
||||
if confidence is not None:
|
||||
data["confidence"] = round(confidence, 4)
|
||||
if confirmed:
|
||||
data["confirmed"] = True
|
||||
|
||||
json_bytes = json.dumps(data, ensure_ascii=True, separators=(",", ":")).encode("ascii")
|
||||
comment_bytes = _ASCII_PREFIX + json_bytes
|
||||
|
||||
try:
|
||||
img = Image.open(image_path)
|
||||
raw_exif = img.info.get("exif")
|
||||
if raw_exif:
|
||||
exif_dict = piexif.load(raw_exif)
|
||||
else:
|
||||
exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "Interop": {}, "1st": {}}
|
||||
|
||||
exif_dict.setdefault("Exif", {})[piexif.ExifIFD.UserComment] = comment_bytes
|
||||
new_exif = piexif.dump(exif_dict)
|
||||
|
||||
# Lagre til midlertidig fil, rename for atomisk skriving
|
||||
tmp = image_path.with_suffix(".tmp" + image_path.suffix)
|
||||
img.save(tmp, exif=new_exif)
|
||||
tmp.replace(image_path)
|
||||
|
||||
logger.debug("EXIF-tags skrevet til %s: bibs=%s", image_path.name, bibs)
|
||||
except Exception as e:
|
||||
logger.warning("Kunne ikke skrive EXIF-tags til %s: %s", image_path, e)
|
||||
|
||||
|
||||
def read_bib_tags(image_path: Path) -> Optional[dict]:
|
||||
"""
|
||||
Les startnummer-metadata fra EXIF UserComment.
|
||||
Returnerer dict (med nøkkel 'bibs') eller None hvis ikke satt.
|
||||
"""
|
||||
try:
|
||||
img = Image.open(image_path)
|
||||
raw_exif = img.info.get("exif")
|
||||
if not raw_exif:
|
||||
return None
|
||||
exif_dict = piexif.load(raw_exif)
|
||||
comment = exif_dict.get("Exif", {}).get(piexif.ExifIFD.UserComment)
|
||||
if not comment or not comment.startswith(_ASCII_PREFIX):
|
||||
return None
|
||||
json_str = comment[len(_ASCII_PREFIX):].decode("ascii", errors="replace")
|
||||
return json.loads(json_str)
|
||||
except Exception:
|
||||
return None
|
||||
Reference in New Issue
Block a user