akiroussama's picture
Add complete application: src/, .streamlit/, models/
38691ae verified
"""
Utilitaires de traitement d'images pour l'application Rakuten.
Ce module fournit des fonctions pour:
- Charger et valider des images uploadées
- Redimensionner les images pour le modèle
- Prétraiter les images pour l'extraction de features ResNet50
"""
from typing import Tuple, Optional
from pathlib import Path
import io
import sys
from PIL import Image
import numpy as np
# Ajouter le répertoire parent au path pour les imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import IMAGE_CONFIG
def load_image_from_upload(uploaded_file) -> Image.Image:
"""
Charge une image depuis un fichier uploadé Streamlit.
Args:
uploaded_file: Objet UploadedFile de Streamlit
Returns:
Image PIL en mode RGB
Raises:
ValueError: Si le fichier n'est pas une image valide
"""
try:
image = Image.open(uploaded_file)
# Convertir en RGB si nécessaire (gère PNG avec transparence, etc.)
if image.mode != "RGB":
image = image.convert("RGB")
return image
except Exception as e:
raise ValueError(f"Impossible de charger l'image: {e}")
def validate_image(image: Image.Image) -> Tuple[bool, str]:
"""
Valide une image selon les critères de l'application.
Args:
image: Image PIL à valider
Returns:
Tuple (is_valid, message)
"""
# Vérifier les dimensions minimales
min_size = 32
if image.width < min_size or image.height < min_size:
return False, f"Image trop petite (minimum {min_size}x{min_size})"
# Vérifier les dimensions maximales
max_size = 10000
if image.width > max_size or image.height > max_size:
return False, f"Image trop grande (maximum {max_size}x{max_size})"
return True, "Image valide"
def resize_image(
image: Image.Image,
target_size: Tuple[int, int] = None,
maintain_aspect_ratio: bool = True
) -> Image.Image:
"""
Redimensionne une image vers la taille cible.
Args:
image: Image PIL à redimensionner
target_size: Taille cible (width, height). Par défaut: config.IMAGE_CONFIG
maintain_aspect_ratio: Si True, conserve le ratio et ajoute du padding
Returns:
Image PIL redimensionnée
"""
if target_size is None:
target_size = IMAGE_CONFIG["target_size"]
if maintain_aspect_ratio:
return _resize_with_padding(image, target_size)
else:
return image.resize(target_size, Image.Resampling.LANCZOS)
def _resize_with_padding(
image: Image.Image,
target_size: Tuple[int, int],
fill_color: Tuple[int, int, int] = (255, 255, 255)
) -> Image.Image:
"""
Redimensionne une image en conservant le ratio et en ajoutant du padding.
Args:
image: Image PIL source
target_size: Taille cible (width, height)
fill_color: Couleur de remplissage RGB pour le padding
Returns:
Image PIL avec padding
"""
target_w, target_h = target_size
original_w, original_h = image.size
# Calculer le ratio de redimensionnement
ratio = min(target_w / original_w, target_h / original_h)
new_w = int(original_w * ratio)
new_h = int(original_h * ratio)
# Redimensionner l'image
resized = image.resize((new_w, new_h), Image.Resampling.LANCZOS)
# Créer l'image avec padding
padded = Image.new("RGB", target_size, fill_color)
# Centrer l'image redimensionnée
paste_x = (target_w - new_w) // 2
paste_y = (target_h - new_h) // 2
padded.paste(resized, (paste_x, paste_y))
return padded
def preprocess_for_resnet(image: Image.Image) -> np.ndarray:
"""
Prétraite une image pour l'extraction de features ResNet50.
Applique les transformations standard ImageNet:
- Redimensionnement à 224x224
- Normalisation des pixels
- Expansion des dimensions pour le batch
Args:
image: Image PIL en RGB
Returns:
Array numpy de shape (1, 224, 224, 3) prêt pour ResNet50
"""
# Redimensionner à 224x224
resized = resize_image(image, (224, 224), maintain_aspect_ratio=True)
# Convertir en array numpy
img_array = np.array(resized, dtype=np.float32)
# Normalisation ImageNet (preprocessing Keras)
# Mode 'caffe': BGR, centré sur ImageNet mean
img_array = img_array[..., ::-1] # RGB -> BGR
img_array[..., 0] -= 103.939
img_array[..., 1] -= 116.779
img_array[..., 2] -= 123.68
# Ajouter la dimension batch
img_array = np.expand_dims(img_array, axis=0)
return img_array
def image_to_bytes(image: Image.Image, format: str = "PNG") -> bytes:
"""
Convertit une image PIL en bytes.
Args:
image: Image PIL
format: Format de sortie (PNG, JPEG, etc.)
Returns:
Bytes de l'image
"""
buffer = io.BytesIO()
image.save(buffer, format=format)
return buffer.getvalue()
def create_thumbnail(
image: Image.Image,
max_size: Tuple[int, int] = (150, 150)
) -> Image.Image:
"""
Crée une miniature d'une image.
Args:
image: Image PIL source
max_size: Taille maximale de la miniature
Returns:
Image PIL miniature
"""
thumbnail = image.copy()
thumbnail.thumbnail(max_size, Image.Resampling.LANCZOS)
return thumbnail
def get_image_info(image: Image.Image) -> dict:
"""
Retourne les informations d'une image.
Args:
image: Image PIL
Returns:
Dict avec les informations de l'image
"""
return {
"width": image.width,
"height": image.height,
"mode": image.mode,
"format": image.format,
"size_str": f"{image.width}x{image.height}",
}