LoloSemper commited on
Commit
1ccdf58
·
verified ·
1 Parent(s): 1419aec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -126
app.py CHANGED
@@ -333,9 +333,11 @@ def create_probability_chart(predictions, consensus_class):
333
  if predictions:
334
  # Obtener probabilidades promedio
335
  avg_probs = np.zeros(7)
336
- for pred in predictions:
 
 
337
  avg_probs += pred['probabilities']
338
- avg_probs /= len(predictions)
339
 
340
  colors = ['#ff6b35' if i in MALIGNANT_INDICES else '#44ff44' for i in range(7)]
341
  bars = ax1.bar(range(7), avg_probs, color=colors, alpha=0.8)
@@ -360,16 +362,17 @@ def create_probability_chart(predictions, consensus_class):
360
  f'{height:.2%}', ha='center', va='bottom', fontsize=9)
361
 
362
  # Gráfico 2: Confianza por modelo
363
- model_names = [pred['model'].split(' ')[1] if len(pred['model'].split(' ')) > 1 else pred['model'] for pred in predictions]
364
- confidences = [pred['confidence'] for pred in predictions]
 
365
 
366
- colors_conf = ['#ff6b35' if pred['is_malignant'] else '#44ff44' for pred in predictions]
367
- bars2 = ax2.bar(range(len(predictions)), confidences, color=colors_conf, alpha=0.8)
368
 
369
  ax2.set_xlabel('Modelos')
370
  ax2.set_ylabel('Confianza')
371
  ax2.set_title('🎯 Confianza por Modelo')
372
- ax2.set_xticks(range(len(predictions)))
373
  ax2.set_xticklabels(model_names, rotation=45)
374
  ax2.grid(True, alpha=0.3)
375
  ax2.set_ylim(0, 1)
@@ -394,8 +397,70 @@ def create_probability_chart(predictions, consensus_class):
394
  except Exception as e:
395
  print(f"Error creando gráfico: {e}")
396
  return "<p>❌ Error generando gráfico de probabilidades</p>"
397
-
 
 
398
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  # Convertir a RGB si es necesario
400
  if img.mode != 'RGB':
401
  img = img.convert('RGB')
@@ -404,9 +469,10 @@ def create_probability_chart(predictions, consensus_class):
404
 
405
  # Obtener predicciones de todos los modelos cargados
406
  for model_name, model_data in loaded_models.items():
407
- pred = predict_with_model(img, model_data)
408
- if pred.get('success', False):
409
- predictions.append(pred)
 
410
 
411
  if not predictions:
412
  return "<h3>❌ Error</h3><p>No se pudieron obtener predicciones de ningún modelo.</p>"
@@ -547,125 +613,10 @@ def create_probability_chart(predictions, consensus_class):
547
  </div>
548
  """
549
 
550
- # Generar visualizaciones
551
- probability_chart = create_probability_chart(predictions, consensus_class)
552
- heatmap = create_heatmap(predictions)
553
-
554
- # Generar HTML del reporte COMPLETO
555
- html_report = f"""
556
- <div style="font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto;">
557
- <h2 style="color: #2c3e50; text-align: center;">🏥 Análisis Completo de Lesión Cutánea</h2>
558
-
559
- <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; margin: 20px 0;">
560
- <h3 style="margin: 0; text-align: center;">📋 Resultado de Consenso</h3>
561
- <p style="font-size: 18px; text-align: center; margin: 10px 0;"><strong>{consensus_class}</strong></p>
562
- <p style="text-align: center; margin: 5px 0;">Confianza Promedio: <strong>{avg_confidence:.1%}</strong></p>
563
- <p style="text-align: center; margin: 5px 0;">Consenso: <strong>{class_votes[consensus_class]}/{len(predictions)} modelos</strong></p>
564
- </div>
565
-
566
- <div style="background: {risk_info['color']}; color: white; padding: 15px; border-radius: 8px; margin: 15px 0;">
567
- <h4 style="margin: 0;">⚠️ Nivel de Riesgo: {risk_info['level']}</h4>
568
- <p style="margin: 5px 0;"><strong>{risk_info['urgency']}</strong></p>
569
- <p style="margin: 5px 0;">Tipo: {'🔴 Potencialmente maligna' if is_malignant else '🟢 Probablemente benigna'}</p>
570
- </div>
571
-
572
- <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin: 15px 0;">
573
- <h4 style="color: #1976d2;">🤖 Resultados Individuales por Modelo</h4>
574
- """
575
-
576
- # RESULTADOS INDIVIDUALES DETALLADOS
577
- for i, pred in enumerate(predictions, 1):
578
- if pred['success']:
579
- model_risk = RISK_LEVELS[pred['predicted_idx']]
580
- malignant_status = "🔴 Maligna" if pred['is_malignant'] else "🟢 Benigna"
581
-
582
- html_report += f"""
583
- <div style="margin: 15px 0; padding: 15px; background: white; border-radius: 8px; border-left: 5px solid {'#ff6b35' if pred['is_malignant'] else '#44ff44'}; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
584
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
585
- <h5 style="margin: 0; color: #333;">#{i}. {pred['model']}</h5>
586
- <span style="background: {model_risk['color']}; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px;">{model_risk['level']}</span>
587
- </div>
588
-
589
- <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; font-size: 14px;">
590
- <div><strong>Diagnóstico:</strong><br>{pred['class']}</div>
591
- <div><strong>Confianza:</strong><br>{pred['confidence']:.1%}</div>
592
- <div><strong>Clasificación:</strong><br>{malignant_status}</div>
593
- </div>
594
-
595
- <div style="margin-top: 10px;">
596
- <strong>Top 3 Probabilidades:</strong><br>
597
- <div style="font-size: 12px; color: #666;">
598
- """
599
-
600
- # Top 3 probabilidades para este modelo
601
- top_indices = np.argsort(pred['probabilities'])[-3:][::-1]
602
- for idx in top_indices:
603
- prob = pred['probabilities'][idx]
604
- if prob > 0.01: # Solo mostrar si > 1%
605
- html_report += f"• {CLASSES[idx].split('(')[1].rstrip(')')}: {prob:.1%}<br>"
606
-
607
- html_report += f"""
608
- </div>
609
- <div style="margin-top: 8px; font-size: 12px; color: #888;">
610
- <strong>Recomendación:</strong> {model_risk['urgency']}
611
- </div>
612
- </div>
613
- </div>
614
- """
615
- else:
616
- html_report += f"""
617
- <div style="margin: 10px 0; padding: 10px; background: #ffebee; border-radius: 5px; border-left: 4px solid #f44336;">
618
- <strong>❌ {pred['model']}</strong><br>
619
- <span style="color: #d32f2f;">Error: {pred.get('error', 'Desconocido')}</span>
620
- </div>
621
- """
622
-
623
- html_report += f"""
624
- </div>
625
-
626
- <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 15px 0;">
627
- <h4 style="color: #495057;">📊 Análisis Estadístico</h4>
628
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
629
- <div>
630
- <strong>Modelos Activos:</strong> {len([p for p in predictions if p['success']])}/{len(predictions)}<br>
631
- <strong>Acuerdo Total:</strong> {class_votes[consensus_class]}/{len([p for p in predictions if p['success']])}<br>
632
- <strong>Confianza Máxima:</strong> {max([p['confidence'] for p in predictions if p['success']]):.1%}
633
- </div>
634
- <div>
635
- <strong>Diagnósticos Malignos:</strong> {len([p for p in predictions if p.get('success') and p.get('is_malignant')])}<br>
636
- <strong>Diagnósticos Benignos:</strong> {len([p for p in predictions if p.get('success') and not p.get('is_malignant')])}<br>
637
- <strong>Consenso Maligno:</strong> {'Sí' if is_malignant else 'No'}
638
- </div>
639
- </div>
640
- </div>
641
-
642
- <div style="background: #ffffff; padding: 15px; border-radius: 8px; margin: 15px 0; border: 1px solid #ddd;">
643
- <h4 style="color: #333;">📈 Gráficos de Análisis</h4>
644
- {probability_chart}
645
- </div>
646
-
647
- <div style="background: #ffffff; padding: 15px; border-radius: 8px; margin: 15px 0; border: 1px solid #ddd;">
648
- <h4 style="color: #333;">🔥 Mapa de Calor de Probabilidades</h4>
649
- {heatmap}
650
- </div>
651
-
652
- <div style="background: #fff3e0; padding: 15px; border-radius: 8px; margin: 15px 0; border: 1px solid #ff9800;">
653
- <h4 style="color: #f57c00;">⚠️ Advertencia Médica</h4>
654
- <p style="margin: 5px 0;">Este análisis es solo una herramienta de apoyo diagnóstico basada en IA.</p>
655
- <p style="margin: 5px 0;"><strong>Siempre consulte con un dermatólogo profesional para un diagnóstico definitivo.</strong></p>
656
- <p style="margin: 5px 0;">No utilice esta información como único criterio para decisiones médicas.</p>
657
- <p style="margin: 5px 0;"><em>Los resultados individuales de cada modelo se muestran para transparencia y análisis comparativo.</em></p>
658
- </div>
659
- </div>
660
- """
661
-
662
  return html_report
663
 
664
  except Exception as e:
665
  return f"<h3>❌ Error en el análisis</h3><p>Error técnico: {str(e)}</p><p>Por favor, intente con otra imagen.</p>"
666
-
667
- except Exception as e:
668
- return f"<h3>❌ Error en el análisis</h3><p>Error técnico: {str(e)}</p><p>Por favor, intente con otra imagen.</p>"
669
 
670
  # Configuración de Gradio
671
  def create_interface():
 
333
  if predictions:
334
  # Obtener probabilidades promedio
335
  avg_probs = np.zeros(7)
336
+ valid_predictions = [p for p in predictions if p.get('success', False)]
337
+
338
+ for pred in valid_predictions:
339
  avg_probs += pred['probabilities']
340
+ avg_probs /= len(valid_predictions)
341
 
342
  colors = ['#ff6b35' if i in MALIGNANT_INDICES else '#44ff44' for i in range(7)]
343
  bars = ax1.bar(range(7), avg_probs, color=colors, alpha=0.8)
 
362
  f'{height:.2%}', ha='center', va='bottom', fontsize=9)
363
 
364
  # Gráfico 2: Confianza por modelo
365
+ valid_predictions = [p for p in predictions if p.get('success', False)]
366
+ model_names = [pred['model'].split(' ')[1] if len(pred['model'].split(' ')) > 1 else pred['model'] for pred in valid_predictions]
367
+ confidences = [pred['confidence'] for pred in valid_predictions]
368
 
369
+ colors_conf = ['#ff6b35' if pred['is_malignant'] else '#44ff44' for pred in valid_predictions]
370
+ bars2 = ax2.bar(range(len(valid_predictions)), confidences, color=colors_conf, alpha=0.8)
371
 
372
  ax2.set_xlabel('Modelos')
373
  ax2.set_ylabel('Confianza')
374
  ax2.set_title('🎯 Confianza por Modelo')
375
+ ax2.set_xticks(range(len(valid_predictions)))
376
  ax2.set_xticklabels(model_names, rotation=45)
377
  ax2.grid(True, alpha=0.3)
378
  ax2.set_ylim(0, 1)
 
397
  except Exception as e:
398
  print(f"Error creando gráfico: {e}")
399
  return "<p>❌ Error generando gráfico de probabilidades</p>"
400
+
401
+ def create_heatmap(predictions):
402
+ """Crear mapa de calor de probabilidades por modelo"""
403
  try:
404
+ valid_predictions = [p for p in predictions if p.get('success', False)]
405
+
406
+ if not valid_predictions:
407
+ return "<p>No hay datos suficientes para el mapa de calor</p>"
408
+
409
+ # Crear matriz de probabilidades
410
+ prob_matrix = np.array([pred['probabilities'] for pred in valid_predictions])
411
+
412
+ # Crear figura
413
+ fig, ax = plt.subplots(figsize=(10, 6))
414
+
415
+ # Crear mapa de calor
416
+ im = ax.imshow(prob_matrix, cmap='RdYlGn_r', aspect='auto', vmin=0, vmax=1)
417
+
418
+ # Configurar etiquetas
419
+ ax.set_xticks(np.arange(7))
420
+ ax.set_yticks(np.arange(len(valid_predictions)))
421
+ ax.set_xticklabels([cls.split('(')[1].rstrip(')') for cls in CLASSES])
422
+ ax.set_yticklabels([pred['model'] for pred in valid_predictions])
423
+
424
+ # Rotar etiquetas del eje x
425
+ plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
426
+
427
+ # Añadir valores en las celdas
428
+ for i in range(len(valid_predictions)):
429
+ for j in range(7):
430
+ text = ax.text(j, i, f'{prob_matrix[i, j]:.2f}',
431
+ ha="center", va="center", color="white" if prob_matrix[i, j] > 0.5 else "black",
432
+ fontsize=8)
433
+
434
+ ax.set_title("Mapa de Calor: Probabilidades por Modelo y Clase")
435
+ fig.tight_layout()
436
+
437
+ # Añadir barra de color
438
+ cbar = plt.colorbar(im, ax=ax)
439
+ cbar.set_label('Probabilidad', rotation=270, labelpad=15)
440
+
441
+ # Convertir a base64
442
+ buf = io.BytesIO()
443
+ plt.savefig(buf, format='png', dpi=300, bbox_inches='tight')
444
+ buf.seek(0)
445
+ heatmap_b64 = base64.b64encode(buf.getvalue()).decode()
446
+ plt.close()
447
+
448
+ return f'<img src="data:image/png;base64,{heatmap_b64}" style="width:100%; max-width:800px;">'
449
+
450
+ except Exception as e:
451
+ print(f"Error creando mapa de calor: {e}")
452
+ return "<p>❌ Error generando mapa de calor</p>"
453
+
454
+ def analizar_lesion(img):
455
+ """Función principal para analizar la lesión"""
456
+ try:
457
+ if img is None:
458
+ return "<h3>⚠️ Por favor, carga una imagen</h3>"
459
+
460
+ # Verificar que hay modelos cargados
461
+ if not loaded_models or all(m.get('type') == 'dummy' for m in loaded_models.values()):
462
+ return "<h3>❌ Error del Sistema</h3><p>No hay modelos disponibles. Por favor, recarga la aplicación.</p>"
463
+
464
  # Convertir a RGB si es necesario
465
  if img.mode != 'RGB':
466
  img = img.convert('RGB')
 
469
 
470
  # Obtener predicciones de todos los modelos cargados
471
  for model_name, model_data in loaded_models.items():
472
+ if model_data.get('type') != 'dummy':
473
+ pred = predict_with_model(img, model_data)
474
+ if pred.get('success', False):
475
+ predictions.append(pred)
476
 
477
  if not predictions:
478
  return "<h3>❌ Error</h3><p>No se pudieron obtener predicciones de ningún modelo.</p>"
 
613
  </div>
614
  """
615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
  return html_report
617
 
618
  except Exception as e:
619
  return f"<h3>❌ Error en el análisis</h3><p>Error técnico: {str(e)}</p><p>Por favor, intente con otra imagen.</p>"
 
 
 
620
 
621
  # Configuración de Gradio
622
  def create_interface():