Spaces:
Running on Zero
Running on Zero
RootScope v4: manuscript model (LightGBM per round, 3 seeds, layer + soft-neighbor context, radial prior)
Browse files
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.
|
| 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 |
-
|
| 468 |
-
|
| 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,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", ".
|
| 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
|
| 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,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
|
| 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
|
| 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)
|