Klinapps commited on
Commit
4ea5bee
·
verified ·
1 Parent(s): aafc662

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -92
app.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
  Cephalometric Landmark Detection API
3
  HRNet-W32 - 19 Landmarks Cefalométricos
4
- Versión 1.1 - Con Indicadores de Confianza
5
  """
6
 
7
  import os
@@ -67,18 +67,6 @@ def get_color_for_landmark(idx):
67
  return group_data["color"]
68
  return (255, 255, 255)
69
 
70
- def get_confidence_indicator(confidence):
71
- """Retorna color e indicador según nivel de confianza"""
72
- # Normalizar confianza (los heatmaps pueden tener valores > 1)
73
- conf_pct = min(confidence * 100, 100)
74
-
75
- if conf_pct >= 80:
76
- return (80, 255, 80), "✓", conf_pct # Verde - Alta
77
- elif conf_pct >= 50:
78
- return (255, 255, 80), "~", conf_pct # Amarillo - Media
79
- else:
80
- return (255, 80, 80), "!", conf_pct # Rojo - Baja
81
-
82
  # ============================================================================
83
  # ARQUITECTURA HRNET-W32
84
  # ============================================================================
@@ -377,7 +365,7 @@ def detect_landmarks(image):
377
  "abbrev": LANDMARK_ABBREV[i],
378
  "x": round(float(preds[0, i, 0] * scale_x), 1),
379
  "y": round(float(preds[0, i, 1] * scale_y), 1),
380
- "confidence": round(float(maxvals[0, i]), 4)
381
  })
382
 
383
  return landmarks
@@ -404,21 +392,8 @@ def draw_landmarks_clean(image, landmarks, show_labels=True):
404
  x, y = lm['x'], lm['y']
405
  color = get_color_for_landmark(lm['id'])
406
 
407
- # Obtener info de confianza
408
- conf_color, conf_indicator, conf_pct = get_confidence_indicator(lm['confidence'])
409
-
410
- # Círculo con borde según confianza
411
- # Borde más grueso y de color diferente para baja confianza
412
- if conf_pct < 50:
413
- # Borde rojo grueso para baja confianza
414
- draw.ellipse([x-radius-3, y-radius-3, x+radius+3, y+radius+3], fill=(255, 80, 80))
415
- elif conf_pct < 80:
416
- # Borde amarillo para confianza media
417
- draw.ellipse([x-radius-2, y-radius-2, x+radius+2, y+radius+2], fill=(255, 255, 80))
418
- else:
419
- # Borde negro normal para alta confianza
420
- draw.ellipse([x-radius-1, y-radius-1, x+radius+1, y+radius+1], fill=(0, 0, 0))
421
-
422
  draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill=color)
423
 
424
  if show_labels:
@@ -494,20 +469,18 @@ def find_label_position(x, y, text, existing_positions, font, draw, img_size):
494
  return (x + 12, y - 5)
495
 
496
  def create_legend_image(landmarks):
497
- """Crea imagen con leyenda de landmarks incluyendo confianza"""
498
- width = 320
499
- height = 580
500
  legend = Image.new('RGB', (width, height), (30, 30, 30))
501
  draw = ImageDraw.Draw(legend)
502
 
503
  try:
504
  font_title = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
505
  font_text = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11)
506
- font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 9)
507
  except:
508
  font_title = ImageFont.load_default()
509
  font_text = font_title
510
- font_small = font_title
511
 
512
  y_pos = 10
513
  draw.text((10, y_pos), "LANDMARKS DETECTADOS", fill=(255, 255, 255), font=font_title)
@@ -522,42 +495,15 @@ def create_legend_image(landmarks):
522
 
523
  for idx in group_data["indices"]:
524
  lm = landmarks[idx]
525
- conf = lm.get('confidence', 0)
526
- conf_color, conf_indicator, conf_pct = get_confidence_indicator(conf)
527
-
528
- # Círculo de color del grupo
529
  draw.ellipse([15, y_pos+2, 23, y_pos+10], fill=group_data["color"])
530
-
531
- # Texto con coordenadas
532
- coord_text = f"{lm['abbrev']}: ({lm['x']:.0f}, {lm['y']:.0f})"
533
- draw.text((30, y_pos), coord_text, fill=(220, 220, 220), font=font_text)
534
-
535
- # Indicador de confianza con color
536
- conf_text = f"{conf_pct:.0f}%"
537
- # Posicionar a la derecha
538
- conf_x = width - 60
539
- draw.text((conf_x, y_pos), conf_text, fill=conf_color, font=font_text)
540
-
541
- # Indicador visual
542
- indicator_x = width - 20
543
- draw.text((indicator_x, y_pos), conf_indicator, fill=conf_color, font=font_text)
544
-
545
  y_pos += 16
546
 
547
  y_pos += 8
548
 
549
- # Leyenda de confianza al final
550
- y_pos += 5
551
- draw.line([(10, y_pos), (width-10, y_pos)], fill=(100, 100, 100), width=1)
552
- y_pos += 8
553
- draw.text((10, y_pos), "Confianza:", fill=(200, 200, 200), font=font_small)
554
- y_pos += 14
555
- draw.text((15, y_pos), "✓ ≥80% Alta", fill=(80, 255, 80), font=font_small)
556
- y_pos += 12
557
- draw.text((15, y_pos), "~ 50-79% Media", fill=(255, 255, 80), font=font_small)
558
- y_pos += 12
559
- draw.text((15, y_pos), "! <50% Baja (revisar)", fill=(255, 80, 80), font=font_small)
560
-
561
  return legend
562
 
563
  def combine_images(main_image, legend):
@@ -591,27 +537,14 @@ def process_image(image, show_labels):
591
  legend = create_legend_image(landmarks)
592
  combined = combine_images(annotated, legend)
593
 
594
- # Calcular estadísticas de confianza
595
- confidences = [lm['confidence'] for lm in landmarks]
596
- avg_conf = np.mean(confidences) * 100
597
- min_conf = np.min(confidences) * 100
598
- low_conf_count = sum(1 for c in confidences if c * 100 < 50)
599
-
600
- # JSON output con confianza
601
  result = {
602
  "num_landmarks": len(landmarks),
603
- "confidence_stats": {
604
- "average": round(avg_conf, 1),
605
- "minimum": round(min_conf, 1),
606
- "low_confidence_count": low_conf_count
607
- },
608
  "landmarks": [{
609
  "id": lm["id"],
610
  "name": lm["name"],
611
- "abbrev": lm["abbrev"],
612
  "x": lm["x"],
613
- "y": lm["y"],
614
- "confidence": round(lm["confidence"] * 100, 1)
615
  } for lm in landmarks]
616
  }
617
 
@@ -622,18 +555,15 @@ def process_image(image, show_labels):
622
  return None, f"Error: {e}\n{traceback.format_exc()}"
623
 
624
  print("=" * 50)
625
- print("Cephalometric Landmark Detection v1.1")
626
- print("Con indicadores de confianza")
627
  print("=" * 50)
628
  load_model()
629
 
630
- with gr.Blocks(title="Cephalometric Landmark Detection") as demo:
631
  gr.Markdown("""
632
  # 🦷 Detección de Landmarks Cefalométricos
633
 
634
  Detección automática de **19 puntos anatómicos** en radiografías cefalométricas laterales usando HRNet-W32.
635
-
636
- **Nuevo:** Indicadores de confianza para cada punto detectado.
637
  """)
638
 
639
  with gr.Row():
@@ -646,7 +576,7 @@ with gr.Blocks(title="Cephalometric Landmark Detection") as demo:
646
  output_image = gr.Image(label="📍 Resultado con Leyenda", height=500)
647
 
648
  with gr.Accordion("📋 Datos JSON", open=False):
649
- output_json = gr.Code(label="Coordenadas y Confianza", language="json", lines=15)
650
 
651
  gr.Markdown("""
652
  ---
@@ -660,12 +590,8 @@ with gr.Blocks(title="Cephalometric Landmark Detection") as demo:
660
  | 🟡 | Dental | Upper Incisor (U1), Lower Incisor (L1) |
661
  | 🩵 | Tejido Blando | Upper Lip, Lower Lip, Subnasale, Soft Tissue Pog |
662
 
663
- ### 🎯 Indicadores de Confianza
664
- | Indicador | Significado |
665
- |-----------|-------------|
666
- | ✓ (verde) | Alta confianza (≥80%) - Punto bien detectado |
667
- | ~ (amarillo) | Confianza media (50-79%) - Verificar posición |
668
- | ! (rojo) | Baja confianza (<50%) - Requiere revisión manual |
669
  """)
670
 
671
  detect_btn.click(fn=process_image, inputs=[input_image, show_labels], outputs=[output_image, output_json])
 
1
  """
2
  Cephalometric Landmark Detection API
3
  HRNet-W32 - 19 Landmarks Cefalométricos
4
+ Versión Final con Visualización Mejorada
5
  """
6
 
7
  import os
 
67
  return group_data["color"]
68
  return (255, 255, 255)
69
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  # ============================================================================
71
  # ARQUITECTURA HRNET-W32
72
  # ============================================================================
 
365
  "abbrev": LANDMARK_ABBREV[i],
366
  "x": round(float(preds[0, i, 0] * scale_x), 1),
367
  "y": round(float(preds[0, i, 1] * scale_y), 1),
368
+ "confidence": round(float(maxvals[0, i]), 2)
369
  })
370
 
371
  return landmarks
 
392
  x, y = lm['x'], lm['y']
393
  color = get_color_for_landmark(lm['id'])
394
 
395
+ # Círculo con borde negro
396
+ draw.ellipse([x-radius-1, y-radius-1, x+radius+1, y+radius+1], fill=(0, 0, 0))
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill=color)
398
 
399
  if show_labels:
 
469
  return (x + 12, y - 5)
470
 
471
  def create_legend_image(landmarks):
472
+ """Crea imagen con leyenda de landmarks"""
473
+ width = 280
474
+ height = 520
475
  legend = Image.new('RGB', (width, height), (30, 30, 30))
476
  draw = ImageDraw.Draw(legend)
477
 
478
  try:
479
  font_title = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
480
  font_text = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11)
 
481
  except:
482
  font_title = ImageFont.load_default()
483
  font_text = font_title
 
484
 
485
  y_pos = 10
486
  draw.text((10, y_pos), "LANDMARKS DETECTADOS", fill=(255, 255, 255), font=font_title)
 
495
 
496
  for idx in group_data["indices"]:
497
  lm = landmarks[idx]
498
+ # Círculo de color
 
 
 
499
  draw.ellipse([15, y_pos+2, 23, y_pos+10], fill=group_data["color"])
500
+ # Texto
501
+ text = f"{lm['abbrev']}: ({lm['x']:.0f}, {lm['y']:.0f})"
502
+ draw.text((30, y_pos), text, fill=(220, 220, 220), font=font_text)
 
 
 
 
 
 
 
 
 
 
 
 
503
  y_pos += 16
504
 
505
  y_pos += 8
506
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  return legend
508
 
509
  def combine_images(main_image, legend):
 
537
  legend = create_legend_image(landmarks)
538
  combined = combine_images(annotated, legend)
539
 
540
+ # JSON output
 
 
 
 
 
 
541
  result = {
542
  "num_landmarks": len(landmarks),
 
 
 
 
 
543
  "landmarks": [{
544
  "id": lm["id"],
545
  "name": lm["name"],
 
546
  "x": lm["x"],
547
+ "y": lm["y"]
 
548
  } for lm in landmarks]
549
  }
550
 
 
555
  return None, f"Error: {e}\n{traceback.format_exc()}"
556
 
557
  print("=" * 50)
558
+ print("Cephalometric Landmark Detection v1.0")
 
559
  print("=" * 50)
560
  load_model()
561
 
562
+ with gr.Blocks(title="Cephalometric Landmark Detection", theme=gr.themes.Soft()) as demo:
563
  gr.Markdown("""
564
  # 🦷 Detección de Landmarks Cefalométricos
565
 
566
  Detección automática de **19 puntos anatómicos** en radiografías cefalométricas laterales usando HRNet-W32.
 
 
567
  """)
568
 
569
  with gr.Row():
 
576
  output_image = gr.Image(label="📍 Resultado con Leyenda", height=500)
577
 
578
  with gr.Accordion("📋 Datos JSON", open=False):
579
+ output_json = gr.Code(label="Coordenadas", language="json", lines=10)
580
 
581
  gr.Markdown("""
582
  ---
 
590
  | 🟡 | Dental | Upper Incisor (U1), Lower Incisor (L1) |
591
  | 🩵 | Tejido Blando | Upper Lip, Lower Lip, Subnasale, Soft Tissue Pog |
592
 
593
+ ---
594
+ > ⚠️ **Nota:** Los puntos detectados son una aproximación inicial. En pacientes en crecimiento y desarrollo o con condiciones anatómicas atípicas, se recomienda ajuste manual.
 
 
 
 
595
  """)
596
 
597
  detect_btn.click(fn=process_image, inputs=[input_image, show_labels], outputs=[output_image, output_json])