End-to-end analysis pipeline for gray leaf spot (Magnaporthe and related
fungal) colony morphometry on 90 mm petri-dish images, powered by a lightweight
SmallUNet trained with area-consistency loss (w=0.7).
▶ Try the live demo — upload images, run inference, see overlays & 16 growth charts in your browser.
Each ConvBlock = Conv3×3 (no bias) → ReLU → Conv3×3 (no bias) → ReLU.
DownBlock = MaxPool2d(2) → ConvBlock.
UpBlock = Bilinear upsample(×2, align_corners=False) → cat([skip, x]) → ConvBlock.
Area-Consistency Weights
The model repo contains variants trained with different area-consistency loss
weights. Higher weights enforce stronger agreement between predicted mask area
and ground-truth polygon area:
All spatial metrics are in mm (or mm²) via per-image px_to_mm calibration
from dish detection, so images of different resolutions are correctly comparable.
Category
Charts
Units
Colony geometry
Colony Area, Colony Diameter, Colony Perimeter
mm², mm, mm
Shape descriptors
Eccentricity, Edge Roughness (P/πd), Colony Centre Offset
unitless, unitless, mm
Texture
Colony Texture Entropy, Colony Texture Std Dev
unitless, unitless
Cracks
Crack Area, Crack Coverage, Number of Cracks
mm², %, count
Hyphae
Hyphae Length — Frangi, Meijering, Hybrid
mm, mm, mm
Growth rates
Relative Growth Rate (RGR), Absolute Growth Rate
ln mm²/day, mm²/day
Charts are only generated when ≥2 valid data points exist for that metric.
All charts are included as PNGs in the download zip.
Usage via HF API (Programmatic Access)
Run the full pipeline remotely via the
Gradio Client without
installing anything locally.
Install
pip install gradio_client
Quick Start — Upload + Run Pipeline
python
1from gradio_client import Client, handle_file
23client = Client("rotsl/grayleafspot-segmentation-demo")45# Step 1: Upload images6result = client.predict(7 files=[8 handle_file("plate_d01.jpg"),9 handle_file("plate_d03.jpg"),10 handle_file("plate_d05.jpg"),11],12 api_name="/on_upload",13)1415# Step 2: Run the full analysis pipeline16analysis = client.predict(17 en="GLS_Exp01",# experiment name18 ed="2026-04-01",# experiment start date19 un="YourName",# user name20 pc=1,# plates count21 thresh=0.5,# mask confidence threshold22 full_pipeline=True,# enable full morphometrics23 api_name="/on_run",24)2526status_msg = analysis[0]27overlays = analysis[1]# list of {image: filepath, caption: str}28charts = analysis[2]# list of {image: filepath, caption: str}29results_table = analysis[3]# {"headers": [...], "data": [[...], ...]}30zip_path = analysis[4]# local path to downloaded analysis_full.zip3132print(status_msg)33print(f"Overlays: {len(overlays)} panels")34print(f"Charts: {len(charts)}")35print(f"Download: {zip_path}")
Run full pipeline (segmentation + morphometrics + 16 charts)
en, ed, un, pc, thresh, full_pipeline
Batch Processing Script
python
1"""Process a folder of petri dish images via the HF Space API."""2from pathlib import Path
3from gradio_client import Client, handle_file
45IMAGE_DIR = Path("./my_experiment")6EXPERIMENT ="GLS_Exp01"7START_DATE ="2026-04-01"89client = Client("rotsl/grayleafspot-segmentation-demo")1011# Collect all images12images =sorted(13 p for p in IMAGE_DIR.rglob("*")14if p.suffix.lower()in{".jpg",".jpeg",".png",".tif",".bmp",".webp"}15)16print(f"Found {len(images)} images")1718# Upload19client.predict(20 files=[handle_file(str(p))for p in images],21 api_name="/on_upload",22)2324# Run pipeline25status, overlays, charts, table, zip_path = client.predict(26 en=EXPERIMENT,27 ed=START_DATE,28 un="BatchUser",29 pc=1,30 thresh=0.5,31 full_pipeline=True,32 api_name="/on_run",33)3435print(status)36print(f"Results zip: {zip_path}")3738# Access results as a DataFrame39import pandas as pd
40df = pd.DataFrame(table["data"], columns=table["headers"])41print(df[["image_path","area_mm2","diameter_mm","crack_coverage_pct"]].to_string())
Each image gets its own px_to_mm conversion factor derived from dish detection.
The pipeline detects the 90 mm petri dish via HoughCircles and computes:
px_to_mm = 90.0 / (2 × dish_radius_px)
This means images of different resolutions (e.g. phone camera vs DSLR vs
microscope) are correctly converted to physical mm units independently.
If dish detection fails for an image, px_to_mm defaults to 1.0 and
dish_detected is set to False.
Segmentation
Resize full image to 256 × 256 → SmallUNet → sigmoid probability map
Threshold at user-configurable confidence level (default 0.5)
Resize mask back to original resolution (nearest-neighbour)
Crack Detection
Local adaptive thresholding (Gaussian, block_size=51) inside colony mask
Filter by elongation: aspect ratio > 2.5 or eccentricity > 0.85
Interior erosion (disk radius 5) to remove edge artefacts
Hyphae Detection
Frangi filter: multi-scale vesselness (σ = 1–4)
Meijering filter: neuriteness (σ = 1–4)
Hybrid: union of both skeletonised responses
Analysis region extends 20 px beyond colony boundary
Troubleshooting
Issue
Fix
Model download fails
Check internet; for gated repos set HF_TOKEN
Dish not detected
Full rim must be visible; avoid heavy shadows
Colony not detected
Verify image has visible colony contrast against agar