image-processor-pro / converter.py
divakar-rajodiya
Image Processor Pro web app
6d8fa62
Raw
History Blame Contribute Delete
2.01 kB
from __future__ import annotations
import io
import logging
import time
from dataclasses import dataclass
from typing import Optional
import pillow_avif # noqa: F401 registers AVIF support with Pillow
from PIL import Image, UnidentifiedImageError
from config import Config
logger = logging.getLogger("image_processor")
class ConversionError(Exception):
"""Raised when an image cannot be decoded or converted."""
@dataclass
class ConversionResult:
image: Image.Image
source_format: str
elapsed_seconds: float
class Converter:
"""Decode arbitrary input bytes and normalize to an RGB Pillow image."""
def __init__(self, config: Config) -> None:
self.config = config
def convert(self, raw_bytes: bytes) -> ConversionResult:
start = time.perf_counter()
try:
with Image.open(io.BytesIO(raw_bytes)) as img:
img.load()
source_format = (img.format or "UNKNOWN").lower()
rgb_image = self._flatten_to_rgb(img)
except UnidentifiedImageError as exc:
raise ConversionError(f"Could not identify image: {exc}") from exc
except OSError as exc:
raise ConversionError(f"Corrupted image data: {exc}") from exc
elapsed = time.perf_counter() - start
return ConversionResult(
image=rgb_image,
source_format=source_format,
elapsed_seconds=elapsed,
)
def _flatten_to_rgb(self, img: Image.Image) -> Image.Image:
"""Convert any image to RGB, flattening transparency over a solid background."""
if img.mode == "RGB":
return img.copy()
if img.mode in ("RGBA", "LA") or (img.mode == "P" and "transparency" in img.info):
rgba = img.convert("RGBA")
background = Image.new("RGB", rgba.size, self.config.background_color)
background.paste(rgba, mask=rgba.split()[-1])
return background
return img.convert("RGB")