Agri-AI / backend /utils /preprocessing.py
Akash-Dragon's picture
fix(backend): add resilient fallbacks for disease, soil, and image preprocessing in serverless environment
254b8d8
Raw
History Blame Contribute Delete
1.85 kB
"""Image preprocessing pipeline for disease and soil CNN models."""
import io
import hashlib
from PIL import Image
import numpy as np
try:
import torch
from torchvision import transforms
inference_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
])
except ImportError:
torch = None
inference_transform = None
def preprocess_image(image_bytes: bytes) -> tuple:
"""
Validate, preprocess and return a (1, 3, 224, 224) float32 tensor/ndarray
plus a deterministic cache key (SHA-256 of raw bytes).
Raises ValueError for invalid / non-image data.
"""
# --- validate ---
try:
img = Image.open(io.BytesIO(image_bytes))
img.verify() # catches truncated files
img = Image.open(io.BytesIO(image_bytes)) # reopen after verify
except Exception as exc:
raise ValueError(f"Invalid image file: {exc}") from exc
# --- convert to RGB (handles RGBA, palette, grayscale) ---
img = img.convert("RGB")
# --- reject suspiciously small images ---
w, h = img.size
if w < 64 or h < 64:
raise ValueError(f"Image too small ({w}×{h}). Minimum 64×64 px required.")
# --- transform ---
if torch is not None and inference_transform is not None:
tensor = inference_transform(img).unsqueeze(0) # (1, 3, 224, 224)
else:
resized = img.resize((224, 224))
arr = np.array(resized, dtype=np.float32) / 255.0
tensor = np.transpose(arr, (2, 0, 1))[np.newaxis, ...]
# --- cache key ---
cache_key = hashlib.sha256(image_bytes).hexdigest()
return tensor, cache_key
def bytes_to_pil(image_bytes: bytes) -> Image.Image:
"""Return a PIL Image from raw bytes, converted to RGB."""
return Image.open(io.BytesIO(image_bytes)).convert("RGB")