File size: 15,586 Bytes
0921a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f18029c
0921a8d
 
 
 
 
f18029c
0921a8d
 
 
 
 
 
 
 
f18029c
0921a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f18029c
0921a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f18029c
0921a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f18029c
 
0921a8d
f18029c
 
 
 
 
 
 
 
 
 
 
 
 
0921a8d
 
f18029c
0921a8d
f18029c
0921a8d
f18029c
 
 
 
0921a8d
f18029c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0921a8d
 
f18029c
0921a8d
f18029c
 
0921a8d
f18029c
 
 
 
 
 
 
 
0921a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6f30a12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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)