Spaces:
Build error
Build error
| import streamlit as st | |
| from PIL import Image | |
| import numpy as np | |
| import tensorflow as tf | |
| from tensorflow.keras.models import load_model | |
| import random | |
| # Taille d'entrée pour les modèles | |
| INPUT_SIZE = 224 | |
| # Chemins des modèles sauvegardés | |
| MODEL_TF_PATH = "best_model.keras" | |
| # Chargement des classes | |
| class_names = ['Cyst', 'Normal', 'Stone', 'Tumor'] | |
| # Fonction pour prédire avec TensorFlow | |
| def predict_with_tensorflow(model_path, image): | |
| # Charger le modèle TensorFlow | |
| model = load_model(model_path) | |
| # Vérifier que INPUT_SIZE et class_names sont définis | |
| assert "INPUT_SIZE" in globals() and "class_names" in globals(), \ | |
| "Les variables INPUT_SIZE et class_names doivent être définies globalement." | |
| # Prétraitement de l'image | |
| try: | |
| # Redimensionner l'image à la taille attendue par le modèle | |
| image_resized = image.resize((INPUT_SIZE, INPUT_SIZE)) | |
| image_array = np.array(image_resized) / 255.0 # Normalisation | |
| image_array = np.expand_dims(image_array, axis=0) # Ajouter une dimension batch | |
| except Exception as e: | |
| raise ValueError(f"Erreur lors du prétraitement de l'image : {e}") | |
| # Prédire avec le modèle | |
| try: | |
| predictions = model.predict(image_array) # Obtenir les probabilités pour chaque classe | |
| predictions = predictions[0] # Récupérer la première ligne (cas batch=1) | |
| except Exception as e: | |
| raise ValueError(f"Erreur lors de la prédiction avec le modèle : {e}") | |
| # Identifier la classe avec la plus haute probabilité | |
| label_idx = np.argmax(predictions) # Index de la classe prédite | |
| predicted_label = class_names[label_idx] # Nom de la classe prédite | |
| return predicted_label, predictions | |
| # Fonction JS pour animer les ballons | |
| def show_balloons(): | |
| st.markdown(""" | |
| <script type="text/javascript"> | |
| function createBalloon(x, y) { | |
| var balloon = document.createElement('div'); | |
| balloon.style.position = 'absolute'; | |
| balloon.style.left = x + 'px'; | |
| balloon.style.top = y + 'px'; | |
| balloon.style.width = '50px'; | |
| balloon.style.height = '50px'; | |
| balloon.style.background = 'url(https://example.com/balloon.png) no-repeat center center'; | |
| balloon.style.backgroundSize = 'contain'; | |
| balloon.style.animation = 'floatBalloon 6s ease-in-out infinite'; | |
| document.body.appendChild(balloon); | |
| } | |
| for (let i = 0; i < 5; i++) { | |
| var x = Math.random() * window.innerWidth; | |
| var y = Math.random() * window.innerHeight; | |
| createBalloon(x, y); | |
| } | |
| </script> | |
| """, unsafe_allow_html=True) | |
| # CSS pour une interface moderne avec dégradés et icônes | |
| st.markdown(""" | |
| <style> | |
| body { | |
| background: linear-gradient(135deg, #6e7fef, #f0c6d1); | |
| font-family: 'Arial', sans-serif; | |
| color: #333; | |
| } | |
| .title { | |
| text-align: center; | |
| font-size: 36px; | |
| font-weight: bold; | |
| color: #ff85a2; | |
| text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); | |
| margin-top: 20px; | |
| } | |
| .upload-section { | |
| text-align: center; | |
| margin: 20px; | |
| } | |
| .btn-primary { | |
| background-color: #6e7fef; | |
| border-color: #6e7fef; | |
| color: white; | |
| font-weight: bold; | |
| } | |
| .btn-primary:hover { | |
| background-color: #5573d7; | |
| } | |
| .result { | |
| text-align: center; | |
| font-size: 24px; | |
| color: #444; | |
| } | |
| .about-section { | |
| margin-top: 40px; | |
| font-size: 16px; | |
| line-height: 1.6; | |
| color: #555; | |
| } | |
| .legend { | |
| font-size: 14px; | |
| color: #555; | |
| } | |
| .sidebar .sidebar-content { | |
| background: linear-gradient(135deg, #ff85a2, #ffeb3b); | |
| color: #333; | |
| padding-top: 20px; | |
| } | |
| .sidebar .sidebar-content .block-container { | |
| padding-left: 20px; | |
| } | |
| .sidebar .sidebar-content .block { | |
| background-color: rgba(255, 255, 255, 0.7); | |
| padding: 10px; | |
| border-radius: 8px; | |
| margin-bottom: 10px; | |
| } | |
| .stSidebar { | |
| background: linear-gradient(to right, #FF6F00, #D92D2F);; | |
| color: white; | |
| font-size: 16px; | |
| } | |
| .logo { | |
| max-width: 200px; | |
| margin: 20px auto; | |
| } | |
| .logo-description { | |
| text-align: center; | |
| font-size: 18px; | |
| color: white; | |
| margin-bottom: 30px; | |
| } | |
| .sidebar select { | |
| width: 100%; | |
| padding: 10px; | |
| background-color: #f3f3f3; | |
| border-radius: 8px; | |
| font-size: 16px; | |
| border: 1px solid #ccc; | |
| } | |
| @keyframes floatBalloon { | |
| 0% { transform: translateY(0); opacity: 1; } | |
| 50% { transform: translateY(-200px); opacity: 0.5; } | |
| 100% { transform: translateY(0); opacity: 1; } | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Application Streamlit | |
| st.markdown('<div class="title">Application de Classification d\'Images</div>', unsafe_allow_html=True) | |
| menu = st.sidebar.selectbox("Menu", ["🧠 Accueil", "Classification de l'état des reins avec ResNet50", "👨💻À propos", "Légendes des modèles"]) | |
| if menu == "🧠 Accueil": | |
| st.write("Bienvenue dans notre application de classification d'images. Cette application a été développée pour la classification d'images dans le contexte du **CT-KIDNEY-DATASET-Normal-Cyst-Tumor-Stone**, un dataset médical contenant des images de tomodensitométrie (CT) de reins, avec des catégories représentant des reins normaux, des kystes, des tumeurs et des calculs rénaux.") | |
| st.write(""" | |
| Cette application a pour objectif de classer ces images en différentes catégories (**Normal** Aucun problème: , **Cyst**: Kystes rénaux, **Tumor**: Tumeur rénale, **Stone**: Calculs rénaux) à l'aide de modèles de machine learning. | |
| Nous avons effectué du Transfer Learning sur trois modèles pré entrainés et avons fait un comparatif des trois avant de choisir **ResNet50** comme celui avec les meilleurs caractéristiques. | |
| Ces modèles ont été entraînés sur le dataset CT-KIDNEY-DATASET et peuvent être utilisés pour prédire la catégorie d'une image donnée, en détectant des anomalies ou en validant l'état du rein à partir des images CT. | |
| """) | |
| # Création des onglets pour chaque modèle | |
| tab_resnet50, tab_vgg16, tab_mobilenetv2 = st.tabs(["ResNet50", "VGG16", "MobileNetV2"]) | |
| with tab_resnet50: | |
| st.image("resnet50_image.webp", caption="ResNet50", width=700) | |
| st.write(""" | |
| **ResNet50** est un réseau de neurones convolutif profond (CNN) très populaire pour la classification d'images, introduit dans l'article "Deep Residual Learning for Image Recognition". | |
| ### Avantages : | |
| - Utilise des **connexions résiduelles** pour résoudre les problèmes de dégradation des performances lors de l'augmentation de la profondeur du réseau. | |
| - Excellente précision sur de grandes bases de données d'images comme ImageNet. | |
| - Convient pour le **fine-tuning** sur des données spécifiques grâce à son architecture pré-entraînée. | |
| ### Utilisation : | |
| ResNet50 est largement utilisé dans des tâches comme : | |
| - La reconnaissance d'objets. | |
| - La segmentation d'images. | |
| - La détection de maladies en imagerie médicale. | |
| """) | |
| with tab_vgg16: | |
| st.image("vgg16_image.jpg", caption="VGG16", width=700) | |
| st.write(""" | |
| **VGG16** est un modèle de CNN développé par l'équipe de recherche Visual Geometry Group (VGG). Il est connu pour sa simplicité et son efficacité dans la classification d'images. | |
| ### Avantages : | |
| - Architecture simple avec des couches convolutives empilées suivies de couches entièrement connectées. | |
| - Bonne généralisation, même pour des données en dehors de son domaine d'origine. | |
| - Facilement extensible pour des tâches comme la segmentation et la détection. | |
| ### Utilisation : | |
| VGG16 est utilisé pour : | |
| - La classification d'images dans des bases de données variées. | |
| - L'extraction de caractéristiques pour des modèles personnalisés. | |
| - Les applications médicales nécessitant des modèles interprétables. | |
| """) | |
| with tab_mobilenetv2: | |
| st.image("mobilenetv2.webp", caption="MobileNetV2", width=700) | |
| st.write(""" | |
| **MobileNetV2** est un modèle léger optimisé pour les appareils mobiles et embarqués. Il repose sur des blocs convolutifs de profondeur et des connexions résiduelles. | |
| ### Avantages : | |
| - Très efficace en termes de calcul avec un compromis optimal entre précision et vitesse. | |
| - Convient aux appareils à faible puissance (comme les smartphones). | |
| - Supporte le déploiement facile via TensorFlow Lite ou PyTorch Mobile. | |
| ### Utilisation : | |
| MobileNetV2 est utilisé pour : | |
| - La reconnaissance d'images en temps réel sur des appareils mobiles. | |
| - Les applications embarquées nécessitant des modèles compacts. | |
| - Les tâches de vision par ordinateur sur des données limitées en ressources. | |
| """) | |
| st.write("Ces modèles pré-entraînés sont tous des choix puissants, adaptés à divers scénarios. Le choix dépend des besoins en performances, en taille de modèle et en capacité d'adaptation aux appareils cibles.") | |
| elif menu == "Classification de l'état des reins avec ResNet50": | |
| # Ajout d'un sous-titre explicatif pour informer sur la fonctionnalité | |
| st.subheader("Classification avec ResNet50 (TensorFlow)") | |
| # Présentation de la fonctionnalité pour l'utilisateur | |
| st.markdown(""" | |
| Cette section vous permet de **classer une image d'état des reins** en fonction de son apparence. | |
| Le modèle utilise **ResNet50**, une architecture d'apprentissage profond optimisée pour analyser les images. | |
| Voici ce que vous devez faire : | |
| 1. Téléchargez une image au format `jpg`, `jpeg` ou `png`. | |
| 2. Cliquez sur le bouton **Classifier** pour lancer l'analyse. | |
| 3. Obtenez le résultat du diagnostic (Normal ou Anomalie) accompagné d'un graphique des probabilités. | |
| """) | |
| # Étape 1 : L'utilisateur télécharge une image | |
| uploaded_file = st.file_uploader("Téléchargez une image des reins (format jpg, jpeg ou png)", type=["jpg", "jpeg", "png"]) | |
| if uploaded_file is not None: | |
| # Affiche l'image téléchargée | |
| image = Image.open(uploaded_file) | |
| st.image(image, caption="Image téléchargée avec succès", use_container_width=True) | |
| # Explication pour l'étape suivante | |
| st.markdown(""" | |
| Cliquez sur le bouton **Classifier** pour que le modèle analyse l'image et détermine si l'état des reins est **Normal** ou présente une **Anomalie**. | |
| """) | |
| # Étape 2 : L'utilisateur clique pour classifier l'image | |
| if st.button("Classifier"): | |
| # Appel de la fonction de prédiction avec le modèle ResNet50 | |
| label, probabilities = predict_with_tensorflow(MODEL_TF_PATH, image) | |
| # Affichage du résultat | |
| st.markdown(f""" | |
| ### Résultat de la classification : | |
| **Classe prédite :** {label} | |
| """, unsafe_allow_html=True) | |
| # Affichage des probabilités sous forme de graphique | |
| st.markdown(""" | |
| #### Confiance du modèle dans chaque catégorie : | |
| Le graphique ci-dessous montre la probabilité associée à chaque classe. Une probabilité élevée indique la classe la plus probable. | |
| """) | |
| st.bar_chart(probabilities) | |
| # Affichage d'un message visuel en fonction de la classe prédite | |
| if label == "Normal": | |
| st.balloons() # Animation festive si le résultat est "Normal" | |
| st.success("Félicitations ! L'image a été classée comme **Normale**.") | |
| else: | |
| st.error("Désolé, le modèle indique une **Anomalie** dans l'image téléchargée.") | |
| st.markdown(""" | |
| #### Que faire en cas d'anomalie ? | |
| Si une anomalie est détectée, il est recommandé de : | |
| - Vérifier l'image téléchargée pour s'assurer qu'elle est correcte. | |
| - Contacter un professionnel de santé pour une analyse approfondie. | |
| """) | |
| elif menu == "Légendes des modèles": | |
| st.subheader("Légendes des modèles") | |
| st.write("Voici des informations détaillées sur le modèle utilisé dans cette application :") | |
| st.write(""" | |
| ### ResNet50 avec Transfert Learning | |
| **ResNet50** est un réseau de neurones convolutif pré-entraîné sur le dataset ImageNet. | |
| Grâce au **transfert learning**, nous avons adapté ce modèle à notre propre jeu de données pour effectuer des classifications spécifiques. | |
| #### Avantages de ResNet50 : | |
| - **Connexions résiduelles** qui facilitent l'apprentissage pour des réseaux profonds. | |
| - **Performances élevées** pour les tâches de vision par ordinateur, même avec des données limitées. | |
| - Adapté pour des tâches comme la reconnaissance d'images ou la classification médicale. | |
| #### Fonctionnement : | |
| - Nous avons utilisé le modèle pré-entraîné pour extraire des caractéristiques. | |
| - Les couches de classification finales ont été remplacées par des couches adaptées à notre domaine. | |
| ### Visualisation des résultats : | |
| - Une image téléchargée est analysée par le modèle. | |
| - Les probabilités des différentes classes sont affichées sous forme de graphique. | |
| """) | |
| elif menu == "👨💻À propos": | |
| st.header("À propos de moi") | |
| st.markdown( | |
| """ | |
| <div style="text-align:center; font-family: Arial; margin: 20px 0;"> | |
| <h2>Mon Parcours</h2> | |
| <p>Je suis un passionné de l'intelligence artificielle et de la donnée. Actuellement en Master 2 en IA et Big Data, je travaille sur des solutions innovantes dans le domaine de l'Intelligence Artificielle appliquée à la santé.</p> | |
| </div> | |
| <div style="display: flex; justify-content: center; gap: 30px; flex-wrap: wrap;"> | |
| <div style="text-align: center;"> | |
| <img src="https://avatars.githubusercontent.com/u/1234567" alt="Ngoue David" style="width: 150px; height: 150px; border-radius: 50%;"> | |
| <h4>Ngoue David</h4> | |
| <p>🎓 Master 2 IA & Big Data</p> | |
| <p>📧 <a href="mailto:ngouedavidrogeryannick@gmail.com">ngouedavidrogeryannick@gmail.com</a></p> | |
| <p>🌐 <a href="https://github.com/TheBeyonder237" target="_blank">Profil GitHub</a></p> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) |