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:
22
app/main.py
22
app/main.py
@@ -6,7 +6,6 @@ from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
from fastapi import Depends, FastAPI, Form, HTTPException, Request, Response, UploadFile
|
||||
from fastapi.responses import FileResponse, JSONResponse, PlainTextResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -104,17 +103,16 @@ def logout(response: Response) -> dict:
|
||||
# ------------------------------------------------------------------------------ Scan
|
||||
|
||||
def _process_photo(raw: bytes) -> tuple:
|
||||
"""Rechenlastiger Teil: dekodieren, freistellen, als JPEG kodieren."""
|
||||
"""Rechenlastiger Teil: dekodieren, freistellen, als JPEG kodieren.
|
||||
|
||||
Die Zuschnitte werden einzeln kodiert und sofort freigegeben - bei zwanzig
|
||||
Karten liegen sonst rund 70 MB Rohbild gleichzeitig im Speicher.
|
||||
"""
|
||||
meta = imaging.read_metadata(raw)
|
||||
image = imaging.decode(raw)
|
||||
crops, fallback = segment.segment(image)
|
||||
encoded = [
|
||||
(
|
||||
imaging.encode_jpeg(crop),
|
||||
imaging.encode_jpeg(cv2.rotate(crop, cv2.ROTATE_180)),
|
||||
)
|
||||
for crop in crops
|
||||
]
|
||||
crops, fallback = segment.segment(imaging.decode(raw))
|
||||
encoded = []
|
||||
while crops:
|
||||
encoded.append(imaging.encode_jpeg(crops.pop(0)))
|
||||
return meta, encoded, fallback
|
||||
|
||||
|
||||
@@ -152,7 +150,7 @@ async def scan(
|
||||
created = now_iso()
|
||||
source = "einzel" if fallback else "stapel"
|
||||
cards = []
|
||||
for (upright, _), result in zip(encoded, results):
|
||||
for upright, result in zip(encoded, results):
|
||||
card_id = uuid.uuid4().hex
|
||||
image_file = f"{card_id}.jpg"
|
||||
(config.IMAGE_DIR / image_file).write_bytes(upright)
|
||||
|
||||
Reference in New Issue
Block a user