ct-tranchau commited on
Commit
80d8541
路
verified 路
1 Parent(s): bb112f6

RootScope v4: manuscript model (LightGBM per round, 3 seeds, layer + soft-neighbor context, radial prior)

Browse files
Files changed (2) hide show
  1. README.md +1 -4
  2. app.py +10 -13
README.md CHANGED
@@ -27,10 +27,7 @@ pericycle, stele, xylem, phloem.
27
  checked against the species table shipped with the package.
28
  3. Press Run. Segmentation and the DINOv2 embeddings run on the GPU; a
29
  700x700 image with about 200 cells takes about 20 to 45 seconds.
30
- 4. Read the report under the table: it says how far to trust the result,
31
- whether the root has an exodermis and why, and lists any warnings.
32
- 5. Download the overlay image, the CSV of per-cell predictions and the
33
- report.
34
 
35
  One image at a time. For batches use the command line: `rootscope --tif-dir`.
36
 
 
27
  checked against the species table shipped with the package.
28
  3. Press Run. Segmentation and the DINOv2 embeddings run on the GPU; a
29
  700x700 image with about 200 cells takes about 20 to 45 seconds.
30
+ 4. Download the overlay image and the CSV of per-cell predictions.
 
 
 
31
 
32
  One image at a time. For batches use the command line: `rootscope --tif-dir`.
33
 
app.py CHANGED
@@ -464,10 +464,10 @@ def _render(results, model_name):
464
  stats += (f" 路 xylem poles: {int(row['n_xylem_poles'])} 路 "
465
  f"phloem poles: {int(row['n_phloem_poles'])}")
466
 
467
- report_path = workdir / f"{stem}_{model_name}_report.txt"
468
- report = ("```\n" + report_path.read_text() + "```") if report_path.exists() else ""
469
  return (str(overlay_path), summary, stats,
470
- gr.DownloadButton(value=str(csv_path), visible=True), report)
471
 
472
 
473
  def run_rootscope(tif_path, tif_file, um_per_px, which_model, label_cells,
@@ -535,7 +535,7 @@ def run_rootscope(tif_path, tif_file, um_per_px, which_model, label_cells,
535
  zip_path = workdir / f"{stem}_rootscope_results.zip"
536
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
537
  for f in sorted(workdir.iterdir()):
538
- if f.suffix in (".png", ".csv", ".txt", ".json"):
539
  z.write(f, arcname=f.name)
540
 
541
  available = [m for m in MODEL_CHOICES
@@ -545,18 +545,18 @@ def run_rootscope(tif_path, tif_file, um_per_px, which_model, label_cells,
545
  results = {"workdir": str(workdir), "stem": stem, "df": df,
546
  "n_layers": n_layers, "um_per_px": float(um_per_px),
547
  "available": available}
548
- overlay_path, summary, stats, csv_button, report = _render(results, chosen)
549
  return (overlay_path, summary, stats, csv_button,
550
  gr.DownloadButton(value=str(zip_path), visible=True),
551
  gr.update(choices=available, value=chosen),
552
- results, report)
553
 
554
 
555
  def switch_model(results, model_name):
556
  """Swap the displayed model without recomputing, since every model's overlay and
557
  CSV was already written to the run's temp dir."""
558
  if not results or model_name not in results.get("available", []):
559
- return gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
560
  return _render(results, model_name)
561
 
562
 
@@ -631,8 +631,7 @@ with gr.Blocks(title=TITLE, **_blocks_kwargs) as demo:
631
  placeholder="e.g. Pisum sativum",
632
  info="Not required. If given, it is checked against the species "
633
  "table: for a training species it settles whether the root "
634
- "has an exodermis; otherwise the image decides and the "
635
- "report says how sure that call is.",
636
  )
637
  with gr.Accordion("Advanced", open=False):
638
  smooth_layers = gr.Checkbox(
@@ -681,7 +680,6 @@ with gr.Blocks(title=TITLE, **_blocks_kwargs) as demo:
681
  stats_md = gr.Markdown()
682
  summary = gr.Dataframe(label="Cells per type", interactive=False,
683
  wrap=True)
684
- report_md = gr.Markdown(label="How far to trust this result")
685
  with gr.Row():
686
  csv_btn = gr.DownloadButton("Download predictions (CSV)",
687
  visible=False)
@@ -714,11 +712,10 @@ with gr.Blocks(title=TITLE, **_blocks_kwargs) as demo:
714
  run_rootscope,
715
  [tif_state, tif_upload, um_per_px, model_pick, label_cells, max_rounds,
716
  species, smooth_layers],
717
- [overlay, summary, stats_md, csv_btn, zip_btn, model_pick, results_state,
718
- report_md],
719
  )
720
  model_pick.change(switch_model, [results_state, model_pick],
721
- [overlay, summary, stats_md, csv_btn, report_md])
722
  if SHOW_DIAGNOSTICS:
723
  diag_btn.click(run_diagnostics, None, diag_out)
724
  env_btn.click(env_report, None, diag_out)
 
464
  stats += (f" 路 xylem poles: {int(row['n_xylem_poles'])} 路 "
465
  f"phloem poles: {int(row['n_phloem_poles'])}")
466
 
467
+ # The per-image trust report (*_report.txt) is not shown on the Space (Nina, 2026-10-08);
468
+ # the command-line package still writes it.
469
  return (str(overlay_path), summary, stats,
470
+ gr.DownloadButton(value=str(csv_path), visible=True))
471
 
472
 
473
  def run_rootscope(tif_path, tif_file, um_per_px, which_model, label_cells,
 
535
  zip_path = workdir / f"{stem}_rootscope_results.zip"
536
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
537
  for f in sorted(workdir.iterdir()):
538
+ if f.suffix in (".png", ".csv", ".json") and not f.name.endswith("_report.txt"):
539
  z.write(f, arcname=f.name)
540
 
541
  available = [m for m in MODEL_CHOICES
 
545
  results = {"workdir": str(workdir), "stem": stem, "df": df,
546
  "n_layers": n_layers, "um_per_px": float(um_per_px),
547
  "available": available}
548
+ overlay_path, summary, stats, csv_button = _render(results, chosen)
549
  return (overlay_path, summary, stats, csv_button,
550
  gr.DownloadButton(value=str(zip_path), visible=True),
551
  gr.update(choices=available, value=chosen),
552
+ results)
553
 
554
 
555
  def switch_model(results, model_name):
556
  """Swap the displayed model without recomputing, since every model's overlay and
557
  CSV was already written to the run's temp dir."""
558
  if not results or model_name not in results.get("available", []):
559
+ return gr.update(), gr.update(), gr.update(), gr.update()
560
  return _render(results, model_name)
561
 
562
 
 
631
  placeholder="e.g. Pisum sativum",
632
  info="Not required. If given, it is checked against the species "
633
  "table: for a training species it settles whether the root "
634
+ "has an exodermis; otherwise the image decides.",
 
635
  )
636
  with gr.Accordion("Advanced", open=False):
637
  smooth_layers = gr.Checkbox(
 
680
  stats_md = gr.Markdown()
681
  summary = gr.Dataframe(label="Cells per type", interactive=False,
682
  wrap=True)
 
683
  with gr.Row():
684
  csv_btn = gr.DownloadButton("Download predictions (CSV)",
685
  visible=False)
 
712
  run_rootscope,
713
  [tif_state, tif_upload, um_per_px, model_pick, label_cells, max_rounds,
714
  species, smooth_layers],
715
+ [overlay, summary, stats_md, csv_btn, zip_btn, model_pick, results_state],
 
716
  )
717
  model_pick.change(switch_model, [results_state, model_pick],
718
+ [overlay, summary, stats_md, csv_btn])
719
  if SHOW_DIAGNOSTICS:
720
  diag_btn.click(run_diagnostics, None, diag_out)
721
  env_btn.click(env_report, None, diag_out)