import os import sys import requests import streamlit as st import numpy as np import matplotlib.pyplot as plt from PIL import Image import io import base64 import pickle # Configuration de l'API API_BASE_URL = os.getenv("MULTISEG_API_BASE_URL", "http://127.0.0.1:8080/api") # Import des fonctions utils pour l'affichage local sys.path.append('/app') sys.path.append('.') from utils.utils import colorize_mask # Configuration de la page st.set_page_config( page_title="Multi-Class Segmentation Comparison", page_icon="🔄", layout="wide" ) # CSS personnalisé mis à jour st.markdown(""" """, unsafe_allow_html=True) # En-tête principal st.markdown("""

🔄 Comparaison de Segmentation Multi-Classes

Système de comparaison entre deux modèles de segmentation sémantique

""", unsafe_allow_html=True) def get_models_info(): """Récupère les informations des deux modèles""" try: response = requests.get(f"{API_BASE_URL}/models") if response.status_code == 200: return response.json() else: st.error(f"Erreur lors de la récupération des infos modèles: {response.status_code}") return None except Exception as e: st.error(f"Erreur de connexion à l'API: {e}") return None def get_sample_images(): """Récupère la liste des images d'exemple""" try: response = requests.get(f"{API_BASE_URL}/sample-images") if response.status_code == 200: return response.json() else: st.error(f"Erreur lors de la récupération des images: {response.status_code}") return None except Exception as e: st.error(f"Erreur de connexion à l'API: {e}") return None def compare_sample_image(filename): """Lance une comparaison sur une image d'exemple""" try: data = {"filename": filename} response = requests.post( f"{API_BASE_URL}/compare-sample", json=data, headers={"Content-Type": "application/json"} ) if response.status_code == 200: return response.json() else: st.error(f"Erreur API: {response.status_code} - {response.text}") return None except Exception as e: st.error(f"Erreur lors de la comparaison: {e}") return None def compare_uploaded_image(uploaded_file): """Lance une comparaison sur une image uploadée""" try: files = {"file": (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)} response = requests.post(f"{API_BASE_URL}/compare-upload", files=files) if response.status_code == 200: return response.json() else: st.error(f"Erreur API: {response.status_code} - {response.text}") return None except Exception as e: st.error(f"Erreur lors de la comparaison: {e}") return None def display_comparison_results(result, models_info): """Affiche les résultats de comparaison entre les deux modèles""" if not result or not result['success']: st.error("Aucun résultat de comparaison disponible") return # Informations des modèles st.markdown("### 🏆 Modèles Comparés") col1, col2 = st.columns(2) with col1: model1 = result['model1_info'] st.markdown(f"""

🥇 Modèle #1

{model1['encoder_name']}
📐 Taille: {model1['input_size']}
🆔 Run: {model1['run_id'][:8]}...
""", unsafe_allow_html=True) with col2: model2 = result['model2_info'] st.markdown(f"""

🥈 Modèle #2

{model2['encoder_name']}
📐 Taille: {model2['input_size']}
🆔 Run: {model2['run_id'][:8]}...
""", unsafe_allow_html=True) # Comparaison des temps d'inférence st.markdown("### ⏱️ Comparaison des Performances") speed_comp = result['speed_comparison'] model1_time = result['model1_inference_time'] * 1000 model2_time = result['model2_inference_time'] * 1000 col1, col2, col3 = st.columns(3) with col1: winner_class = "speed-winner" if speed_comp['faster_model'] == model1['encoder_name'] else "speed-slower" st.markdown(f"""
🥇 {model1['encoder_name']}
{model1_time:.1f} ms
""", unsafe_allow_html=True) with col2: st.markdown(f"""
⚡ Comparaison
{speed_comp['faster_model']}
est {speed_comp['speedup_ratio']}x plus rapide
Différence: {speed_comp['time_difference_ms']:.1f} ms
""", unsafe_allow_html=True) with col3: winner_class = "speed-winner" if speed_comp['faster_model'] == model2['encoder_name'] else "speed-slower" st.markdown(f"""
🥈 {model2['encoder_name']}
{model2_time:.1f} ms
""", unsafe_allow_html=True) # Affichage des segmentations if result.get('figure_data'): st.markdown("### 🎨 Résultats de Segmentation") try: fig_bytes = base64.b64decode(result['figure_data']) fig = pickle.loads(fig_bytes) st.pyplot(fig) plt.close(fig) except Exception as e: st.error(f"Erreur lors de l'affichage: {e}") # Comparaison des statistiques st.markdown("### 📊 Comparaison des Statistiques") col1, col2 = st.columns(2) with col1: st.markdown(f"**📈 {model1['encoder_name']} - Répartition des classes:**") if result.get('model1_stats'): display_model_stats_chart(result['model1_stats'], models_info, f"Modèle 1 - {model1['encoder_name']}") with col2: st.markdown(f"**📈 {model2['encoder_name']} - Répartition des classes:**") if result.get('model2_stats'): display_model_stats_chart(result['model2_stats'], models_info, f"Modèle 2 - {model2['encoder_name']}") # Analyse comparative des classes if result.get('model1_stats') and result.get('model2_stats'): display_class_comparison(result['model1_stats'], result['model2_stats'], model1, model2) def display_model_stats_chart(stats, models_info, title): """Affiche les statistiques d'un modèle sous forme de graphique""" if not stats: st.warning("Aucune statistique disponible") return # Préparer les données pour le graphique class_names = list(stats.keys()) percentages = [data['percentage'] for data in stats.values()] # Récupérer les couleurs des classes colors = [] if models_info and 'class_colors' in models_info and 'class_names' in models_info: model_class_names = models_info['class_names'] model_class_colors = models_info['class_colors'] for class_name in class_names: try: idx = model_class_names.index(class_name) color = model_class_colors[idx] colors.append([c/255.0 for c in color]) except (ValueError, IndexError): colors.append([0.5, 0.5, 0.5]) else: colors = plt.cm.Set3(np.linspace(0, 1, len(class_names))) # Créer le graphique fig, ax = plt.subplots(figsize=(8, 5)) bars = ax.bar(class_names, percentages, color=colors, alpha=0.8, edgecolor='white', linewidth=1) ax.set_xlabel('Classes de Segmentation', fontsize=10, fontweight='bold') ax.set_ylabel('Pourcentage (%)', fontsize=10, fontweight='bold') ax.set_title(title, fontsize=11, fontweight='bold', pad=15) # Rotation des labels selon le nombre de classes if len(class_names) <= 4: plt.xticks(rotation=0, ha='center', fontsize=9) elif len(class_names) <= 6: plt.xticks(rotation=30, ha='right', fontsize=8) else: plt.xticks(rotation=45, ha='right', fontsize=8) plt.yticks(fontsize=9) # Valeurs sur les barres for bar, percentage in zip(bars, percentages): height = bar.get_height() ax.text(bar.get_x() + bar.get_width()/2., height + max(percentages) * 0.01, f'{percentage:.1f}%', ha='center', va='bottom', fontsize=8, fontweight='bold') plt.tight_layout() ax.grid(axis='y', alpha=0.2, linestyle='--') ax.set_axisbelow(True) # Style des axes ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) # Couleur de fond fig.patch.set_facecolor('white') ax.set_facecolor('#fafafa') st.pyplot(fig) plt.close(fig) def display_class_comparison(stats1, stats2, model1, model2): """Affiche une comparaison détaillée des classes entre les deux modèles""" st.markdown("### 🔍 Analyse Comparative par Classe") # Combiner toutes les classes présentes all_classes = set(stats1.keys()) | set(stats2.keys()) comparison_data = [] for class_name in sorted(all_classes): pct1 = stats1.get(class_name, {}).get('percentage', 0) pct2 = stats2.get(class_name, {}).get('percentage', 0) diff = pct1 - pct2 comparison_data.append({ 'class': class_name, 'model1_pct': pct1, 'model2_pct': pct2, 'difference': diff }) # Afficher sous forme de tableau interactif col1, col2 = st.columns([2, 1]) with col1: st.markdown("**📋 Tableau Comparatif:**") for data in comparison_data: diff_color = "🟢" if abs(data['difference']) < 1 else ("🔴" if abs(data['difference']) > 5 else "🟡") diff_text = f"{data['difference']:+.1f}%" st.markdown(f""" **{data['class']}** {diff_color} - {model1['encoder_name']}: {data['model1_pct']:.1f}% - {model2['encoder_name']}: {data['model2_pct']:.1f}% - Différence: {diff_text} """) with col2: st.markdown("**🎯 Légende:**") st.markdown("🟢 Différence < 1%") st.markdown("🟡 Différence 1-5%") st.markdown("🔴 Différence > 5%") # Sidebar avec informations des modèles with st.sidebar: st.markdown("## 🔧 Informations des Modèles") models_info = get_models_info() if models_info: st.success("✅ Modèles chargés") # Modèle 1 best_model = models_info.get('best_model', {}) st.markdown("**🥇 Meilleur Modèle:**") st.write(f"**Encoder:** {best_model.get('encoder_name', 'N/A')}") st.write(f"**Run ID:** {best_model.get('run_id', 'N/A')[:8]}...") # Modèle 2 second_model = models_info.get('second_best_model', {}) st.markdown("**🥈 Deuxième Modèle:**") st.write(f"**Encoder:** {second_model.get('encoder_name', 'N/A')}") st.write(f"**Run ID:** {second_model.get('run_id', 'N/A')[:8]}...") st.write(f"**Classes:** {models_info.get('num_classes', 'N/A')}") else: st.error("❌ Modèles non disponibles") st.markdown("---") st.markdown("## 🎯 Classes Segmentées") # Affichage avec carrés colorés if models_info and 'class_names' in models_info and 'class_colors' in models_info: class_names = models_info['class_names'] class_colors = models_info['class_colors'] for i, (class_name, color) in enumerate(zip(class_names, class_colors)): color_hex = f"#{color[0]:02x}{color[1]:02x}{color[2]:02x}" st.markdown(f"""
{i}. {class_name}
""", unsafe_allow_html=True) # États de session if "sample_comparison_result" not in st.session_state: st.session_state.sample_comparison_result = None if "upload_comparison_result" not in st.session_state: st.session_state.upload_comparison_result = None def reset_sample_comparison(): st.session_state.sample_comparison_result = None def reset_upload_comparison(): st.session_state.upload_comparison_result = None # Interface principale tab1, tab2 = st.tabs(["📸 Images d'Exemple", "📤 Upload Personnel"]) # Tab 1: Images d'exemple with tab1: st.markdown("## 📸 Comparaison sur Images d'Exemple") st.markdown("Comparez les performances des deux modèles sur des images Cityscapes.") # Récupérer les images d'exemple sample_data = get_sample_images() if sample_data and sample_data['images']: # Sélecteur d'image image_options = {img['display_name']: img['filename'] for img in sample_data['images']} selected_display_name = st.selectbox( "Choisissez une image:", options=list(image_options.keys()), help=f"{sample_data['total_count']} images disponibles" ) selected_filename = image_options[selected_display_name] # Afficher les infos de l'image sélectionnée selected_img_info = next(img for img in sample_data['images'] if img['filename'] == selected_filename) col1, col2 = st.columns([2, 1]) with col1: st.info(f"📁 **Fichier:** {selected_filename}") with col2: ground_truth_status = "✅ Disponible" if selected_img_info['has_ground_truth'] else "❌ Non disponible" st.info(f"🎯 **Vérité terrain:** {ground_truth_status}") # Prévisualisation de l'image sélectionnée if selected_filename: try: image_url = f"{API_BASE_URL}/sample-image/{selected_filename}" response = requests.get(image_url, timeout=5) if response.status_code == 200: sample_image = Image.open(io.BytesIO(response.content)) st.image( sample_image, caption=f"Aperçu: {selected_display_name}", use_container_width=True ) except: st.info("📷 Aperçu non disponible - L'image sera chargée lors de la comparaison") # Bouton de comparaison if st.button("🔄 Lancer la Comparaison", key="compare_sample", type="primary"): if selected_filename: with st.spinner("Comparaison en cours..."): result = compare_sample_image(selected_filename) if result and result['success']: st.session_state.sample_comparison_result = result st.success("✅ Comparaison terminée!") st.rerun() else: st.error("❌ Erreur lors de la comparaison") else: st.warning("Aucune image d'exemple disponible") # Affichage des résultats de comparaison if st.session_state.sample_comparison_result: st.markdown("---") display_comparison_results(st.session_state.sample_comparison_result, models_info) # Bouton de reset if st.button("🔄 Nouvelle Comparaison", key="reset_sample", type="secondary"): reset_sample_comparison() st.rerun() # Tab 2: Upload personnel with tab2: st.markdown("## 📤 Comparaison sur Image Personnelle") st.markdown("Uploadez une image pour comparer les performances des deux modèles.") uploaded_file = st.file_uploader( "Sélectionnez une image:", type=['jpg', 'jpeg', 'png'], help="Taille maximale recommandée: 5MB" ) if uploaded_file is not None: # Afficher l'image uploadée image = Image.open(uploaded_file) st.image(image, caption=f"Image uploadée: {uploaded_file.name}", use_container_width=True) # Informations sur le fichier file_size_mb = len(uploaded_file.getvalue()) / 1024 / 1024 st.info(f"📁 **Fichier:** {uploaded_file.name} | 📊 **Taille:** {file_size_mb:.2f} MB") # Bouton de comparaison if st.button("🔄 Lancer la Comparaison", key="compare_upload", type="primary"): with st.spinner("Comparaison en cours..."): result = compare_uploaded_image(uploaded_file) if result and result['success']: st.session_state.upload_comparison_result = result st.success("✅ Comparaison terminée!") st.rerun() else: st.error("❌ Erreur lors de la comparaison") # Affichage des résultats de comparaison if st.session_state.upload_comparison_result: st.markdown("---") display_comparison_results(st.session_state.upload_comparison_result, models_info) # Bouton de reset if st.button("🔄 Nouvelle Comparaison", key="reset_upload", type="secondary"): reset_upload_comparison() st.rerun() # Footer st.markdown("---") st.markdown("""
🔄 Multi-Class Segmentation Comparison API - Système de comparaison de modèles de vision par ordinateur
Développé avec FastAPI, Streamlit et PyTorch
""", unsafe_allow_html=True)