feat: lighting quality assessment for Fitzpatrick estimation
Browse files
app.py
CHANGED
|
@@ -389,6 +389,36 @@ def generate_pdf_report(image_rgb, binary_overlay, multiclass_overlay, result):
|
|
| 389 |
bg = FITZ_RGB.get(ftype, (229, 231, 235))
|
| 390 |
fg = FITZ_TEXT_RGB.get(ftype, (50, 50, 50))
|
| 391 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
# Badge
|
| 393 |
x_badge = pdf.get_x()
|
| 394 |
y_badge = pdf.get_y()
|
|
@@ -408,12 +438,13 @@ def generate_pdf_report(image_rgb, binary_overlay, multiclass_overlay, result):
|
|
| 408 |
pdf._font("", 9)
|
| 409 |
det_x = x_badge + 40
|
| 410 |
det_y = y_badge
|
|
|
|
| 411 |
details = [
|
| 412 |
("ITA", f"{fitz.ita_angle:.1f} +/- {fitz.ita_std:.1f} grados"),
|
| 413 |
("L* medio (piel sana)", f"{fitz.l_skin_mean:.1f}"),
|
|
|
|
| 414 |
("b* medio (piel sana)", f"{fitz.b_skin_mean:.1f}"),
|
| 415 |
("Pixeles sanos", f"{fitz.healthy_pixels:,}"),
|
| 416 |
-
("Ratio piel sana", f"{fitz.healthy_ratio:.1%}"),
|
| 417 |
("Confianza", f"{fitz.confidence:.0%}"),
|
| 418 |
]
|
| 419 |
for label, value in details:
|
|
@@ -626,7 +657,32 @@ def build_fitz_html(fitz):
|
|
| 626 |
return "<p style='color:#6b7280;'>No se pudo estimar (insuficientes pixeles de piel sana).</p>"
|
| 627 |
bg = FITZ_COLORS.get(fitz.fitzpatrick_type, "#e5e7eb")
|
| 628 |
fg = FITZ_TEXT_COLORS.get(fitz.fitzpatrick_type, "#1f2937")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
return f"""
|
|
|
|
| 630 |
<div style="display:flex; gap:16px; align-items:center; flex-wrap:wrap;">
|
| 631 |
<div style="background:{bg}; color:{fg}; border-radius:12px; padding:18px 28px;
|
| 632 |
font-size:1.5em; font-weight:700; min-width:120px; text-align:center;
|
|
@@ -636,7 +692,8 @@ def build_fitz_html(fitz):
|
|
| 636 |
</div>
|
| 637 |
<div style="font-size:0.95em; line-height:1.8;">
|
| 638 |
<b>ITA:</b> {fitz.ita_angle:.1f}° ± {fitz.ita_std:.1f}°<br>
|
| 639 |
-
<b>L* medio:</b> {fitz.l_skin_mean:.1f}<br>
|
|
|
|
| 640 |
<b>Pixeles sanos:</b> {fitz.healthy_pixels:,}<br>
|
| 641 |
<b>Confianza:</b> {fitz.confidence:.0%}
|
| 642 |
</div>
|
|
|
|
| 389 |
bg = FITZ_RGB.get(ftype, (229, 231, 235))
|
| 390 |
fg = FITZ_TEXT_RGB.get(ftype, (50, 50, 50))
|
| 391 |
|
| 392 |
+
# Lighting warning in PDF
|
| 393 |
+
lighting_quality = getattr(fitz, "lighting_quality", "good")
|
| 394 |
+
lighting_warning = getattr(fitz, "lighting_warning", "")
|
| 395 |
+
if lighting_quality == "insufficient":
|
| 396 |
+
pdf.set_fill_color(254, 242, 242)
|
| 397 |
+
pdf.set_draw_color(252, 165, 165)
|
| 398 |
+
pdf.set_text_color(220, 38, 38)
|
| 399 |
+
pdf._font("B", 8)
|
| 400 |
+
y_warn = pdf.get_y()
|
| 401 |
+
pdf.rect(pdf.get_x(), y_warn, 185, 10, "DF")
|
| 402 |
+
pdf.set_xy(pdf.get_x() + 2, y_warn + 1)
|
| 403 |
+
pdf.cell(0, 4, "ADVERTENCIA: Iluminacion insuficiente - tipo Fitzpatrick puede estar sobreestimado", 0, 1)
|
| 404 |
+
pdf._font("", 7)
|
| 405 |
+
pdf.set_text_color(153, 27, 27)
|
| 406 |
+
pdf.cell(0, 3, lighting_warning, 0, 1)
|
| 407 |
+
pdf.ln(3)
|
| 408 |
+
elif lighting_quality == "low":
|
| 409 |
+
pdf.set_fill_color(255, 251, 235)
|
| 410 |
+
pdf.set_draw_color(252, 211, 77)
|
| 411 |
+
pdf.set_text_color(217, 119, 6)
|
| 412 |
+
pdf._font("B", 8)
|
| 413 |
+
y_warn = pdf.get_y()
|
| 414 |
+
pdf.rect(pdf.get_x(), y_warn, 185, 10, "DF")
|
| 415 |
+
pdf.set_xy(pdf.get_x() + 2, y_warn + 1)
|
| 416 |
+
pdf.cell(0, 4, "PRECAUCION: Iluminacion suboptima - resultado puede tener 1-2 niveles de error", 0, 1)
|
| 417 |
+
pdf._font("", 7)
|
| 418 |
+
pdf.set_text_color(146, 64, 14)
|
| 419 |
+
pdf.cell(0, 3, lighting_warning, 0, 1)
|
| 420 |
+
pdf.ln(3)
|
| 421 |
+
|
| 422 |
# Badge
|
| 423 |
x_badge = pdf.get_x()
|
| 424 |
y_badge = pdf.get_y()
|
|
|
|
| 438 |
pdf._font("", 9)
|
| 439 |
det_x = x_badge + 40
|
| 440 |
det_y = y_badge
|
| 441 |
+
l_scene = getattr(fitz, "l_scene_mean", 0)
|
| 442 |
details = [
|
| 443 |
("ITA", f"{fitz.ita_angle:.1f} +/- {fitz.ita_std:.1f} grados"),
|
| 444 |
("L* medio (piel sana)", f"{fitz.l_skin_mean:.1f}"),
|
| 445 |
+
("L* escena (global)", f"{l_scene:.1f}"),
|
| 446 |
("b* medio (piel sana)", f"{fitz.b_skin_mean:.1f}"),
|
| 447 |
("Pixeles sanos", f"{fitz.healthy_pixels:,}"),
|
|
|
|
| 448 |
("Confianza", f"{fitz.confidence:.0%}"),
|
| 449 |
]
|
| 450 |
for label, value in details:
|
|
|
|
| 657 |
return "<p style='color:#6b7280;'>No se pudo estimar (insuficientes pixeles de piel sana).</p>"
|
| 658 |
bg = FITZ_COLORS.get(fitz.fitzpatrick_type, "#e5e7eb")
|
| 659 |
fg = FITZ_TEXT_COLORS.get(fitz.fitzpatrick_type, "#1f2937")
|
| 660 |
+
|
| 661 |
+
# Lighting warning banner
|
| 662 |
+
warning_html = ""
|
| 663 |
+
lighting_warning = getattr(fitz, "lighting_warning", "")
|
| 664 |
+
lighting_quality = getattr(fitz, "lighting_quality", "good")
|
| 665 |
+
l_scene = getattr(fitz, "l_scene_mean", 0)
|
| 666 |
+
|
| 667 |
+
if lighting_quality == "insufficient":
|
| 668 |
+
warning_html = f"""
|
| 669 |
+
<div style="background:#fef2f2; border:1px solid #fca5a5; border-radius:8px;
|
| 670 |
+
padding:12px 16px; margin-bottom:12px; font-size:0.9em;">
|
| 671 |
+
<span style="color:#dc2626; font-weight:700;">⚠ Iluminacion insuficiente</span><br>
|
| 672 |
+
<span style="color:#991b1b;">{lighting_warning}</span><br>
|
| 673 |
+
<span style="color:#6b7280; font-size:0.85em;">L* escena: {l_scene:.0f} (minimo recomendado: 35)</span>
|
| 674 |
+
</div>"""
|
| 675 |
+
elif lighting_quality == "low":
|
| 676 |
+
warning_html = f"""
|
| 677 |
+
<div style="background:#fffbeb; border:1px solid #fcd34d; border-radius:8px;
|
| 678 |
+
padding:12px 16px; margin-bottom:12px; font-size:0.9em;">
|
| 679 |
+
<span style="color:#d97706; font-weight:700;">⚠ Iluminacion suboptima</span><br>
|
| 680 |
+
<span style="color:#92400e;">{lighting_warning}</span><br>
|
| 681 |
+
<span style="color:#6b7280; font-size:0.85em;">L* escena: {l_scene:.0f} (recomendado: >50)</span>
|
| 682 |
+
</div>"""
|
| 683 |
+
|
| 684 |
return f"""
|
| 685 |
+
{warning_html}
|
| 686 |
<div style="display:flex; gap:16px; align-items:center; flex-wrap:wrap;">
|
| 687 |
<div style="background:{bg}; color:{fg}; border-radius:12px; padding:18px 28px;
|
| 688 |
font-size:1.5em; font-weight:700; min-width:120px; text-align:center;
|
|
|
|
| 692 |
</div>
|
| 693 |
<div style="font-size:0.95em; line-height:1.8;">
|
| 694 |
<b>ITA:</b> {fitz.ita_angle:.1f}° ± {fitz.ita_std:.1f}°<br>
|
| 695 |
+
<b>L* medio piel:</b> {fitz.l_skin_mean:.1f}<br>
|
| 696 |
+
<b>L* escena:</b> {l_scene:.1f}<br>
|
| 697 |
<b>Pixeles sanos:</b> {fitz.healthy_pixels:,}<br>
|
| 698 |
<b>Confianza:</b> {fitz.confidence:.0%}
|
| 699 |
</div>
|