Scan-Pfad entschlacken, Container-Limits setzen
Drei Änderungen, die den Spitzenverbrauch eines Stapelscans senken: - Zuschnitte werden nicht mehr auf 1400 px hochgerechnet. Auf einem Stapelfoto ist eine Karte nur ein paar hundert Pixel breit; sie aufzublasen erzeugt vier Mal so viele Pixel ohne mehr Information und kostet zusätzlich Bildtokens beim Modellaufruf. OUT_WIDTH ist jetzt eine Obergrenze. - Die um 180 Grad gedrehte Fassung entsteht erst, wenn ein Ergebnis leer bleibt, statt für jede Karte auf Vorrat. Das halbiert die JPEG-Kodierungen im Normalfall. - Zuschnitte werden einzeln kodiert und sofort freigegeben, statt gesammelt im Speicher zu liegen. Dazu mem_limit und cpus für den App-Container: ein Ausreißer soll den Container treffen, nicht den Host, auf dem auch Jitsi und MySQL laufen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import logging
|
||||
|
||||
import anthropic
|
||||
|
||||
from . import config
|
||||
from . import config, imaging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -123,11 +123,12 @@ async def _ask(image_jpeg: bytes) -> dict:
|
||||
return json.loads(text)
|
||||
|
||||
|
||||
async def extract(image_jpeg: bytes, rotated_jpeg: bytes = None) -> dict:
|
||||
async def extract(image_jpeg: bytes) -> dict:
|
||||
"""Ein Kartenbild auslesen.
|
||||
|
||||
Bleibt das Ergebnis leer, wird einmal um 180 Grad gedreht nachgefasst:
|
||||
wie herum die Karte auf dem Tisch lag, ist geometrisch nicht bestimmbar.
|
||||
Die gedrehte Fassung entsteht erst hier, nicht auf Vorrat fuer jede Karte.
|
||||
"""
|
||||
if not available():
|
||||
return _empty("Kein ANTHROPIC_API_KEY gesetzt")
|
||||
@@ -137,9 +138,10 @@ async def extract(image_jpeg: bytes, rotated_jpeg: bytes = None) -> dict:
|
||||
log.exception("Extraktion fehlgeschlagen")
|
||||
return _empty(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
if not _has_content(result) and rotated_jpeg is not None:
|
||||
if not _has_content(result):
|
||||
try:
|
||||
retry = await _ask(rotated_jpeg)
|
||||
rotated = await asyncio.to_thread(imaging.rotate_jpeg_180, image_jpeg)
|
||||
retry = await _ask(rotated)
|
||||
if _has_content(retry):
|
||||
retry["_rotated"] = True
|
||||
return retry
|
||||
@@ -148,12 +150,12 @@ async def extract(image_jpeg: bytes, rotated_jpeg: bytes = None) -> dict:
|
||||
return result
|
||||
|
||||
|
||||
async def extract_all(pairs: list) -> list:
|
||||
async def extract_all(images: list) -> list:
|
||||
"""Alle Karten eines Fotos parallel auslesen."""
|
||||
limit = asyncio.Semaphore(max(1, config.EXTRACT_CONCURRENCY))
|
||||
|
||||
async def one(upright: bytes, rotated: bytes) -> dict:
|
||||
async def one(image_jpeg: bytes) -> dict:
|
||||
async with limit:
|
||||
return await extract(upright, rotated)
|
||||
return await extract(image_jpeg)
|
||||
|
||||
return await asyncio.gather(*(one(u, r) for u, r in pairs))
|
||||
return await asyncio.gather(*(one(image) for image in images))
|
||||
|
||||
Reference in New Issue
Block a user