from __future__ import annotations import io from typing import Any, Dict, List import numpy as np from PIL import Image import tensorflow as tf LABELS: List[str] = [ "Heart", "Oblong", "Oval", "Round", "Square", ] TARGET_SIZE = 244 def _load_image(image_bytes: bytes) -> Image.Image: image = Image.open(io.BytesIO(image_bytes)) if image.mode != "RGB": image = image.convert("RGB") return image def _ensure_three_channels(array: np.ndarray) -> np.ndarray: if array.ndim == 2: array = np.stack([array] * 3, axis=-1) elif array.ndim == 3: if array.shape[-1] == 1: array = np.repeat(array, 3, axis=-1) elif array.shape[-1] > 3: array = array[..., :3] return array def _preprocess(image_bytes: bytes) -> np.ndarray: image = _load_image(image_bytes) resized = image.resize((TARGET_SIZE, TARGET_SIZE), Image.BILINEAR) array = np.asarray(resized, dtype="float32") array = _ensure_three_channels(array) array /= 255.0 return np.expand_dims(array, axis=0) class PreTrainedModel: def __init__(self, model_path: str = "model/best_model.keras") -> None: self.model = tf.keras.models.load_model(model_path) def predict(self, inputs: bytes) -> List[Dict[str, Any]]: x = _preprocess(inputs) preds = self.model.predict(x, verbose=0) if isinstance(preds, (list, tuple)): preds = preds[0] probs = np.asarray(preds).squeeze().tolist() idx = int(np.argmax(probs)) return [ {"label": LABELS[idx], "score": float(probs[idx])}, ] def load_model(model_dir: str = ".") -> PreTrainedModel: return PreTrainedModel(model_path=f"{model_dir}/model/best_model.keras")