54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
from dataclasses import dataclass
|
|
|
|
from PIL import Image, ImageOps
|
|
|
|
from app.config import get_settings
|
|
|
|
|
|
@dataclass
|
|
class PreparedImage:
|
|
jpeg_bytes: bytes
|
|
width: int
|
|
height: int
|
|
original_bytes: int
|
|
compressed_bytes: int
|
|
mime: str = "image/jpeg"
|
|
|
|
def to_meta(self) -> dict[str, int | str]:
|
|
return {
|
|
"mime": self.mime,
|
|
"width": self.width,
|
|
"height": self.height,
|
|
"original_bytes": self.original_bytes,
|
|
"compressed_bytes": self.compressed_bytes,
|
|
}
|
|
|
|
|
|
def prepare_image(raw_bytes: bytes) -> PreparedImage:
|
|
settings = get_settings()
|
|
max_edge = max(256, int(settings.vision_max_edge_px))
|
|
quality = max(40, min(95, int(settings.vision_jpeg_quality)))
|
|
|
|
with Image.open(io.BytesIO(raw_bytes)) as img:
|
|
img = ImageOps.exif_transpose(img)
|
|
img = img.convert("RGB")
|
|
width, height = img.size
|
|
if max(width, height) > max_edge:
|
|
img.thumbnail((max_edge, max_edge), Image.Resampling.LANCZOS)
|
|
width, height = img.size
|
|
|
|
buffer = io.BytesIO()
|
|
img.save(buffer, format="JPEG", quality=quality, optimize=True)
|
|
jpeg_bytes = buffer.getvalue()
|
|
|
|
return PreparedImage(
|
|
jpeg_bytes=jpeg_bytes,
|
|
width=width,
|
|
height=height,
|
|
original_bytes=len(raw_bytes),
|
|
compressed_bytes=len(jpeg_bytes),
|
|
)
|