grayleafspotr is a standalone R package for gray leaf spot analysis that is used directly from RStudio. The public interface is R, while the analysis pipeline runs through the bundled Python pipeline.
The package is self-contained inside grayleafspotr/. When you open the project in RStudio, that folder is the working root for the package, example data, models, notebooks, and outputs.
Pipeline
The analysis pipeline is inst/python/pipeline/analysislocal.py, running inside rvenv_arm_311 (ARM64 Python 3.11). The sole model is models/best_area_w_0.7.pt (SmallUNet). No SAM, no Albumentations, no additional checkpoints are required.
What This Package Does
- Reads plate images from a folder you choose.
- Runs the bundled Python analysis pipeline from RStudio.
- Uses
models/best_area_w_0.7.pt(SmallUNet) for segmentation; downloads the model automatically from HuggingFace if not present locally. - Writes raw
analysis.json,analysis.csv, andmanifest.jsonfiles. - Returns a tidy
grayleafspot_runobject that you can inspect in R. - Ships built-in
ggplot2templates you can use as-is or customize. - Bundles three example plate images in
inst/extdata/testdata/06FEB/for offline testing and vignette examples.
Package Layout
-
R/— exported R functions and helpers. -
inst/python/— bundled Python pipeline and its requirements.-
inst/python/pipeline/analysislocal.py— SmallUNet pipeline. -
inst/python/pipeline/utils.py— image-processing utilities. -
inst/python/requirements_arm.txt— deps forrvenv_arm_311.
-
-
inst/extdata/example/— shipped example run (JSON/CSV/manifest). -
inst/extdata/testdata/06FEB/— three bundled test images. -
man/— Roxygen2-generated documentation for all exported functions. -
models/— local model cache (gitignored; auto-downloaded on first use). -
input_images/— additional sample images for interactive use (gitignored). -
notebooks/— RStudio notebook-style walkthrough. -
vignettes/— installed-package vignettes. -
tests/— package tests (unit + integration). -
grayleafspotr.Rproj— opens the project in RStudio.
Main Pipeline Flow
flowchart TD
A["User images in input folder"] --> B["R function: grayleafspot_analyze"]
B --> C["Python bridge: R/python_engine.R"]
C --> D["Bundled Python CLI: pipeline.cli"]
D --> E["pipeline.analysislocal.analyze_image_local"]
E --> F["Dish geometry detection"]
E --> G["SmallUNet: models/best_area_w_0.7.pt"]
F --> H["Colony mask and classical crack analysis"]
G --> H
H --> I["analysis.json"]
H --> J["analysis.csv"]
H --> K["manifest.json"]
I --> L["R reads results back into grayleafspot_run"]
J --> L
K --> L
L --> M["Template ggplot2 plots"]
Requirements
Before running the package, make sure you have:
- R 4.1 or newer.
- RStudio installed.
- Python 3.11 (ARM64 via Homebrew) available on your system.
- A Python environment (
rvenv_arm_311) with the runtime dependencies frominst/python/requirements_arm.txt. - The model asset for the main pipeline:
models/best_area_w_0.7.pt.
If you are working from a fresh clone and models/ is missing, the package will download the model automatically the first time you call grayleafspot_analyze(). You can also trigger the download explicitly:
The model is fetched from https://huggingface.co/rotsl/grayleafspot-segmentation and cached in tools::R_user_dir("grayleafspotr", "cache").
Step-by-Step Setup
1. Open the package in RStudio
Open grayleafspotr/grayleafspotr.Rproj in RStudio. That makes the package folder the working root.
2. Install the R tooling
If devtools is not installed yet, install the package dependencies from the R console:
install.packages(c("devtools", "usethis"))If devtools fails because gert cannot find libgit2, install the system tooling first from macOS Terminal:
Then restart RStudio and run the R install command again.
3. Create the main Python environment (rvenv_arm_311)
This project requires ARM-native Python 3.11 and a version compatible with PyTorch.
Requirements:
- Apple Silicon Mac
- Homebrew installed
- Python 3.11 (ARM64)
- Verify ARM Python:
Expected output:
arm64
- Create the virtual environment:
- Activate the environment:
- Upgrade
pip:
- Install the main pipeline dependencies:
- Verify the installation:
Expected output:
2.11.0
Important notes:
- Do not use
/usr/local/bin/python3.13— that path runs under Rosetta (x86_64) and will break PyTorch installation. - Always use
/opt/homebrew/bin/python3.11. - Make sure
platform.machine() == "arm64".
Point the package at the interpreter with:
Sys.setenv(GRAYLEAFSPOTR_PYTHON = "/Users/wot25kir/grayleafspot/grayleafspotr/rvenv_arm_311/bin/python")RStudio Workflow
Check the Python bridge
grayleafspot_python_available(engine_model = "localunet")
grayleafspot_python_executable(engine_model = "localunet")If grayleafspot_python_available() returns FALSE, the usual causes are:
- the wrong Python interpreter is selected
- the
rvenv_arm_311environment is missing packages frominst/python/requirements_arm.txt -
models/best_area_w_0.7.ptis missing
The pinned Python stack for the main pipeline (rvenv_arm_311) is:
numpy==2.4.4opencv-python==4.13.0.92pillow==12.2.0scipy==1.17.1scikit-image==0.26.0torch==2.11.0torchvision==0.26.0python-dotenv==1.2.2
Running The Package
Example images
The package accepts any folder of plate images. Common formats include:
- PNG
- JPG / JPEG
- BMP
- TIFF / TIF
- WEBP
When working from this repo, you can use the sample images in:
grayleafspotr/input_images/06FEB
grayleafspotr/input_images/30JAN
To analyze your own data, point input_dir at your image folder.
Restarting in a fresh R session
If you restart RStudio, run these lines first so the package uses the correct Python environment for the main pipeline:
setwd("/Users/wot25kir/grayleafspot/grayleafspotr")
Sys.setenv(GRAYLEAFSPOTR_PYTHON = "/Users/wot25kir/grayleafspot/grayleafspotr/rvenv_arm_311/bin/python")
options(grayleafspotr.python = NULL)
devtools::load_all("/Users/wot25kir/grayleafspot/grayleafspotr")
grayleafspot_python_executable(engine_model = "localunet")
grayleafspot_python_available(engine_model = "localunet")Run the analysis
run <- grayleafspot_analyze(
input_dir = "input_images/06FEB",
output_dir = "outputs",
filenames = c(
"20260210_P001_06-FEB_WT_PCBM_SUB_d04_TOP.jpg",
"20260212_P001_06-FEB_WT_PCBM_SUB_d06_TOP.jpg",
"20260216_P001_06-FEB_WT_PCBM_SUB_d10_TOP.jpg"
),
save_outputs = TRUE,
verbose = TRUE,
engine_model = "localunet"
)What happens:
- R calls the bundled Python pipeline (
analysislocal.py) usingrvenv_arm_311. - The Python code loads the SmallUNet checkpoint from
models/best_area_w_0.7.pt. - The pipeline performs dish detection, SmallUNet segmentation, crack analysis, and summary metric extraction.
- The run is saved as a timestamped folder under
output_dir. - R reads the results back into a
grayleafspot_runobject.
Reload saved results
After a run completes, reload the saved folder directly if you want to inspect it again later:
run <- read_grayleafspot_results("outputs/20260427T142731Z_localunet")
run$results[, c("filename", "day", "area_mm2", "diameter_mm", "crack_count", "qc_status")]Where outputs go
If you set output_dir = "outputs", the package writes a new run folder inside grayleafspotr/outputs/ by default.
A saved run contains:
analysis.jsonanalysis.csvmanifest.json-
overlays/with image overlays showing the final mask (red) and crack polylines (yellow) on top of each source image
The run folder name is timestamped, for example:
outputs/20260427T120000Z_localunet
If you set save_outputs = FALSE, the package still returns the run object in R, but it uses a temporary output location that is cleaned up afterward.
What the outputs contain
analysis.json contains the full structured analysis for each image, including:
- image identifiers
- colony morphology in millimetres and square millimetres
- texture metrics
- crack summaries from the classical crack analysis stage
- radial profile data
- segmentation diagnostics
- overlay image paths
analysis.csv contains a tidy, plot-friendly table with the main numeric results for each image. The measurements are reported in millimetres or square millimetres rather than pixels.
manifest.json records the run metadata:
- pipeline name
- pipeline label
- creation time
- file paths for the saved outputs
How To Plot Results
The package ships template plots as ggplot2 objects.
plot_colony_expansion(run)
plot_growth_roughness(run)
plot_stress_remodeling(run)
plot_radial_growth_area(run)
plot_texture_organization(run)
plot_shape_vs_stress(run)
plot_radial_profile(run)Because these functions return ggplot2 objects, you can customize them:
plot_colony_expansion(run) +
ggplot2::labs(
title = "My custom title",
subtitle = "Custom subtitle"
)If you want your own plotting workflow, convert the results to tidy data:
df <- as_grayleafspot_growth_data(run)From there you can use ggplot2, dplyr, quarto, or plain R code however you like.
Save plots to files
dir.create("outputs/figures", recursive = TRUE, showWarnings = FALSE)
plot_specs <- list(
colony_expansion = list(fn = plot_colony_expansion, file = "01_colony_expansion.png"),
growth_roughness = list(fn = plot_growth_roughness, file = "02_growth_roughness.png"),
stress_remodeling = list(fn = plot_stress_remodeling, file = "03_stress_remodeling.png"),
radial_growth_area = list(fn = plot_radial_growth_area, file = "04_radial_growth_area.png"),
texture_organization = list(fn = plot_texture_organization, file = "05_texture_organization.png"),
shape_vs_stress = list(fn = plot_shape_vs_stress, file = "06_shape_vs_stress.png"),
radial_profile = list(fn = plot_radial_profile, file = "07_radial_profile.png")
)
for (spec in plot_specs) {
ggplot2::ggsave(
filename = file.path("outputs/figures", spec$file),
plot = spec$fn(run),
width = 8,
height = 5,
dpi = 300
)
}Notebook And Vignette
You can follow the same workflow in:
-
notebooks/grayleafspotr_workflow.Rmdfor an RStudio notebook-style walkthrough -
vignettes/grayleafspotr-workflow.Rmdfor the installed package vignette
Both files mirror the same step-by-step sequence:
- set the package root and point
GRAYLEAFSPOTR_PYTHONatrvenv_arm_311/bin/python - clear any saved
grayleafspotr.pythonoption - source the package code or load the installed package
- confirm the Python bridge works with
engine_model = "localunet" - inspect the example data in
inst/extdata/example/ - render the built-in plots from the example run
- run
grayleafspot_analyze()withengine_model = "localunet"oninput_images/06FEB - reload the saved results from
outputs/<timestamp>_localunet - build and save the plots to
outputs/figures/ - inspect the mask overlay PNGs in
outputs/<timestamp>_localunet/overlays/ - clean up the
outputs/folder at the end of the notebook
Troubleshooting
devtools::load_all() fails
If devtools is missing, install it first:
install.packages(c("devtools", "usethis"))If devtools fails during install because gert cannot find libgit2, install the macOS tools shown above and try again.
grayleafspot_python_available() is FALSE
Check these in order:
- Confirm
grayleafspot_python_executable(engine_model = "localunet")points torvenv_arm_311/bin/python. - Reinstall
inst/python/requirements_arm.txtintorvenv_arm_311. - Make sure
models/best_area_w_0.7.ptexists. - Restart RStudio and try again.
RStudio Notebook
The package includes a notebook-style walkthrough at:
grayleafspotr/notebooks/grayleafspotr_workflow.Rmd
How to run it in RStudio
- Open
grayleafspotr/grayleafspotr.Rproj. - Open
notebooks/grayleafspotr_workflow.Rmd. - Run the first chunk, which sets:
Sys.setenv(GRAYLEAFSPOTR_PYTHON = file.path(pkg_root, "rvenv_arm_311", "bin", "python"))
options(grayleafspotr.python = NULL)- Run the example-data chunks first.
- Run the real-analysis chunk once
grayleafspot_python_available(engine_model = "localunet")isTRUE. - Use the saved-run chunk to reload the output folder from
outputs/. - Knit the document or run chunks interactively as needed.
Notebook inputs and outputs
The notebook uses the sample images in input_images/06FEB when the Python ML dependencies are available.
If the Python stack is not available, it falls back to the shipped example run so the document still renders cleanly.
The notebook writes analysis outputs to:
grayleafspotr/outputs
The outputs/figures/ subfolder is where the saved PNG plots go.
Installed Vignette
The package also includes a vignette version of the same walkthrough at:
vignettes/grayleafspotr-workflow.Rmd
Use this when you want a packaged, installable reference document rather than the notebook-style source workflow.
Development Workflow
This package follows the standard devtools workflow:
devtools::load_all()
devtools::document()
devtools::test()
devtools::check()Recommended sequence while developing:
- Open the
.Rprojfile. - Run
devtools::load_all(). - Edit code in
R/. - Re-run the notebook or vignette.
- Run tests.
- Run
devtools::check()before release.
Main Functions
| Function | Description |
|---|---|
grayleafspot_run() |
Primary entry point — run pipeline, return parsed JSON |
grayleafspot_analyze() |
Full-featured pipeline returning an S3 grayleafspot_run object |
grayleafspot_python_available() |
Check that the six ML modules are importable |
grayleafspot_python_executable() |
Return the resolved Python interpreter path |
grayleafspot_download_model() |
Download best_area_w_0.7.pt from HuggingFace |
read_grayleafspot_results() |
Load a saved run directory into a grayleafspot_run
|
write_grayleafspot_results() |
Serialize results to JSON, CSV, and manifest |
as_grayleafspot_growth_data() |
Coerce a run to a tidy data frame |
example_grayleafspot_results() |
Load the built-in example run |
plot_colony_expansion() |
Colony radius over time |
plot_growth_roughness() |
Growth rate and edge roughness |
plot_stress_remodeling() |
Crack coverage and count |
plot_texture_organization() |
Entropy and center-to-edge delta |
plot_shape_vs_stress() |
Eccentricity vs crack coverage scatter |
plot_radial_growth_area() |
Radial area by plate (faceted) |
plot_feature_heatmap() |
Pearson correlation heatmap |
plot_radial_profile() |
Radial intensity profile |
Quick Start
Explore without running the pipeline
library(grayleafspotr)
run <- example_grayleafspot_results()
plot_colony_expansion(run)Run the pipeline on your images
Step 1 — one-time Python setup. Add this line to ~/.Rprofile so it loads automatically every time R starts:
file.edit("~/.Rprofile")
# Add:
# Sys.setenv(GRAYLEAFSPOTR_PYTHON = "/path/to/rvenv_arm_311/bin/python")Step 2 — run:
library(grayleafspotr)
res <- grayleafspot_run(
input_dir = "images",
output_dir = "outputs",
run_name = "trial_01"
)grayleafspot_run() automatically finds the installed package Python directory, creates output_dir if needed, and returns the parsed JSON results. No hardcoded paths required.
