Update app.py
Browse files
app.py
CHANGED
@@ -32,23 +32,15 @@ from tensorflow.keras.applications.densenet import preprocess_input as preproces
|
|
32 |
|
33 |
# ---- Fonctions utilitaires robustes pour les modèles Keras ----
|
34 |
def get_primary_input_name(model):
|
35 |
-
"""Retourne le nom de la couche d'input principale du modèle."""
|
36 |
-
# ===== LA CORRECTION FINALE ET DÉFINITIVE EST ICI =====
|
37 |
-
# L'attribut correct est `model.inputs`, qui est une liste de tenseurs.
|
38 |
-
# On prend le premier tenseur de la liste et on récupère son nom.
|
39 |
if isinstance(model.inputs, list) and len(model.inputs) > 0:
|
40 |
-
# Le nom est souvent de la forme "input_layer:0", on ne garde que "input_layer"
|
41 |
return model.inputs[0].name.split(':')[0]
|
42 |
-
# Fallback au cas où, mais ne devrait pas être nécessaire
|
43 |
return "input_1"
|
44 |
-
# =======================================================
|
45 |
|
46 |
def safe_forward(model, x):
|
47 |
-
"""Appelle un modèle en utilisant le nom d'input correct pour éviter les UserWarnings."""
|
48 |
input_name = get_primary_input_name(model)
|
49 |
return model({input_name: x}, training=False).numpy()
|
50 |
|
51 |
-
# ---- Prédiction
|
52 |
def predict_single(image_pil, weights=(0.45, 0.25, 0.30)):
|
53 |
img_np = np.array(image_pil)
|
54 |
img_299 = cv2.resize(img_np, (299, 299))
|
@@ -68,7 +60,7 @@ def predict_single(image_pil, weights=(0.45, 0.25, 0.30)):
|
|
68 |
return preds[0]
|
69 |
|
70 |
|
71 |
-
# ---- Grad-CAM
|
72 |
def make_gradcam(image_pil, model, last_conv_layer_name="conv5_block32_concat", class_index=None):
|
73 |
img_np = np.array(image_pil)
|
74 |
img_resized = cv2.resize(img_np, (224, 224))
|
@@ -81,28 +73,30 @@ def make_gradcam(image_pil, model, last_conv_layer_name="conv5_block32_concat",
|
|
81 |
|
82 |
with tf.GradientTape() as tape:
|
83 |
last_conv_layer_output, preds = grad_model(input_for_model, training=False)
|
84 |
-
|
85 |
if isinstance(preds, list):
|
86 |
preds = preds[0]
|
87 |
-
|
88 |
if class_index is None:
|
89 |
class_index = tf.argmax(preds[0])
|
90 |
class_channel = preds[:, class_index]
|
91 |
|
92 |
grads = tape.gradient(class_channel, last_conv_layer_output)
|
93 |
|
94 |
-
|
95 |
-
|
|
|
96 |
|
|
|
97 |
heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
|
98 |
heatmap = tf.squeeze(heatmap)
|
99 |
heatmap = tf.maximum(heatmap, 0) / (tf.math.reduce_max(heatmap) + 1e-8)
|
100 |
heatmap = heatmap.numpy()
|
101 |
|
102 |
heatmap = np.uint8(255 * heatmap)
|
|
|
103 |
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
|
104 |
|
105 |
img_bgr = cv2.cvtColor(img_resized, cv2.COLOR_RGB2BGR)
|
|
|
106 |
superimposed_img = cv2.addWeighted(img_bgr, 0.6, heatmap, 0.4, 0)
|
107 |
|
108 |
return cv2.cvtColor(superimposed_img, cv2.COLOR_BGR2RGB)
|
@@ -113,7 +107,6 @@ def gradio_predict(image_pil):
|
|
113 |
return "Veuillez uploader une image.", None, None
|
114 |
try:
|
115 |
probs = predict_single(image_pil)
|
116 |
-
|
117 |
benign_prob = sum(probs[i] for i, cls in enumerate(CLASS_NAMES) if diagnosis_map[cls] == "Bénin")
|
118 |
malign_prob = sum(probs[i] for i, cls in enumerate(CLASS_NAMES) if diagnosis_map[cls] == "Malin")
|
119 |
global_diag = "Bénin" if benign_prob >= malign_prob else "Malin"
|
@@ -136,7 +129,6 @@ def gradio_predict(image_pil):
|
|
136 |
|
137 |
# ---- Gradio UI ----
|
138 |
example_paths = ["exemple1.jpg", "exemple2.jpg", "exemple3.jpg"]
|
139 |
-
|
140 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
141 |
gr.Markdown("# Analyse de lésions cutanées (Ensemble de modèles + Grad-CAM)")
|
142 |
gr.Markdown("Cet outil propose une prédiction de la nature de la lésion (Bénin/Malin) avec explication visuelle.")
|
|
|
32 |
|
33 |
# ---- Fonctions utilitaires robustes pour les modèles Keras ----
|
34 |
def get_primary_input_name(model):
|
|
|
|
|
|
|
|
|
35 |
if isinstance(model.inputs, list) and len(model.inputs) > 0:
|
|
|
36 |
return model.inputs[0].name.split(':')[0]
|
|
|
37 |
return "input_1"
|
|
|
38 |
|
39 |
def safe_forward(model, x):
|
|
|
40 |
input_name = get_primary_input_name(model)
|
41 |
return model({input_name: x}, training=False).numpy()
|
42 |
|
43 |
+
# ---- Prédiction ----
|
44 |
def predict_single(image_pil, weights=(0.45, 0.25, 0.30)):
|
45 |
img_np = np.array(image_pil)
|
46 |
img_299 = cv2.resize(img_np, (299, 299))
|
|
|
60 |
return preds[0]
|
61 |
|
62 |
|
63 |
+
# ---- Grad-CAM ----
|
64 |
def make_gradcam(image_pil, model, last_conv_layer_name="conv5_block32_concat", class_index=None):
|
65 |
img_np = np.array(image_pil)
|
66 |
img_resized = cv2.resize(img_np, (224, 224))
|
|
|
73 |
|
74 |
with tf.GradientTape() as tape:
|
75 |
last_conv_layer_output, preds = grad_model(input_for_model, training=False)
|
|
|
76 |
if isinstance(preds, list):
|
77 |
preds = preds[0]
|
|
|
78 |
if class_index is None:
|
79 |
class_index = tf.argmax(preds[0])
|
80 |
class_channel = preds[:, class_index]
|
81 |
|
82 |
grads = tape.gradient(class_channel, last_conv_layer_output)
|
83 |
|
84 |
+
# ===== LA CORRECTION FINALE EST ICI : on ne moyenne que sur les axes spatiaux =====
|
85 |
+
pooled_grads = tf.reduce_mean(grads, axis=(0, 1))
|
86 |
+
# ===================================================================================
|
87 |
|
88 |
+
last_conv_layer_output = last_conv_layer_output[0]
|
89 |
heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
|
90 |
heatmap = tf.squeeze(heatmap)
|
91 |
heatmap = tf.maximum(heatmap, 0) / (tf.math.reduce_max(heatmap) + 1e-8)
|
92 |
heatmap = heatmap.numpy()
|
93 |
|
94 |
heatmap = np.uint8(255 * heatmap)
|
95 |
+
# applyColorMap prend une image 1 canal et retourne une image 3 canaux (BGR)
|
96 |
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
|
97 |
|
98 |
img_bgr = cv2.cvtColor(img_resized, cv2.COLOR_RGB2BGR)
|
99 |
+
# Les deux images ont maintenant la même taille (224, 224) et 3 canaux.
|
100 |
superimposed_img = cv2.addWeighted(img_bgr, 0.6, heatmap, 0.4, 0)
|
101 |
|
102 |
return cv2.cvtColor(superimposed_img, cv2.COLOR_BGR2RGB)
|
|
|
107 |
return "Veuillez uploader une image.", None, None
|
108 |
try:
|
109 |
probs = predict_single(image_pil)
|
|
|
110 |
benign_prob = sum(probs[i] for i, cls in enumerate(CLASS_NAMES) if diagnosis_map[cls] == "Bénin")
|
111 |
malign_prob = sum(probs[i] for i, cls in enumerate(CLASS_NAMES) if diagnosis_map[cls] == "Malin")
|
112 |
global_diag = "Bénin" if benign_prob >= malign_prob else "Malin"
|
|
|
129 |
|
130 |
# ---- Gradio UI ----
|
131 |
example_paths = ["exemple1.jpg", "exemple2.jpg", "exemple3.jpg"]
|
|
|
132 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
133 |
gr.Markdown("# Analyse de lésions cutanées (Ensemble de modèles + Grad-CAM)")
|
134 |
gr.Markdown("Cet outil propose une prédiction de la nature de la lésion (Bénin/Malin) avec explication visuelle.")
|