DavidNgoue commited on
Commit
6f30a12
·
verified ·
1 Parent(s): a8083ec

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +314 -0
  3. best_model.keras +3 -0
  4. requirements.txt +0 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ best_model.keras filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+ import numpy as np
4
+ import torch
5
+ from torchvision.transforms import Compose, Resize, ToTensor, Normalize
6
+ import tensorflow as tf
7
+ from tensorflow.keras.models import load_model
8
+ import random
9
+
10
+ # Taille d'entrée pour les modèles
11
+ INPUT_SIZE = 224
12
+
13
+ # Chemins des modèles sauvegardés
14
+ MODEL_TF_PATH = "best_model.keras"
15
+
16
+ # Chargement des classes
17
+ class_names = ['Cyst', 'Normal', 'Stone', 'Tumor']
18
+
19
+
20
+ # Fonction pour prédire avec TensorFlow
21
+ def predict_with_tensorflow(model_path, image):
22
+ # Charger le modèle TensorFlow
23
+ model = load_model(model_path)
24
+
25
+ # Vérifier que INPUT_SIZE et class_names sont définis
26
+ assert "INPUT_SIZE" in globals() and "class_names" in globals(), \
27
+ "Les variables INPUT_SIZE et class_names doivent être définies globalement."
28
+
29
+ # Prétraitement de l'image
30
+ try:
31
+ # Redimensionner l'image à la taille attendue par le modèle
32
+ image_resized = image.resize((INPUT_SIZE, INPUT_SIZE))
33
+ image_array = np.array(image_resized) / 255.0 # Normalisation
34
+ image_array = np.expand_dims(image_array, axis=0) # Ajouter une dimension batch
35
+ except Exception as e:
36
+ raise ValueError(f"Erreur lors du prétraitement de l'image : {e}")
37
+
38
+ # Prédire avec le modèle
39
+ try:
40
+ predictions = model.predict(image_array) # Obtenir les probabilités pour chaque classe
41
+ predictions = predictions[0] # Récupérer la première ligne (cas batch=1)
42
+ except Exception as e:
43
+ raise ValueError(f"Erreur lors de la prédiction avec le modèle : {e}")
44
+
45
+ # Identifier la classe avec la plus haute probabilité
46
+ label_idx = np.argmax(predictions) # Index de la classe prédite
47
+ predicted_label = class_names[label_idx] # Nom de la classe prédite
48
+
49
+ return predicted_label, predictions
50
+
51
+
52
+ # Fonction JS pour animer les ballons
53
+ def show_balloons():
54
+ st.markdown("""
55
+ <script type="text/javascript">
56
+ function createBalloon(x, y) {
57
+ var balloon = document.createElement('div');
58
+ balloon.style.position = 'absolute';
59
+ balloon.style.left = x + 'px';
60
+ balloon.style.top = y + 'px';
61
+ balloon.style.width = '50px';
62
+ balloon.style.height = '50px';
63
+ balloon.style.background = 'url(https://example.com/balloon.png) no-repeat center center';
64
+ balloon.style.backgroundSize = 'contain';
65
+ balloon.style.animation = 'floatBalloon 6s ease-in-out infinite';
66
+ document.body.appendChild(balloon);
67
+ }
68
+
69
+ for (let i = 0; i < 5; i++) {
70
+ var x = Math.random() * window.innerWidth;
71
+ var y = Math.random() * window.innerHeight;
72
+ createBalloon(x, y);
73
+ }
74
+ </script>
75
+ """, unsafe_allow_html=True)
76
+
77
+ # CSS pour une interface moderne avec dégradés et icônes
78
+ st.markdown("""
79
+ <style>
80
+ body {
81
+ background: linear-gradient(135deg, #6e7fef, #f0c6d1);
82
+ font-family: 'Arial', sans-serif;
83
+ color: #333;
84
+ }
85
+
86
+ .title {
87
+ text-align: center;
88
+ font-size: 36px;
89
+ font-weight: bold;
90
+ color: #ff85a2;
91
+ text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
92
+ margin-top: 20px;
93
+ }
94
+
95
+ .upload-section {
96
+ text-align: center;
97
+ margin: 20px;
98
+ }
99
+
100
+ .btn-primary {
101
+ background-color: #6e7fef;
102
+ border-color: #6e7fef;
103
+ color: white;
104
+ font-weight: bold;
105
+ }
106
+
107
+ .btn-primary:hover {
108
+ background-color: #5573d7;
109
+ }
110
+
111
+ .result {
112
+ text-align: center;
113
+ font-size: 24px;
114
+ color: #444;
115
+ }
116
+
117
+ .about-section {
118
+ margin-top: 40px;
119
+ font-size: 16px;
120
+ line-height: 1.6;
121
+ color: #555;
122
+ }
123
+
124
+ .legend {
125
+ font-size: 14px;
126
+ color: #555;
127
+ }
128
+
129
+ .sidebar .sidebar-content {
130
+ background: linear-gradient(135deg, #ff85a2, #ffeb3b);
131
+ color: #333;
132
+ padding-top: 20px;
133
+ }
134
+
135
+ .sidebar .sidebar-content .block-container {
136
+ padding-left: 20px;
137
+ }
138
+
139
+ .sidebar .sidebar-content .block {
140
+ background-color: rgba(255, 255, 255, 0.7);
141
+ padding: 10px;
142
+ border-radius: 8px;
143
+ margin-bottom: 10px;
144
+ }
145
+
146
+ .stSidebar {
147
+ background: linear-gradient(to right, #FF6F00, #D92D2F);;
148
+ color: white;
149
+ font-size: 16px;
150
+ }
151
+
152
+ .logo {
153
+ max-width: 200px;
154
+ margin: 20px auto;
155
+ }
156
+
157
+ .logo-description {
158
+ text-align: center;
159
+ font-size: 18px;
160
+ color: white;
161
+ margin-bottom: 30px;
162
+ }
163
+
164
+ .sidebar select {
165
+ width: 100%;
166
+ padding: 10px;
167
+ background-color: #f3f3f3;
168
+ border-radius: 8px;
169
+ font-size: 16px;
170
+ border: 1px solid #ccc;
171
+ }
172
+
173
+ @keyframes floatBalloon {
174
+ 0% { transform: translateY(0); opacity: 1; }
175
+ 50% { transform: translateY(-200px); opacity: 0.5; }
176
+ 100% { transform: translateY(0); opacity: 1; }
177
+ }
178
+ </style>
179
+ """, unsafe_allow_html=True)
180
+
181
+ # Application Streamlit
182
+ st.markdown('<div class="title">Application de Classification d\'Images</div>', unsafe_allow_html=True)
183
+
184
+ menu = st.sidebar.selectbox("Menu", ["🧠 Accueil", "Classification avec ResNet50 (TensorFlow)", "👨‍💻À propos", "Légendes des modèles"])
185
+
186
+ if menu == "🧠 Accueil":
187
+ 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.")
188
+
189
+ st.write("""
190
+ Cette application a pour objectif de classer ces images en différentes catégories (Normal, Cyst, Tumor, Stone) à l'aide de modèles de machine learning.
191
+ 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.
192
+ 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.
193
+ """)
194
+
195
+ # Création des onglets pour chaque modèle
196
+ tab_resnet50, tab_vgg16, tab_mobilenetv2 = st.tabs(["ResNet50", "VGG16", "MobileNetV2"])
197
+
198
+ with tab_resnet50:
199
+ st.image("resnet50_image.webp", caption="ResNet50", width=300)
200
+ st.write("""
201
+ **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".
202
+
203
+ ### Avantages :
204
+ - 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.
205
+ - Excellente précision sur de grandes bases de données d'images comme ImageNet.
206
+ - Convient pour le **fine-tuning** sur des données spécifiques grâce à son architecture pré-entraînée.
207
+
208
+ ### Utilisation :
209
+ ResNet50 est largement utilisé dans des tâches comme :
210
+ - La reconnaissance d'objets.
211
+ - La segmentation d'images.
212
+ - La détection de maladies en imagerie médicale.
213
+ """)
214
+
215
+ with tab_vgg16:
216
+ st.image("vgg16_image.jpg", caption="VGG16", width=300)
217
+ st.write("""
218
+ **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.
219
+
220
+ ### Avantages :
221
+ - Architecture simple avec des couches convolutives empilées suivies de couches entièrement connectées.
222
+ - Bonne généralisation, même pour des données en dehors de son domaine d'origine.
223
+ - Facilement extensible pour des tâches comme la segmentation et la détection.
224
+
225
+ ### Utilisation :
226
+ VGG16 est utilisé pour :
227
+ - La classification d'images dans des bases de données variées.
228
+ - L'extraction de caractéristiques pour des modèles personnalisés.
229
+ - Les applications médicales nécessitant des modèles interprétables.
230
+ """)
231
+
232
+ with tab_mobilenetv2:
233
+ st.image("mobilenetv2.webp", caption="MobileNetV2", width=300)
234
+ st.write("""
235
+ **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.
236
+
237
+ ### Avantages :
238
+ - Très efficace en termes de calcul avec un compromis optimal entre précision et vitesse.
239
+ - Convient aux appareils à faible puissance (comme les smartphones).
240
+ - Supporte le déploiement facile via TensorFlow Lite ou PyTorch Mobile.
241
+
242
+ ### Utilisation :
243
+ MobileNetV2 est utilisé pour :
244
+ - La reconnaissance d'images en temps réel sur des appareils mobiles.
245
+ - Les applications embarquées nécessitant des modèles compacts.
246
+ - Les tâches de vision par ordinateur sur des données limitées en ressources.
247
+ """)
248
+
249
+ 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.")
250
+
251
+
252
+
253
+ elif menu == "Classification avec ResNet50 (TensorFlow)":
254
+ st.subheader("Classification avec ResNet50 (TensorFlow)")
255
+ uploaded_file = st.file_uploader("Téléchargez une image", type=["jpg", "jpeg", "png"])
256
+
257
+ if uploaded_file is not None:
258
+ image = Image.open(uploaded_file)
259
+ st.image(image, caption="Image Téléchargée", use_container_width=True)
260
+
261
+ if st.button("Classifier"):
262
+ label, probabilities = predict_with_tensorflow(MODEL_TF_PATH, image) # Fonction de prédiction adaptée pour ResNet50
263
+ st.markdown(f'<div class="result">Classe prédite : <b>{label}</b></div>', unsafe_allow_html=True)
264
+
265
+ # Affiche un graphique en barres des probabilités des classes
266
+ st.bar_chart(probabilities)
267
+
268
+ # Résultat visuel en fonction de la classe prédite
269
+ if label == "Normal":
270
+ st.balloons() # Animation festive si la prédiction est "Normal"
271
+ else:
272
+ st.markdown("<h2 style='color: red;'>Désolé, le résultat indique une anomalie...</h2>", unsafe_allow_html=True)
273
+
274
+ elif menu == "Légendes des modèles":
275
+ st.subheader("Légendes des modèles")
276
+ st.write("Voici des informations détaillées sur le modèle utilisé dans cette application :")
277
+ st.write("""
278
+ ### ResNet50 avec Transfert Learning
279
+ **ResNet50** est un réseau de neurones convolutif pré-entraîné sur le dataset ImageNet.
280
+ Grâce au **transfert learning**, nous avons adapté ce modèle à notre propre jeu de données pour effectuer des classifications spécifiques.
281
+
282
+ #### Avantages de ResNet50 :
283
+ - **Connexions résiduelles** qui facilitent l'apprentissage pour des réseaux profonds.
284
+ - **Performances élevées** pour les tâches de vision par ordinateur, même avec des données limitées.
285
+ - Adapté pour des tâches comme la reconnaissance d'images ou la classification médicale.
286
+
287
+ #### Fonctionnement :
288
+ - Nous avons utilisé le modèle pré-entraîné pour extraire des caractéristiques.
289
+ - Les couches de classification finales ont été remplacées par des couches adaptées à notre domaine.
290
+
291
+ ### Visualisation des résultats :
292
+ - Une image téléchargée est analysée par le modèle.
293
+ - Les probabilités des différentes classes sont affichées sous forme de graphique.
294
+ """)
295
+
296
+
297
+ elif menu == "👨‍💻À propos":
298
+ st.header("À propos de moi")
299
+ st.markdown(
300
+ """
301
+ <div style="text-align:center; font-family: Arial; margin: 20px 0;">
302
+ <h2>Mon Parcours</h2>
303
+ <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>
304
+ </div>
305
+ <div style="display: flex; justify-content: center; gap: 30px; flex-wrap: wrap;">
306
+ <div style="text-align: center;">
307
+ <img src="https://avatars.githubusercontent.com/u/1234567" alt="Ngoue David" style="width: 150px; height: 150px; border-radius: 50%;">
308
+ <h4>Ngoue David</h4>
309
+ <p>🎓 Master 2 IA & Big Data</p>
310
+ <p>📧 <a href="mailto:ngouedavidrogeryannick@gmail.com">ngouedavidrogeryannick@gmail.com</a></p>
311
+ <p>🌐 <a href="https://github.com/TheBeyonder237" target="_blank">Profil GitHub</a></p>
312
+ </div>
313
+ </div>
314
+ """, unsafe_allow_html=True)
best_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:de6dee944d3a170d289e2aecffadb1453a416b486ef88c586f21a24d11a35b4d
3
+ size 13570137
requirements.txt ADDED
Binary file (262 Bytes). View file