Spaces:
Sleeping
Sleeping
| import os | |
| import io | |
| import logging | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI, UploadFile, File | |
| from fastapi.responses import StreamingResponse | |
| from PIL import Image | |
| import numpy as np | |
| # Setup logger | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Note : Le code pour Azure Log Analytics peut être configuré ici | |
| # en utilisant azure-monitor-opentelemetry (si la clé de connexion est fournie via env) | |
| # try: | |
| # from azure.monitor.opentelemetry import configure_azure_monitor | |
| # if "APPLICATIONINSIGHTS_CONNECTION_STRING" in os.environ: | |
| # configure_azure_monitor() | |
| # except ImportError: | |
| # pass | |
| CLASSES = ['void', 'flat', 'construction', 'object', 'nature', 'sky', 'human', 'vehicle'] | |
| COLORS = [ | |
| [0, 0, 0], [128, 64, 128], [70, 70, 70], [153, 153, 153], | |
| [107, 142, 35], [70, 130, 180], [220, 20, 60], [0, 0, 142] | |
| ] | |
| # Variables globales pour l'état de l'application | |
| model = None | |
| MODEL_PATH = os.path.join(os.path.dirname(__file__), '../models/best_unet_model.keras') | |
| async def lifespan(app: FastAPI): | |
| global model | |
| if os.path.exists(MODEL_PATH): | |
| try: | |
| os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' | |
| import tensorflow as tf | |
| logger.info("Chargement du modèle Keras...") | |
| model = tf.keras.models.load_model(MODEL_PATH, compile=False) | |
| logger.info("Modèle chargé avec succès !") | |
| except Exception as e: | |
| model = None | |
| logger.error(f"Erreur critique de TensorFlow : {e}") | |
| else: | |
| logger.warning(f"Modèle introuvable ({MODEL_PATH}). L'API tourne en 'MOCK MODE'.") | |
| yield | |
| # Nettoyage si nécessaire | |
| model = None | |
| app = FastAPI( | |
| title="Cityscapes Segmentation API", | |
| description="API de segmentation sémantique pour Future Vision Transport", | |
| version="1.0", | |
| lifespan=lifespan | |
| ) | |
| async def root(): | |
| return {"message": "Service back-end de prédiction Actif."} | |
| def health_check(): | |
| return {"status": "ok", "model_active": model is not None} | |
| async def segmentation(file: UploadFile = File(...)): | |
| # Lire l'image envoyée | |
| image_bytes = await file.read() | |
| image = Image.open(io.BytesIO(image_bytes)).convert('RGB') | |
| IMG_HEIGHT, IMG_WIDTH = 256, 512 | |
| img_resized = image.resize((IMG_WIDTH, IMG_HEIGHT)) | |
| if model is not None: | |
| # Prétraitement de l'image | |
| img_array = np.array(img_resized) / 255.0 | |
| img_batch = np.expand_dims(img_array, axis=0) | |
| # Prédiction | |
| prediction = model.predict(img_batch, verbose=0)[0] | |
| mask_classes = np.argmax(prediction, axis=-1).astype(np.uint8) | |
| else: | |
| # Mock Mode : générer un masque vide si aucun modèle n'est chargé | |
| mask_classes = np.zeros((IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8) | |
| # Colorisation du masque | |
| mask_colored = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8) | |
| for class_id, color in enumerate(COLORS): | |
| mask_colored[mask_classes == class_id] = color | |
| mask_img = Image.fromarray(mask_colored) | |
| # Convertir l'image en bytes pour la réponse | |
| img_io = io.BytesIO() | |
| mask_img.save(img_io, format="PNG") | |
| img_io.seek(0) | |
| return StreamingResponse(img_io, media_type="image/png") |