Spaces:
Sleeping
Sleeping
File size: 1,891 Bytes
0cdb4e8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | """OpenCV-based preprocessing for uploaded room photos.
Validates decodability, applies EXIF orientation, resizes so the longest edge is
<= MAX_EDGE_PX (aspect preserved), and writes a normalized copy to disk.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import cv2
import numpy as np
from PIL import Image, ImageOps
from ..config import settings
@dataclass
class PreprocessResult:
path: Path # normalized image path on disk
rel_path: str # path relative to the server root (e.g. "static/uploads/x.jpg")
width: int
height: int
def _load_oriented(raw_path: Path) -> Image.Image:
img = Image.open(raw_path)
img = ImageOps.exif_transpose(img) # honor EXIF orientation
return img.convert("RGB")
def preprocess_image(
raw_path: Path, out_path: Path, max_edge: int | None = None
) -> PreprocessResult:
"""Validate, auto-orient, resize and save a normalized image.
Raises ValueError if the file cannot be decoded as an image.
"""
max_edge = max_edge or settings.MAX_EDGE_PX
try:
pil = _load_oriented(raw_path)
except Exception as exc: # noqa: BLE001
raise ValueError(f"Could not decode image: {raw_path.name}") from exc
arr = cv2.cvtColor(np.array(pil), cv2.COLOR_RGB2BGR)
if arr is None or arr.size == 0:
raise ValueError(f"Empty/invalid image: {raw_path.name}")
h, w = arr.shape[:2]
scale = min(1.0, max_edge / float(max(h, w)))
if scale < 1.0:
w, h = int(round(w * scale)), int(round(h * scale))
arr = cv2.resize(arr, (w, h), interpolation=cv2.INTER_AREA)
out_path.parent.mkdir(parents=True, exist_ok=True)
cv2.imwrite(str(out_path), arr)
rel = out_path.relative_to(settings.STATIC_DIR).as_posix()
return PreprocessResult(path=out_path, rel_path=f"static/{rel}", width=w, height=h)
|