import gradio as gr import tensorflow as tf import numpy as np import cv2 from huggingface_hub import hf_hub_download from tensorflow.keras.preprocessing import image # === Importar funciones del pipeline === from preprocessing.zoom import apply_zoom from preprocessing.hair_removal import quitar_pelos from preprocessing.segmentation import segmentar_lesion from preprocessing.metrics import ( calcular_area, calcular_perimetro, calcular_circularidad, calcular_simetria ) # Tamaño de entrada del modelo ROWS, COLS = 224, 224 # === Cargar modelo desde Hugging Face Hub === model_path = hf_hub_download(repo_id="Martinagg/simpleNet", filename="simpleNet.h5") model = tf.keras.models.load_model(model_path) # === Cargar modelo ZoomNet para Grad-CAM === zoom_path = hf_hub_download(repo_id="Martinagg/ZoomNet", filename="ZoomNet.keras") model_zoom = tf.keras.models.load_model(zoom_path) # === Función Grad-CAM === def make_gradcam_heatmap(img_array, model, last_conv_layer_name="conv4", pred_index=None): grad_model = tf.keras.models.Model( [model.inputs], [model.get_layer(last_conv_layer_name).output, model.output] ) with tf.GradientTape() as tape: conv_outputs, predictions = grad_model(img_array) if isinstance(predictions, list): predictions = predictions[0] if pred_index is None: pred_index = tf.argmax(predictions[0]) class_channel = predictions[:, pred_index] grads = tape.gradient(class_channel, conv_outputs) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) conv_outputs = conv_outputs[0] heatmap = conv_outputs @ pooled_grads[..., tf.newaxis] heatmap = tf.squeeze(heatmap) heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) return heatmap.numpy() # === Función principal === def preprocess_and_predict(img_input): img = np.array(img_input)[:, :, ::-1] zoomed = apply_zoom(img, zoom_factor=0.9) rgb_clean = cv2.cvtColor(zoomed, cv2.COLOR_BGR2RGB) clean = quitar_pelos(rgb_clean) mask, lesion_rgb = segmentar_lesion(clean, size=(ROWS, COLS)) lesion_resized = cv2.resize(lesion_rgb, (ROWS, COLS)) img_array = image.img_to_array(lesion_resized) / 255.0 img_array = np.expand_dims(img_array, axis=0) probs = model.predict(img_array)[0] classes = ["Benign", "Malignant"] pred_idx = np.argmax(probs) pred_label = classes[pred_idx] prob_percent = int(probs[pred_idx] * 100) # Color dinámico para la barra color = "green" if pred_label == "Benign" else "red" result_text_html = f"
Una herramienta de apoyo basada en IA para la detección temprana de cáncer de piel.
El melanoma es un cáncer de piel agresivo que se origina en los melanocitos. Aunque poco frecuente, es el más peligroso por su capacidad de generar metástasis si no se detecta a tiempo.
En esta aplicación hemos implementado una red neuronal convolucional (CNN) entrenada con imágenes dermatoscópicas para estimar la probabilidad de que una lesión sea benigna o maligna.
Además, incorporamos técnicas de interpretabilidad como Grad-CAM y métricas geométricas basadas en el criterio clínico ABCDE, que sirven como apoyo en la exploración médica.
Sube una imagen clara de tu lunar Asegúrate de que esté bien iluminada y centrada.
") img_input = gr.Image(type="pil", label="Imagen de la lesión", elem_id="upload-img") run_btn = gr.Button("Analizar", elem_id="analyze-btn", scale=0) # === Segmentación === with gr.Column(elem_classes="section"): gr.HTML("En este apartado verás cómo preparamos tu imagen para que la red neuronal se concentre en la lesión:
El Grad-CAM resalta las zonas en las que la red neuronal ha puesto mayor atención para clasificar la lesión. Los colores cálidos indican mayor relevancia: rojo y amarillo muestran las áreas más importantes.
""") gradcam_img = gr.Image(type="numpy", label="Mapa de activación", elem_id="gradcam-img") gr.HTML("""Asimetría
Bordes irregulares
Color variado
Diámetro > 6mm
Evolución
Estas características aumentan la probabilidad de melanoma. Consulte a un dermatólogo si observa alguna.
Métricas lunar para consultar en casos
""") metrics_table = gr.Dataframe( headers=["Métrica", "Valor"], datatype=["str", "number"], interactive=False, label="Métricas calculadas", wrap=True, row_count=(5, "fixed"), col_count=(2, "fixed") ) # === Aviso final === gr.HTML("""Este sistema es solo de apoyo y nunca sustituye la valoración de un experto médico.