Pro-Coder commited on
Commit
2fd84ab
·
verified ·
1 Parent(s): 674c456

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -20
app.py CHANGED
@@ -377,17 +377,27 @@ ASSETS_DIR = "assets"
377
 
378
 
379
 
 
380
  def load_performance_reports():
381
- """Scan assets/ for *_metrics.json files and pair each with its confusion-matrix
382
- image plus any other {prefix}_*.png/.jpg analysis images present (e.g. a
383
- melanoma threshold curve, a cluster-confusion breakdown). Returns a list of
384
- dicts; empty list if nothing's been uploaded yet."""
 
 
 
 
 
 
385
  import json
386
 
387
  reports = []
388
  if not os.path.isdir(ASSETS_DIR):
389
  return reports
390
  all_files = sorted(os.listdir(ASSETS_DIR))
 
 
 
391
  for fname in all_files:
392
  if not fname.endswith("_metrics.json"):
393
  continue
@@ -403,40 +413,73 @@ def load_performance_reports():
403
  candidate = os.path.join(ASSETS_DIR, f"{prefix}_confusion_matrix{ext}")
404
  if os.path.isfile(candidate):
405
  image_path = candidate
 
406
  break
407
 
408
- # Any other images that share this report's prefix (threshold curves,
409
- # cluster breakdowns, etc.) -- shown as extra analysis below the main
410
- # confusion matrix. Excludes the confusion matrix itself to avoid
411
- # showing it twice.
412
  extra_images = []
413
  for other in all_files:
414
  if not other.startswith(prefix + "_") or not other.lower().endswith((".png", ".jpg", ".jpeg")):
415
  continue
416
  if image_path and other == os.path.basename(image_path):
417
  continue
418
- label = (
419
- other[len(prefix) + 1:]
420
- .rsplit(".", 1)[0]
421
- .replace("_", " ")
422
- .strip()
423
- .title()
424
- )
425
  extra_images.append({"path": os.path.join(ASSETS_DIR, other), "label": label})
 
426
 
427
  reports.append({"data": data, "image_path": image_path, "extra_images": extra_images})
428
- return reports
429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
 
432
  def format_performance_markdown(report):
433
  data = report["data"]
434
  lines = [f"### {data.get('model_name', 'Model')}"]
 
 
 
 
 
 
 
 
435
  acc = data.get("accuracy")
436
  f1m = data.get("f1_macro")
437
  if acc is not None:
438
  lines.append(f"**Accuracy:** {acc*100:.1f}% · **Macro F1:** {f1m*100:.1f}%" if f1m is not None else f"**Accuracy:** {acc*100:.1f}%")
439
-
440
  per_class = data.get("per_class", {})
441
  if per_class:
442
  lines.append("\n| Class | Precision | Recall | F1 | Support |")
@@ -446,14 +489,13 @@ def format_performance_markdown(report):
446
  f"| {cls} | {m.get('precision', 0)*100:.1f}% | {m.get('recall', 0)*100:.1f}% "
447
  f"| {m.get('f1', 0)*100:.1f}% | {int(m.get('support', 0))} |"
448
  )
449
-
450
  notes = data.get("notes")
451
  if notes:
452
  lines.append(f"\n_Note: {notes}_")
453
-
454
  return "\n".join(lines)
455
 
456
-
457
  ABOUT_MARKDOWN = f"""
458
  ## About this project
459
 
@@ -567,6 +609,8 @@ with gr.Blocks(title="Skin Disease Detection — Demo") as demo:
567
  gr.Markdown(format_performance_markdown(report))
568
  if report["image_path"]:
569
  gr.Image(value=report["image_path"], label="Confusion matrix (test set)", show_label=True)
 
 
570
 
571
  with gr.Tab("ℹ️ About"):
572
  gr.Markdown(ABOUT_MARKDOWN)
 
377
 
378
 
379
 
380
+
381
  def load_performance_reports():
382
+ """Scan assets/ for model performance data in two modes:
383
+ 1. Full reports: a *_metrics.json paired with its confusion-matrix image
384
+ and any other same-prefix analysis images (threshold curves, cluster
385
+ breakdowns, etc.).
386
+ 2. Image-only reports: any *_confusion_matrix.{png,jpg,jpeg} that doesn't
387
+ have a matching metrics.json yet -- shown with its charts but no
388
+ numeric table, so you don't have to wait on exporting metrics.json
389
+ just to see the images you already generated.
390
+ Returns a list of dicts; empty list if nothing's been uploaded at all.
391
+ """
392
  import json
393
 
394
  reports = []
395
  if not os.path.isdir(ASSETS_DIR):
396
  return reports
397
  all_files = sorted(os.listdir(ASSETS_DIR))
398
+ claimed = set() # filenames already used by a full (metrics.json-backed) report
399
+
400
+ # --- Pass 1: full reports (metrics.json + its images) ---
401
  for fname in all_files:
402
  if not fname.endswith("_metrics.json"):
403
  continue
 
413
  candidate = os.path.join(ASSETS_DIR, f"{prefix}_confusion_matrix{ext}")
414
  if os.path.isfile(candidate):
415
  image_path = candidate
416
+ claimed.add(f"{prefix}_confusion_matrix{ext}")
417
  break
418
 
 
 
 
 
419
  extra_images = []
420
  for other in all_files:
421
  if not other.startswith(prefix + "_") or not other.lower().endswith((".png", ".jpg", ".jpeg")):
422
  continue
423
  if image_path and other == os.path.basename(image_path):
424
  continue
425
+ label = other[len(prefix) + 1:].rsplit(".", 1)[0].replace("_", " ").strip().title()
 
 
 
 
 
 
426
  extra_images.append({"path": os.path.join(ASSETS_DIR, other), "label": label})
427
+ claimed.add(other)
428
 
429
  reports.append({"data": data, "image_path": image_path, "extra_images": extra_images})
 
430
 
431
+ # --- Pass 2: image-only reports -- any confusion-matrix image not already
432
+ # claimed by a full report above, grouped with its same-prefix siblings ---
433
+ seen_prefixes = set()
434
+ for fname in all_files:
435
+ if fname in claimed or not fname.lower().endswith((".png", ".jpg", ".jpeg")):
436
+ continue
437
+ marker = "_confusion_matrix"
438
+ idx = fname.lower().find(marker)
439
+ if idx == -1:
440
+ continue # only auto-surface files that look like a confusion matrix
441
+ prefix = fname[:idx]
442
+ if prefix in seen_prefixes:
443
+ continue
444
+ seen_prefixes.add(prefix)
445
+
446
+ extra_images = []
447
+ for other in all_files:
448
+ if other == fname or other in claimed or not other.startswith(prefix + "_"):
449
+ continue
450
+ if not other.lower().endswith((".png", ".jpg", ".jpeg")):
451
+ continue
452
+ label = other[len(prefix) + 1:].rsplit(".", 1)[0].replace("_", " ").strip().title()
453
+ extra_images.append({"path": os.path.join(ASSETS_DIR, other), "label": label})
454
+ claimed.add(other)
455
+
456
+ model_name = prefix.replace("_", " ").strip().title() or "Model"
457
+ reports.append({
458
+ "data": {"model_name": model_name, "image_only": True},
459
+ "image_path": os.path.join(ASSETS_DIR, fname),
460
+ "extra_images": extra_images,
461
+ })
462
+
463
+ return reports
464
+
465
 
466
 
467
  def format_performance_markdown(report):
468
  data = report["data"]
469
  lines = [f"### {data.get('model_name', 'Model')}"]
470
+
471
+ if data.get("image_only"):
472
+ lines.append(
473
+ "_Charts available — metrics summary not uploaded yet. Add a matching "
474
+ "`*_metrics.json` to this prefix for the full accuracy/F1/per-class table._"
475
+ )
476
+ return "\n".join(lines)
477
+
478
  acc = data.get("accuracy")
479
  f1m = data.get("f1_macro")
480
  if acc is not None:
481
  lines.append(f"**Accuracy:** {acc*100:.1f}% · **Macro F1:** {f1m*100:.1f}%" if f1m is not None else f"**Accuracy:** {acc*100:.1f}%")
482
+
483
  per_class = data.get("per_class", {})
484
  if per_class:
485
  lines.append("\n| Class | Precision | Recall | F1 | Support |")
 
489
  f"| {cls} | {m.get('precision', 0)*100:.1f}% | {m.get('recall', 0)*100:.1f}% "
490
  f"| {m.get('f1', 0)*100:.1f}% | {int(m.get('support', 0))} |"
491
  )
492
+
493
  notes = data.get("notes")
494
  if notes:
495
  lines.append(f"\n_Note: {notes}_")
496
+
497
  return "\n".join(lines)
498
 
 
499
  ABOUT_MARKDOWN = f"""
500
  ## About this project
501
 
 
609
  gr.Markdown(format_performance_markdown(report))
610
  if report["image_path"]:
611
  gr.Image(value=report["image_path"], label="Confusion matrix (test set)", show_label=True)
612
+ for extra in report.get("extra_images", []):
613
+ gr.Image(value=extra["path"], label=extra["label"], show_label=True)
614
 
615
  with gr.Tab("ℹ️ About"):
616
  gr.Markdown(ABOUT_MARKDOWN)