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:
Lucas Orth
2026-09-06 12:22:37 +02:00
parent 30db433c9a
commit bc1981f1bd
6 changed files with 64 additions and 27 deletions

View File

@@ -11,7 +11,8 @@ import numpy as np
CARD_RATIO = 85.6 / 54.0
RATIO_MIN, RATIO_MAX = 1.25, 2.05
OUT_WIDTH = 1400
OUT_WIDTH = 1400 # Obergrenze; kleinere Karten werden nicht hochskaliert
MIN_WIDTH = 600
OUT_HEIGHT = round(OUT_WIDTH / CARD_RATIO)
DETECT_EDGE = 1600 # Aufloesung, auf der gesucht wird
@@ -129,19 +130,32 @@ def segment(image: np.ndarray) -> tuple[list[np.ndarray], bool]:
crops = []
for rect in _reading_order(rects):
quad = _order_quad(cv2.boxPoints(rect)) / scale # zurueck auf volle Aufloesung
width, height = _target_size(quad)
target = np.array(
[[0, 0], [OUT_WIDTH, 0], [OUT_WIDTH, OUT_HEIGHT], [0, OUT_HEIGHT]],
dtype="float32",
[[0, 0], [width, 0], [width, height], [0, height]], dtype="float32"
)
matrix = cv2.getPerspectiveTransform(quad, target)
crops.append(
cv2.warpPerspective(
image, matrix, (OUT_WIDTH, OUT_HEIGHT), flags=cv2.INTER_CUBIC
)
cv2.warpPerspective(image, matrix, (width, height), flags=cv2.INTER_CUBIC)
)
return crops, False
def _target_size(quad: np.ndarray) -> tuple:
"""Zuschnitt so gross wie die Vorlage, hoechstens OUT_WIDTH.
Auf einem Stapelfoto ist eine Karte nur ein paar hundert Pixel breit.
Sie auf 1400 hochzurechnen erzeugt vier Mal so viele Pixel ohne ein
Quentchen mehr Information - und kostet Speicher, Bildgroesse und
Bildtokens beim Modellaufruf.
"""
long_edge = max(
np.linalg.norm(quad[1] - quad[0]), np.linalg.norm(quad[2] - quad[3])
)
width = int(min(OUT_WIDTH, max(MIN_WIDTH, round(long_edge))))
return width, round(width / CARD_RATIO)
def _fit_whole(image: np.ndarray) -> np.ndarray:
"""Ohne Fund: das ganze Bild als eine Karte behandeln."""
height, width = image.shape[:2]