import streamlit as st import requests import glob import os from PIL import Image import base64 import io # --- 1. Configuration UI --- st.set_page_config(page_title="Future Vision Transport - Démos", layout="wide", page_icon="") st.title("Segmentation Sémantique Embarquée") st.markdown("Interface d'Inférence interagissant en direct avec l'API FastAPI construite lors de la Phase 5.") # Cible du Microservice API # --- Configuration du port local (Décommenter selon le Cloud choisi) --- # Si tu testes pour GCP (Port 8000) : # API_URL = os.getenv("API_URL", "http://localhost:8000") # Si tu testes pour Hugging Face (Port 7860) : API_URL = os.getenv("API_URL", "http://localhost:7860") # --- 2. Fonctions Méthodologiques --- @st.cache_data def load_available_images(): """ Scanne intelligemment le dossier de Test pour remonter toute la flotte d'images réelles (Phase 6.1). Utilise le cache de Streamlit pour ne pas faire mouliner le disque dur à chaque clic ! """ img_dir = "data/P8_Cityscapes_leftImg8bit_trainvaltest/leftImg8bit/test/" if not os.path.exists(img_dir): return [] image_paths = sorted(glob.glob(os.path.join(img_dir, "**/*_leftImg8bit.png"), recursive=True)) return image_paths images_list = load_available_images() if not images_list: st.error("**Stop !** Dossier des images introuvable. J'ai cherché dans `data/P8_Cityscapes_leftImg8bit_trainvaltest`. Êtes-vous sûr d'avoir lancé `streamlit run app/app.py` depuis la racine de `Projet_8` ?") st.stop() # --- 3. Barre de Menu (Sidebar) --- st.sidebar.header("Tableau de Bord") st.sidebar.markdown("Sélectionnez l'image issue des caméras embarquées pour la transmettre au réseau de neurones.") # On allège visuellement la liste déroulante en n'affichant que le nom du fichier images_names = [os.path.basename(p) for p in images_list] selected_filename = st.sidebar.selectbox("Fichier à analyser :", images_names) selected_path = images_list[images_names.index(selected_filename)] # Algorithme pour reconstruire le chemin du "Ground Truth Color" (Masque Parfait) de Cityscapes city_name = selected_filename.split('_')[0] gt_color_filename = selected_filename.replace('_leftImg8bit.png', '_gtFine_color.png') gt_color_path = os.path.join("data/P8_Cityscapes_gtFine_trainvaltest/gtFine/test/", city_name, gt_color_filename) # --- 4. Le Client REST --- if st.sidebar.button("Lancer l'Inférence de l'API"): st.markdown("---") with st.spinner("Transmission sécurisée à l'API FastAPI en cours..."): try: # Sérialisation : On capture l'image en données binaires pour le flux HTTP sortant with open(selected_path, "rb") as image_file: # payload 'multipart/form-data' files = {"file": (selected_filename, image_file, "image/png")} response = requests.post(f"{API_URL}/segmentation", files=files) # Si le serveur nous envoie un Code HTTP 200 (Succès Total) if response.status_code == 200: # L'API nous renvoie désormais directement l'image au format PNG ! (StreamingResponse) predicted_mask = Image.open(io.BytesIO(response.content)) st.success("Triangulation serveur réussie ! Prédiction récupérée depuis l'API.") # --- VISUALISATION DES 3 COMPOSANTES (Phase 6.1 Guideline) --- col1, col2, col3 = st.columns(3) # A. La Réalité Capturée col1.subheader("Caméra (leftImg8bit)") col1.image(Image.open(selected_path), use_container_width=True) # B. Le Masque Parfait fourni par Cityscapes col2.subheader("Masque Vérité (Attendu)") if os.path.exists(gt_color_path): col2.image(Image.open(gt_color_path), use_container_width=True) else: col2.warning("Non fourni (Normal pour le Test-Set de compétition).") # C. L'Intelligence Artificielle en Action col3.subheader("Prédiction FastAPI (Notre IA)") col3.image(predicted_mask, use_container_width=True) # Affichage des classes (Codées en dur ici car l'API renvoie directement une image pour optimiser) CLASSES = ['void', 'flat', 'construction', 'object', 'nature', 'sky', 'human', 'vehicle'] with st.expander("Consulter la palette de couleurs sémantiques "): st.write(", ".join(CLASSES)) else: st.error(f"Erreur Serveur (HTTP {response.status_code}) : Vous devez vérifier ce que l'API imprime dans son propre terminal.") except requests.exceptions.ConnectionError: st.error(f"ERREUR CRITIQUE : L'API est injoignable sur l'URL ciblée (`{API_URL}`).") # st.info("Si vous testez en local pour GCP, lancez le serveur FastAPI via : `cd api && uvicorn main:app --port 8000` et vérifiez l'API_URL.") st.info("Si vous testez en local pour Hugging Face, lancez le serveur FastAPI via : `cd api && uvicorn main:app --port 7860` et vérifiez l'API_URL.")