Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| import cv2 | |
| from tensorflow.keras.models import load_model | |
| import tensorflow as tf | |
| from tensorflow import keras | |
| # Cargar el modelo entrenado | |
| MODEL_PATH = 'modelo_isatron_jeysshonl.h5' | |
| model = load_model(MODEL_PATH) | |
| # Función para encontrar la última capa convolucional | |
| def find_last_conv_layer(model): | |
| """Encuentra la última capa convolucional en el modelo""" | |
| for layer in reversed(model.layers): | |
| if 'conv' in layer.name.lower(): | |
| return layer.name | |
| raise ValueError("No se encontró una capa convolucional en el modelo.") | |
| # Obtener el nombre de la última capa convolucional | |
| try: | |
| last_conv_layer_name = find_last_conv_layer(model) | |
| print(f"Última capa convolucional encontrada: {last_conv_layer_name}") | |
| except ValueError as e: | |
| print(f"Advertencia: {e}") | |
| last_conv_layer_name = None | |
| # Definir tamaño de imagen (tu modelo usa 224x224) | |
| IMG_SIZE = 224 | |
| def load_and_preprocess_image(img): | |
| """Preprocesa la imagen para el modelo""" | |
| # Convertir imagen de Gradio (PIL Image) a array numpy | |
| img = np.array(img) | |
| # Si la imagen es RGB, convertir a escala de grises si el modelo lo requiere | |
| # Tu modelo parece usar RGB (224, 224, 3), así que mantenemos RGB | |
| if len(img.shape) == 2: # Si es escala de grises | |
| img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) | |
| # Redimensionar imagen al tamaño requerido | |
| img = cv2.resize(img, (IMG_SIZE, IMG_SIZE)) | |
| # Normalizar imagen | |
| img = img / 255.0 | |
| # Expandir dimensiones para batch | |
| img = np.expand_dims(img, axis=0) | |
| return img | |
| def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None): | |
| """Genera un mapa de calor Grad-CAM""" | |
| if last_conv_layer_name is None: | |
| return None | |
| try: | |
| # Crear un modelo que mapee la imagen de entrada a las activaciones | |
| grad_model = keras.models.Model( | |
| [model.inputs], | |
| [model.get_layer(last_conv_layer_name).output, model.output] | |
| ) | |
| # Calcular el gradiente de la clase predicha | |
| with tf.GradientTape() as tape: | |
| last_conv_layer_output, preds = grad_model(img_array) | |
| if pred_index is None: | |
| pred_index = np.argmax(preds[0]) | |
| class_channel = preds[:, pred_index] | |
| # Calcular los gradientes | |
| grads = tape.gradient(class_channel, last_conv_layer_output) | |
| # Pooling global de los gradientes | |
| pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) | |
| # Multiplicar cada canal por su importancia | |
| last_conv_layer_output = last_conv_layer_output[0] | |
| heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis] | |
| heatmap = tf.squeeze(heatmap) | |
| # Normalizar el mapa de calor entre 0 y 1 | |
| heatmap = tf.maximum(heatmap, 0) / tf.reduce_max(heatmap) | |
| heatmap = heatmap.numpy() | |
| return heatmap | |
| except Exception as e: | |
| print(f"Error generando Grad-CAM: {e}") | |
| return None | |
| def overlay_heatmap(heatmap, img, alpha=0.4): | |
| """Superpone el mapa de calor en la imagen original""" | |
| # Redimensionar mapa de calor al tamaño de la imagen | |
| heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) | |
| # Convertir mapa de calor a RGB | |
| heatmap = np.uint8(255 * heatmap) | |
| heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) | |
| # Asegurar que img esté en formato correcto | |
| if len(img.shape) == 2: # Escala de grises | |
| img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) | |
| elif img.shape[2] == 3: # RGB | |
| img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) | |
| # Aplicar mapa de calor a la imagen original | |
| overlayed_img = heatmap * alpha + img | |
| overlayed_img = np.uint8(overlayed_img) | |
| # Convertir de nuevo a RGB para Gradio | |
| overlayed_img = cv2.cvtColor(overlayed_img, cv2.COLOR_BGR2RGB) | |
| return overlayed_img | |
| def image_classifier(img): | |
| """Función principal de clasificación con Grad-CAM""" | |
| # Mantener la imagen original para superponer | |
| orig_img = np.array(img) | |
| # Preprocesar la imagen | |
| img_array = load_and_preprocess_image(img) | |
| # Realizar predicción | |
| preds = model.predict(img_array, verbose=0) | |
| # Tu modelo devuelve [Normal, Neumonía] | |
| normal_prob = float(preds[0][0]) | |
| pneumonia_prob = float(preds[0][1]) | |
| # Determinar el índice de la clase predicha | |
| pred_index = np.argmax(preds[0]) | |
| # Generar mapa de calor si es posible | |
| if last_conv_layer_name: | |
| heatmap = make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=pred_index) | |
| if heatmap is not None: | |
| overlayed_img = overlay_heatmap(heatmap, orig_img) | |
| else: | |
| overlayed_img = orig_img | |
| else: | |
| overlayed_img = orig_img | |
| # Preparar resultado de predicción | |
| prediction_result = { | |
| 'NORMAL': normal_prob, | |
| 'NEUMONÍA': pneumonia_prob | |
| } | |
| return overlayed_img, prediction_result | |
| # Crear interfaz Gradio mejorada | |
| demo = gr.Interface( | |
| fn=image_classifier, | |
| inputs=gr.Image(type="pil", label="Subir imagen de rayos X"), | |
| outputs=[ | |
| gr.Image(type="numpy", label="Imagen con Mapa de Calor (Grad-CAM)"), | |
| gr.Label(label="Predicción", num_top_classes=2) | |
| ], | |
| title="<h1 style='text-align: center;'>IsaTron V2: Herramienta de Apoyo al Diagnóstico de Neumonía</h1>", | |
| description=""" | |
| <div style='text-align: justify;'> | |
| IsaTron es una herramienta de inteligencia artificial desarrollada con redes neuronales convolucionales (CNN) | |
| para apoyar el diagnóstico de neumonía pediátrica a partir de imágenes de rayos X de tórax. La IA analiza las | |
| imágenes y produce un mapa de calor Grad-CAM que resalta las áreas de mayor relevancia para la predicción del | |
| modelo, lo cual ayuda a los profesionales de la salud a visualizar mejor las zonas potencialmente afectadas. | |
| </div> | |
| <br> | |
| <div style='text-align: justify;'> | |
| <strong>⚠️ Advertencia Importante:</strong> IsaTron está diseñado exclusivamente como una herramienta de apoyo | |
| al diagnóstico y NO reemplaza una evaluación médica profesional. Es crucial que los resultados generados por | |
| esta herramienta sean interpretados por personal de salud calificado. Esta herramienta no debe utilizarse para | |
| tomar decisiones clínicas sin la supervisión de un médico especialista. | |
| </div> | |
| """, | |
| examples=[ | |
| ['1normal.jpeg'], | |
| ['image1_pneumonia_virus.jpeg'], | |
| ['image1_pneumonia_bacteria.jpeg'], | |
| ['image2_normal.jpeg'], | |
| ['image2_pneumonia_bacteria.jpeg'], | |
| ['image3_normal.jpeg'], | |
| ['image4_normal.jpeg'] | |
| ], | |
| article=""" | |
| <div style='text-align: justify;'> | |
| <h3>Sobre IsaTron</h3> | |
| <p> | |
| Este proyecto utiliza tecnologías avanzadas de inteligencia artificial, incluyendo redes neuronales | |
| convolucionales (CNN) y Grad-CAM (Gradient-weighted Class Activation Mapping), para mejorar la | |
| interpretabilidad de los resultados. IsaTron ha sido entrenado con imágenes médicas de rayos X de tórax | |
| y es capaz de predecir neumonía con un alto grado de confianza. | |
| </p> | |
| <p> | |
| <strong>Grad-CAM</strong> permite visualizar qué regiones de la imagen son más importantes para la decisión | |
| del modelo, proporcionando transparencia en el proceso de clasificación y ayudando a los profesionales | |
| médicos a comprender mejor el razonamiento de la IA. | |
| </p> | |
| <p> | |
| Los resultados obtenidos deben ser confirmados por un médico especialista para realizar un diagnóstico | |
| clínico adecuado. Para más información sobre el proyecto, visite: | |
| <a href="https://repositorio.unbosque.edu.co/handle/20.500.12495/9514" target="_blank"> | |
| Repositorio Institucional Universidad El Bosque | |
| </a> | |
| </p> | |
| </div> | |
| <br> | |
| <div style='text-align: center;'> | |
| <p><strong>IsaTron V2 - Desarrollado por Jeysshon Bustos</strong></p> | |
| <p>Universidad El Bosque © 2022-2024</p> | |
| </div> | |
| """, | |
| theme=gr.themes.Soft(), | |
| allow_flagging="never" | |
| ) | |
| # Ejecutar la interfaz | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |