---
title: "grayleafspotr Workflow"
output:
  BiocStyle::html_document:
    toc: true
    toc_depth: 3
vignette: >
  %\VignetteIndexEntry{grayleafspotr Workflow}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  echo    = TRUE,
  message = FALSE,
  warning = FALSE,
  fig.width  = 7,
  fig.height = 4.5
)
library(grayleafspotr)

# Gate the pipeline chunks with a presence check. If an external basilisk
# cache is configured, it must exist before the live examples can run. Live
# Python examples are not run on Bioconductor build machines.
is_bioc_build <- identical(Sys.getenv("IS_BIOC_BUILD_MACHINE"), "true")
basilisk_dir <- Sys.getenv("BASILISK_EXTERNAL_DIR", unset = "")
py_ok <- !is_bioc_build &&
  (!nzchar(basilisk_dir) || dir.exists(basilisk_dir)) &&
  tryCatch(
    grayleafspot_python_available(engine_model = "localunet"),
    error = function(e) FALSE
  )
```

## Abstract {-}

Gray leaf spot (GLS) is a fungal disease caused by the ascomycete
*Magnaporthe oryzae*, an economically important pathogen best known as the
causal agent of rice blast and also associated with gray leaf spot on other
grass hosts. In laboratory settings, fungal isolates are grown on petri
dishes and photographed at successive time points to characterise colony
expansion, morphology, and structural organisation. Manual measurement of
these traits is time-consuming and operator-dependent.

`grayleafspotr` provides a fully automated pipeline for quantitative
phenotyping of GLS colonies from time-lapse plate photographs. The package
uses a bundled SmallUNet deep-learning segmentation model to identify colonies
in each image, then extracts morphometric and texture features that describe
colony growth, shape, and internal organisation over time. The resulting tidy
data frame can be explored with included template `ggplot2` visualisations or
passed to any downstream R analysis.

Python dependencies are managed automatically through `basilisk`; no manual
Python environment setup is required.

### Interoperability with Bioconductor {-}

`grayleafspotr` integrates with the Bioconductor ecosystem in two ways.
Python environment management uses `basilisk`, so the segmentation model runs
in a versioned, reproducible environment rather than depending on the user's
system Python. For visual quality control, `plot_grayleafspot_overlay()` uses
`EBImage` to draw the detected dish boundary, colony outline, and crack
segments directly on the source photograph, so the numeric summary features
in `$results` can be checked against the underlying image rather than trusted
as an opaque black box. General-purpose plate-image segmentation tools
(e.g. ImageJ/Fiji macros, or ad hoc OpenCV scripts) typically require users to
re-implement this kind of feature extraction and visual-QC step themselves;
`grayleafspotr` packages the full path from raw photograph to tidy,
`ggplot2`-ready results and an `EBImage`-based visual audit trail in one
reproducible pipeline.

---

## Package overview

### What the package does

Given a folder of plate photographs, `grayleafspotr`:

1. **Detects the petri dish** boundary using classical circle fitting.
2. **Segments the colony** inside the dish with a SmallUNet model
   (`best_area_w_0.7.pt`).
3. **Extracts per-image features**: area (mm²), equivalent diameter,
   circularity, eccentricity, edge roughness, crack coverage, texture
   entropy, and a radial intensity profile.
4. **Returns a tidy result** object (`grayleafspot_run`) containing a
   data frame indexed by filename and imaging day.
5. **Provides template plots**, one function per figure, all returning
   `ggplot2` objects ready for further customisation.
6. **Supports visual QC**: `plot_grayleafspot_overlay()` draws the detected
   ROIs on the source image via `EBImage`.

### Input requirements

| Requirement | Detail |
|---|---|
| Image formats | JPEG, PNG, BMP, TIFF, WEBP |
| Naming convention | Encode the day with a `d\d+` token: `*_d04_*.jpg` for day 4 |
| Plate diameter | Standard 90 mm (adjustable via `plate_diameter_mm`) |
| Folder structure | One folder per experiment; one image per plate per time point |

Example filenames and the day values they produce:

```
20260210_P001_06-FEB_WT_PCBM_SUB_d04_TOP.jpg  →  day 4
20260212_P001_06-FEB_WT_PCBM_SUB_d06_TOP.jpg  →  day 6
20260216_P001_06-FEB_WT_PCBM_SUB_d10_TOP.jpg  →  day 10
```

### Workflow at a glance

```
Plate images (folder)
        │
        ▼
grayleafspot_analyze()  /  grayleafspot_run()
        │
        ├─ Dish geometry detection  (classical Hough transform)
        ├─ SmallUNet segmentation   (best_area_w_0.7.pt)
        └─ Feature extraction       (area, shape, texture, cracks, radial)
        │
        ▼
grayleafspot_run S3 object
        │
        ├─ $results   per-image feature data frame
        └─ $run       run manifest (paths, timestamp, engine)
        │
        ▼
Template ggplot2 plots  /  EBImage ROI overlay  /  tidy data  /  custom analysis
```

---

## Installation

Install from Bioconductor:

```r
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("grayleafspotr")
```

`basilisk` installs Python dependencies when you first run the analysis
pipeline. For setup details and manual configuration, see
[Developer note: Python override](#developer-note-python-override).

---

## Bundled example data

The package ships three pre-computed example results so that all plotting and
data-wrangling functions can be explored without running the full pipeline.

### Load the example run

```{r load-example-run}
example_run <- example_grayleafspot_results()
```

### Inspect the feature table

```{r inspect-feature-table}
example_run$results[, c("filename", "day", "area_mm2", "diameter_mm",
                         "circularity", "crack_coverage_pct", "qc_status")]
```

### Locate bundled source images

The three plate photographs used to generate this example are also included in
the package and are accessible via `system.file()`:

```{r locate-bundled-images}
image_dir <- system.file("extdata", "testdata", "06FEB", package = "grayleafspotr")
list.files(image_dir)
```

---

## Template visualisations

Every plotting function accepts a `grayleafspot_run` object and returns a
`ggplot2` object that can be customised with standard `ggplot2` calls.

### Colony expansion over time

Colony equivalent diameter (mm) plotted against imaging day.

```{r plot-colony-expansion}
plot_colony_expansion(example_run)
```

### Growth rate and edge roughness

Daily growth increment (mm/day) alongside a measure of colony edge
irregularity.

```{r plot-growth-roughness}
plot_growth_roughness(example_run)
```

### Crack coverage and count

Proportion of the colony mask classified as cracked tissue, alongside the
total crack count, a proxy for structural stress.

```{r plot-stress-remodeling}
plot_stress_remodeling(example_run)
```

### Texture organisation

Texture entropy and the centre-to-edge entropy gradient reflect internal
colony heterogeneity.

```{r plot-texture-organization}
plot_texture_organization(example_run)
```

### Shape versus stress

Eccentricity (0 = circular, 1 = elongated) plotted against crack coverage
to reveal coupling between morphology and structural remodelling.

```{r plot-shape-vs-stress}
plot_shape_vs_stress(example_run)
```

### Feature correlation heatmap

Pearson correlations between all numeric features.

```{r plot-feature-heatmap}
plot_feature_heatmap(example_run)
```

### Radial intensity profile

Mean pixel intensity as a function of normalised radial distance from the
colony centre.

```{r plot-radial-profile}
plot_radial_profile(example_run)
```

---

## Work with tidy data

Convert the run object to a plain data frame for any downstream workflow:

```{r growth-data-tidy}
growth_data <- as_grayleafspot_growth_data(example_run)
growth_data
```

### Custom plot

Because every template function returns a `ggplot2` object, you can layer
on additional components:

```{r custom-plot}
plot_colony_expansion(example_run) +
  ggplot2::labs(
    title    = "Colony expansion: 06-FEB experiment",
    subtitle = "WT strain, PCBM substrate"
  ) +
  ggplot2::theme_classic()
```

---

## Analyze your own images

### Prepare your image folder

Place all plate photographs for one experiment in a single directory. The
filename must contain a `d\d+` token that encodes the imaging day:

```
my_experiment/
├── isolate_A_d03_rep1.jpg
├── isolate_A_d05_rep1.jpg
├── isolate_A_d07_rep1.jpg
└── ...
```

The examples below use the three bundled 06FEB test images
(`inst/extdata/testdata/06FEB/`) in place of `my_experiment/`. When the
Python availability check returns `FALSE`, the vignette shows the code
without running these chunks.

### Run the analysis: simple entry point

`grayleafspot_run()` is the recommended entry point for most users. It
returns the raw JSON payload as a named list.

```{r run-simple-entry-point, eval=py_ok}
img_dir <- system.file("extdata", "testdata", "06FEB", package = "grayleafspotr")
out_dir <- file.path(tempdir(), "grayleafspotr-workflow-demo")

res <- grayleafspot_run(
  input_dir  = img_dir,
  output_dir = out_dir,
  run_name   = "isolate_A"
)

res$results   # per-image feature table (data frame)
res$run       # run manifest
```

### Full-featured alternative: `grayleafspot_analyze()`

`grayleafspot_analyze()` returns a `grayleafspot_run` S3 object with direct
access to the template plots:

```{r run-full-featured, eval=py_ok}
run <- grayleafspot_analyze(
  input_dir  = img_dir,
  output_dir = out_dir,
  run_name   = "isolate_A_analyze",
  verbose    = FALSE
)

plot_colony_expansion(run)
```

### Visual QC: overlay detected ROIs on the source image

`plot_grayleafspot_overlay()` draws the detected dish boundary (yellow),
colony outline (red), and crack segments (cyan) on top of the original
photograph, using `EBImage`. This directly answers "did the pipeline segment
what I expect?" without having to trust the summary statistics alone:

```{r visualize-overlay, eval=py_ok}
image_file <- list.files(img_dir, full.names = TRUE)[1]
plot_grayleafspot_overlay(run, image_path = image_file)
```

### Reload saved results

Every run writes outputs to a timestamped sub-folder. Reload a previous run
without re-running the pipeline:

```{r reload-saved-results, eval=py_ok}
saved_dir <- run$run$output_dir
reloaded <- read_grayleafspot_results(saved_dir)
plot_colony_expansion(reloaded)
```

---

## Developer note: Python override

`basilisk` installs NumPy, OpenCV, PyTorch, scikit-image, and the other
Python dependencies on the first pipeline call. Setup may take a few minutes;
subsequent calls use the cached environment.

If `basilisk` cannot create an environment on your system, you can set up one
manually. This may be necessary on some Linux systems or HPC clusters. Follow
the [manual Python environment setup instructions in the README](https://github.com/rotsl/grayleafspotr#manual-python-environment-setup).
Create the environment outside the package directory so development tools do
not have to scan its contents.

To use an existing environment, set the path to its Python interpreter:

```r
# ~/.Rprofile: optional Python override
Sys.setenv(GRAYLEAFSPOTR_PYTHON = "/path/to/rvenv/bin/python")
```

When `GRAYLEAFSPOTR_PYTHON` is set, the pipeline uses that interpreter
instead of the basilisk-managed environment.

---

## References {-}

Related datasets and software, by the same author:

* Rohan R (2026). *Time-series Magnaporthe colony images from twelve petri
  dishes and morphometric analysis results generated using metrics-petri
  3.0.0*. BioStudies S-BSST3199.
  <https://www.ebi.ac.uk/biostudies/studies/S-BSST3199>
* Rohan R (2026). *metrics-petri* [software]. Harvard Dataverse.
  <https://doi.org/10.7910/DVN/SR2HBR>

---

## Session information {-}

```{r session-info}
sessionInfo()
```
