QUELLCODE / PY

frame.py

src/hardware_box/vision/frame.py

1# OpenAss: nur privater, experimenteller Gebrauch; auf eigene Verantwortung.2# Niemals Unternehmensrechner, Produktivsysteme, Industrieanlagen oder3# sicherheitskritische Systeme anschließen oder bedienen.4# Haftungshinweis und gesetzliche Grenzen: ../../../DISCLAIMER.txt.5import base646import hashlib7import io8import time9from dataclasses import dataclass1011from PIL import Image12from hardware_box.hid.mapping import Geometry131415@dataclass(frozen=True)16class Frame:17    png: bytes18    geometry: Geometry19    captured_at: float20    sequence: int2122    @property23    def sha256(self) -> str:24        return hashlib.sha256(self.png).hexdigest()2526    @property27    def data_url(self) -> str:28        return "data:image/png;base64," + base64.b64encode(self.png).decode("ascii")293031def prepare(image: Image.Image, settings, sequence: int, captured_at=None) -> Frame:32    # Letterbox, never distort an aspect ratio. Mapping rejects the black bars.33    sw, sh = settings.screenshot_width, settings.screenshot_height34    ratio = min(sw / image.width, sh / image.height)35    cw, ch = max(2, round(image.width * ratio)), max(2, round(image.height * ratio))36    left, top = (sw - cw) // 2, (sh - ch) // 237    resized = image.convert("RGB").resize((cw, ch), Image.Resampling.LANCZOS)38    canvas = Image.new("RGB", (sw, sh))39    canvas.paste(resized, (left, top))40    output = io.BytesIO()41    canvas.save(output, format="PNG")42    geometry = Geometry(sw, sh, settings.target_width, settings.target_height,43                        content_rect=(left, top, cw, ch))44    return Frame(output.getvalue(), geometry, captured_at or time.monotonic(), sequence)