SergioI1991 commited on
Commit
737d563
·
verified ·
1 Parent(s): 446b8e6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -44
app.py CHANGED
@@ -1,28 +1,38 @@
1
  import gradio as gr
2
  import tensorflow as tf
3
  import numpy as np
 
4
 
5
  # --- Configuración ---
6
  IMG_SIZE = (224, 224)
7
  MODEL_PATH = "dental_classifier_model.keras" # Asegúrate de que esta ruta sea correcta
8
  CLASS_NAMES = ['no_valido', 'valido']
9
 
10
- # --- Cargar Modelo ---
11
- # Manejo de errores básico para la carga del modelo
 
 
12
  try:
 
 
13
  model = tf.keras.models.load_model(MODEL_PATH)
 
14
  print("Modelo cargado exitosamente.")
15
  except Exception as e:
16
- print(f"Error cargando el modelo desde {MODEL_PATH}: {e}")
17
- print("Asegúrate de que el archivo 'dental_classifier_model.keras' esté en el mismo directorio.")
18
- # Salir o manejar el error como prefieras si el modelo no se carga
19
- # Por ahora, Gradio mostrará un error si 'model' no está definido.
20
 
21
  # --- Funciones de Procesamiento ---
22
  def preprocess_image(img):
23
  """Preprocesa la imagen de entrada al formato que espera el modelo."""
24
  if img is None:
25
  return None
 
 
 
 
 
 
26
  img = tf.image.resize(img, IMG_SIZE)
27
  img_array = tf.expand_dims(img, 0)
28
  img_array = img_array / 255.0 # Normalizar
@@ -30,16 +40,22 @@ def preprocess_image(img):
30
 
31
  def predecir(rx_image):
32
  """Realiza la predicción y formatea la salida HTML."""
 
 
 
33
  if rx_image is None:
34
- return "<div style='color:red; text-align:center; padding-top:100px;'>Por favor, sube una imagen primero.</div>"
35
 
36
  img_array = preprocess_image(rx_image)
37
-
 
 
 
38
  try:
39
  preds = model.predict(img_array)
40
  except Exception as e:
41
  print(f"Error durante la predicción: {e}")
42
- return f"<div style='color:red; text-align:center; padding-top:100px;'>Error al procesar el modelo: {e}</div>"
43
 
44
  score = tf.nn.softmax(preds[0])
45
 
@@ -51,32 +67,25 @@ def predecir(rx_image):
51
  other_class = CLASS_NAMES[other_index]
52
  other_confidence = score[other_index] * 100
53
 
54
- # --- Modificación de Estilo ---
55
- # El color del borde sigue siendo dinámico (verde/rojo)
56
- color_borde = "#4CAF50" if predicted_class == "valido" else "#FF0000"
57
- # El color del texto ahora es siempre negro (#000000)
58
- color_texto_resultado = "#000000"
59
 
60
- # HTML para el resultado
61
  resultado_texto = f"""
62
- <div style='
63
- font-size:36px;
64
- text-align:center;
65
- padding:40px;
66
- height:350px;
67
- border: 3px solid {color_borde}; /* Borde dinámico */
68
- border-radius:25px;
69
- background-color:#ffffff;
70
- display:flex;
71
- flex-direction:column;
72
- justify-content:center;
73
- color:{color_texto_resultado}; /* Texto siempre negro */
74
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
75
  '>
76
- <div>Resultado: <b>{predicted_class.upper()}</b></div>
77
- <div>Confianza: {confidence:.2f}%</div>
78
- <div style='font-size: 24px; margin-top: 15px;'>
79
- (Probabilidad {other_class}: {other_confidence:.2f}%)
 
 
 
 
80
  </div>
81
  </div>
82
  """
@@ -84,27 +93,124 @@ def predecir(rx_image):
84
  return resultado_texto
85
 
86
  # --- Interfaz de Gradio ---
87
- with gr.Blocks(theme="default") as demo:
88
- # Forzar fondo claro en toda la app
89
- gr.HTML("<style>body{background-color:#ffffff !important;}</style>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  gr.Markdown("## Clasificador RX LAB 🦷 V1(529NV-348V) TFG Marta B.")
 
 
 
 
 
 
 
 
 
 
92
 
93
- with gr.Row():
94
- with gr.Column(scale=1):
95
- rx_input = gr.Image(type="numpy", label="Sube tu RX")
 
96
 
97
  with gr.Row():
98
- boton_limpiar = gr.Button("Limpiar", variant="secondary")
99
- boton_analizar = gr.Button("Analizar", variant="primary")
100
 
101
- with gr.Column(scale=1):
102
- resultado = gr.HTML(label="Resultado")
 
103
 
104
  # --- Conexiones de Eventos ---
105
  boton_analizar.click(fn=predecir, inputs=rx_input, outputs=resultado)
106
- boton_limpiar.click(lambda: (None, None), inputs=[], outputs=[rx_input, resultado])
107
 
108
  # --- Lanzar la App ---
109
  if __name__ == "__main__":
110
- demo.launch()
 
1
  import gradio as gr
2
  import tensorflow as tf
3
  import numpy as np
4
+ import time # Para simular un retraso en la carga del modelo
5
 
6
  # --- Configuración ---
7
  IMG_SIZE = (224, 224)
8
  MODEL_PATH = "dental_classifier_model.keras" # Asegúrate de que esta ruta sea correcta
9
  CLASS_NAMES = ['no_valido', 'valido']
10
 
11
+ # --- Cargar Modelo con mensaje de carga ---
12
+ model = None
13
+ model_load_message = "Cargando modelo... por favor espera."
14
+
15
  try:
16
+ # Simular una carga lenta para ver el mensaje (puedes quitar esto en producción)
17
+ time.sleep(2)
18
  model = tf.keras.models.load_model(MODEL_PATH)
19
+ model_load_message = "Modelo cargado exitosamente."
20
  print("Modelo cargado exitosamente.")
21
  except Exception as e:
22
+ model_load_message = f"Error cargando el modelo: {e}. Asegúrate que 'dental_classifier_model.keras' existe."
23
+ print(model_load_message)
 
 
24
 
25
  # --- Funciones de Procesamiento ---
26
  def preprocess_image(img):
27
  """Preprocesa la imagen de entrada al formato que espera el modelo."""
28
  if img is None:
29
  return None
30
+ # Asegurarse de que la imagen tiene 3 canales si es en escala de grises
31
+ if len(img.shape) == 2:
32
+ img = np.stack((img,)*3, axis=-1)
33
+ elif img.shape[2] == 4: #RGBA a RGB
34
+ img = img[:, :, :3]
35
+
36
  img = tf.image.resize(img, IMG_SIZE)
37
  img_array = tf.expand_dims(img, 0)
38
  img_array = img_array / 255.0 # Normalizar
 
40
 
41
  def predecir(rx_image):
42
  """Realiza la predicción y formatea la salida HTML."""
43
+ if model is None:
44
+ return "<div style='color:#FF6B6B; text-align:center; padding-top:100px; font-weight: bold;'>Error: El modelo no se ha cargado.</div>"
45
+
46
  if rx_image is None:
47
+ return "<div style='color:#FF6B6B; text-align:center; padding-top:100px; font-weight: bold;'>Por favor, sube una imagen RX para analizar.</div>"
48
 
49
  img_array = preprocess_image(rx_image)
50
+ if img_array is None: # Si preprocess_image devuelve None por algún motivo
51
+ return "<div style='color:#FF6B6B; text-align:center; padding-top:100px; font-weight: bold;'>Error: No se pudo procesar la imagen.</div>"
52
+
53
+
54
  try:
55
  preds = model.predict(img_array)
56
  except Exception as e:
57
  print(f"Error durante la predicción: {e}")
58
+ return f"<div style='color:#FF6B6B; text-align:center; padding-top:100px; font-weight: bold;'>Error al realizar la predicción: {e}</div>"
59
 
60
  score = tf.nn.softmax(preds[0])
61
 
 
67
  other_class = CLASS_NAMES[other_index]
68
  other_confidence = score[other_index] * 100
69
 
70
+ # Colores para el borde y el texto del resultado
71
+ color_borde = "#4CAF50" if predicted_class == "valido" else "#FF6B6B" # Verde para válido, Rojo para no válido
72
+ color_texto_principal = "#2C3E50" # Azul oscuro para el texto principal
73
+ color_texto_secundario = "#555555" # Gris oscuro para el texto secundario
 
74
 
75
+ # HTML para el resultado con estilos mejorados
76
  resultado_texto = f"""
77
+ <div class='result-box' style='
78
+ border: 3px solid {color_borde};
79
+ box-shadow: 0 6px 20px rgba(0,0,0,0.15); /* Sombra más pronunciada */
 
 
 
 
 
 
 
 
 
 
80
  '>
81
+ <div style='color:{color_texto_principal}; font-size:42px; font-weight:bold;'>
82
+ Resultado: <span style='color: {color_borde};'>{predicted_class.upper()}</span>
83
+ </div>
84
+ <div style='color:{color_texto_secundario}; font-size:30px; margin-top:15px;'>
85
+ Confianza: <span style='font-weight:bold;'>{confidence:.2f}%</span>
86
+ </div>
87
+ <div style='color:{color_texto_secundario}; font-size:24px; margin-top:10px;'>
88
+ (Probabilidad {other_class}: <span style='font-weight:bold;'>{other_confidence:.2f}%</span>)
89
  </div>
90
  </div>
91
  """
 
93
  return resultado_texto
94
 
95
  # --- Interfaz de Gradio ---
96
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue=gr.themes.colors.emerald, secondary_hue=gr.themes.colors.slate)) as demo:
97
+ # --- Estilos CSS personalizados ---
98
+ gr.HTML(f"""
99
+ <style>
100
+ body {{
101
+ background-color:#f8f9fa !important;
102
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
103
+ color: #333;
104
+ }}
105
+ .gradio-container {{
106
+ max-width: 1200px;
107
+ margin: auto;
108
+ padding: 20px;
109
+ background-color: #ffffff;
110
+ border-radius: 15px;
111
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
112
+ }}
113
+ h2 {{
114
+ color: #2C3E50; /* Azul oscuro para el título */
115
+ font-size: 2.8em; /* Tamaño más grande */
116
+ font-weight: 700; /* Más negrita */
117
+ text-align: center;
118
+ margin-bottom: 30px;
119
+ padding-bottom: 15px;
120
+ border-bottom: 2px solid #ECF0F1;
121
+ }}
122
+ .gr-button.primary {{
123
+ background-color: #28a745 !important; /* Verde principal */
124
+ border-color: #28a745 !important;
125
+ color: white !important;
126
+ font-weight: bold;
127
+ padding: 12px 25px;
128
+ font-size: 1.1em;
129
+ border-radius: 8px;
130
+ transition: all 0.3s ease;
131
+ }}
132
+ .gr-button.primary:hover {{
133
+ background-color: #218838 !important;
134
+ border-color: #1e7e34 !important;
135
+ transform: translateY(-2px);
136
+ box-shadow: 0 4px 10px rgba(0,0,0,0.2);
137
+ }}
138
+ .gr-button.secondary {{
139
+ background-color: #6c757d !important; /* Gris secundario */
140
+ border-color: #6c757d !important;
141
+ color: white !important;
142
+ font-weight: bold;
143
+ padding: 12px 25px;
144
+ font-size: 1.1em;
145
+ border-radius: 8px;
146
+ transition: all 0.3s ease;
147
+ }}
148
+ .gr-button.secondary:hover {{
149
+ background-color: #5a6268 !important;
150
+ border-color: #545b62 !important;
151
+ transform: translateY(-2px);
152
+ box-shadow: 0 4px 10px rgba(0,0,0,0.2);
153
+ }}
154
+ .result-box {{
155
+ font-size: 28px;
156
+ text-align: center;
157
+ padding: 50px;
158
+ min-height: 380px;
159
+ border-radius: 20px;
160
+ background-color: #f0f4f7; /* Un fondo ligeramente azulado */
161
+ display: flex;
162
+ flex-direction: column;
163
+ justify-content: center;
164
+ color: #333;
165
+ transition: all 0.3s ease;
166
+ }}
167
+ .result-box:hover {{
168
+ transform: translateY(-5px);
169
+ box-shadow: 0 8px 25px rgba(0,0,0,0.2);
170
+ }}
171
+ .gr-image {{
172
+ border-radius: 12px;
173
+ border: 1px solid #ddd;
174
+ box-shadow: 0 2px 10px rgba(0,0,0,0.08);
175
+ }}
176
+ .gr-label {{
177
+ font-weight: 600;
178
+ color: #34495E;
179
+ font-size: 1.2em;
180
+ margin-bottom: 8px;
181
+ }}
182
+ </style>
183
+ """)
184
 
185
  gr.Markdown("## Clasificador RX LAB 🦷 V1(529NV-348V) TFG Marta B.")
186
+ gr.Markdown("<p style='text-align:center; font-size:1.1em; color:#555;'>Sube una imagen para clasificarla como válida o no válida.</p>")
187
+
188
+ # Mensaje de carga del modelo
189
+ gr.Textbox(value=model_load_message, interactive=False, container=False,
190
+ show_label=False, elem_id="model_status_message",
191
+ label="Estado del Modelo",
192
+ render=True, # Asegura que se renderiza inicialmente
193
+ info="El modelo está cargando..." if "Cargando" in model_load_message else None,
194
+ visible=True if "Cargando" in model_load_message or "Error" in model_load_message else False,
195
+ )
196
 
197
+ with gr.Row(variant="panel", scale=1):
198
+ with gr.Column(scale=1, min_width=400):
199
+ gr.Markdown("### Sube tu Radiografía")
200
+ rx_input = gr.Image(type="numpy", label="Imagen de Radiografía Dental", show_label=True, height=450)
201
 
202
  with gr.Row():
203
+ boton_limpiar = gr.Button("Limpiar", variant="secondary", size="lg", icon="🗑️")
204
+ boton_analizar = gr.Button("Analizar RX", variant="primary", size="lg", icon="🔍")
205
 
206
+ with gr.Column(scale=1, min_width=400):
207
+ gr.Markdown("### Resultado del Análisis")
208
+ resultado = gr.HTML(label="Análisis de Radiografía", show_label=True, value="<div class='result-box'><div style='color:#555; font-size:24px;'>Esperando imagen...</div></div>")
209
 
210
  # --- Conexiones de Eventos ---
211
  boton_analizar.click(fn=predecir, inputs=rx_input, outputs=resultado)
212
+ boton_limpiar.click(lambda: (None, "<div class='result-box'><div style='color:#555; font-size:24px;'>Esperando imagen...</div></div>"), inputs=[], outputs=[rx_input, resultado])
213
 
214
  # --- Lanzar la App ---
215
  if __name__ == "__main__":
216
+ demo.launch(share=False)