Files
2026-08-26 14:13:11 +08:00

47 lines
2.0 KiB
Python

"""Optional image metadata and computer-vision hints for image observations."""
from __future__ import annotations
import io
from typing import Any
def image_metadata(data: bytes) -> dict[str, Any]:
"""Read safe image metadata without changing the original upload."""
try:
from PIL import Image, ImageOps
with Image.open(io.BytesIO(data)) as image:
normalized = ImageOps.exif_transpose(image)
return {
"width": int(normalized.width),
"height": int(normalized.height),
"format": str(image.format or "").lower(),
"orientation": "landscape" if normalized.width >= normalized.height else "portrait",
"has_alpha": "A" in normalized.getbands(),
}
except Exception as error:
return {"error": f"image metadata unavailable: {type(error).__name__}"}
def cv_hints(data: bytes) -> dict[str, Any]:
"""Return conservative CV hints; OpenCV is intentionally optional."""
try:
import cv2 # type: ignore
import numpy as np # type: ignore
except Exception:
return {"available": False, "hints": []}
try:
image = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_GRAYSCALE)
if image is None:
return {"available": True, "hints": [], "error": "image decode failed"}
edges = cv2.Canny(image, 50, 150)
lines = cv2.HoughLinesP(edges, 1, 3.141592653589793 / 180, threshold=50, minLineLength=30, maxLineGap=8)
line_hints = []
for line in (lines[:32] if lines is not None else []):
x1, y1, x2, y2 = [int(value) for value in line[0]]
line_hints.append({"type": "line", "start_px": [x1, y1], "end_px": [x2, y2]})
return {"available": True, "hints": line_hints, "edge_pixels": int((edges > 0).sum())}
except Exception as error:
return {"available": True, "hints": [], "error": f"cv failed: {type(error).__name__}"}