rotsl/fungal-colony-pipeline
π Fungal Colony Image Analysis Pipeline
  
End-to-end analysis pipeline for Magnaporthe (and other fungal) colony morphometry on 90 mm petri-dish images.
[βΆ Try the live demo](https://huggingface.co/spaces/rotsl/fungal-colony-input) β upload images, run inference, see overlays & growth charts in your browser.
Designed for Apple Silicon Mac (M1/M2/M3 Pro/Max, MPS backend, float32). Also works on CPU (Linux/Windows).
Model
Weights: `rotsl/grayleafspot-segmentation/grayleafspot.pt`
Pipeline Overview
input.py pipeline.py / app.py (Space)
ββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββ
β Gradio GUI β β Read image_metadata.csv β
β - Scan folder β image_metadata. β Load smp.Unet (thread-local) β
β - Tag metadata βββββ csv/json ββββββΆβ For each image: β
β - Export CSV β β 1. OpenCV HoughCircles β dish β
β - Export ICS β β 2. U-Net β colony mask β
ββββββββββββββββββββ β 3. Crack detection (adaptive) β
β 4. Hyphae (Frangi + Meijering) β
β 5. Morphometrics (mm/mmΒ²) β
β 6 overlay panels per image β
β Growth charts (β₯2 images) β
β Output: analysis_full.csv/json β
ββββββββββββββββββββββββββββββββββββββββVisualisation Outputs
6 Overlay Panels Per Image
Growth Charts (when β₯2 images)
- Colony Area (mmΒ²) vs days β with fill + data labels
- Diameter (mm) over time
- Relative Growth Rate (RGR) β bar chart per interval
- Crack Coverage (%) over time
- Hyphae Network Length (mm) over time
- Morphology β eccentricity + edge roughness dual panel
All charts are included as PNGs in the download zip.
Installation (Local)
Prerequisites
- Python 3.10+
- macOS with Apple Silicon recommended (MPS acceleration) β also works on CPU
- ~100 MB disk for model weights (cached in
~/.cache/huggingface)
Setup
# 1. Clone
git clone https://huggingface.co/rotsl/fungal-colony-pipeline
cd fungal-colony-pipeline
# 2. Virtual environment
python3 -m venv .venv
source .venv/bin/activate
# 3. Install
pip install -r requirements.txt
# 4. (Optional) Pre-download model
python -c "
from huggingface_hub import hf_hub_download
p = hf_hub_download('rotsl/grayleafspot-segmentation', 'grayleafspot.pt')
print(f'Downloaded to: {p}')
"Usage (Local)
Step 1: Tag Images with Metadata
python input.py- Open
http://localhost:7860 - Paste image folder path β π Scan
- Fill experiment details (name, start date, user, plates count)
- Click thumbnails β edit per-image dates β πΎ Save
- π₯ Export β writes
image_metadata.csv,.json,reminders.icsto your folder
Step 2: Run Analysis
IMG_DIR=./data python pipeline.pyOutputs: analysis_full.csv + analysis_full.json in IMG_DIR
Usage via HF API (Programmatic Access)
You can run the full pipeline remotely via the Gradio Client without installing anything locally. The Space exposes five API endpoints.
Install
pip install gradio_clientQuick Start β Upload + Run Pipeline
from gradio_client import Client, handle_file
client = Client("rotsl/fungal-colony-input")
# Step 1: Upload images
result = client.predict(
files=[
handle_file("plate_d01.jpg"),
handle_file("plate_d03.jpg"),
handle_file("plate_d05.jpg"),
],
api_name="/on_upload",
)
# result = (gallery_items, status_markdown)
# Step 2: Run the full analysis pipeline
analysis = client.predict(
en="MagExp01", # experiment name
ed="2025-04-01", # experiment start date
un="YourName", # user name
pc=1, # plates count
api_name="/on_run",
)
# analysis is a tuple:
# [0] status message (str)
# [1] overlay gallery β list of dicts with 'image' paths (6 panels per input image)
# [2] growth chart gallery β list of dicts with 'image' paths
# [3] results dataframe (dict with 'headers' and 'data')
# [4] path to analysis_full.zip
status_msg = analysis[0]
overlays = analysis[1] # list of {image: filepath, caption: str}
charts = analysis[2] # list of {image: filepath, caption: str}
results_table = analysis[3] # {"headers": [...], "data": [[...], ...]}
zip_path = analysis[4] # local path to downloaded analysis_full.zip
print(status_msg)
print(f"Overlays: {len(overlays)} panels")
print(f"Charts: {len(charts)}")
print(f"Results: {len(results_table['data'])} rows Γ {len(results_table['headers'])} cols")
print(f"Download: {zip_path}")Export Metadata Only (no inference)
meta = client.predict(
en="MagExp01",
ed="2025-04-01",
un="YourName",
pc=1,
api_name="/on_export",
)
# meta[0] = status message
# meta[1] = metadata dataframe
# meta[2] = path to image_metadata.zipAvailable API Endpoints
cURL Example
# Upload images and run pipeline via REST API
# (Gradio uses a session-based API β the Python client is recommended)
curl -X POST https://rotsl-fungal-colony-input.hf.space/gradio_api/call/on_upload \
-H "Content-Type: application/json" \
-d '{
"data": [
[{"path": "https://your-server.com/plate_d01.jpg"}]
]
}'Note: For multi-step workflows (upload β run), use the Python gradio_client which handles session state automatically. Direct REST calls require managing the session hash between requests.Batch Processing Script
"""Process a folder of petri dish images via the HF Space API."""
from pathlib import Path
from gradio_client import Client, handle_file
IMAGE_DIR = Path("./my_experiment")
EXPERIMENT = "MagExp01"
START_DATE = "2025-04-01"
client = Client("rotsl/fungal-colony-input")
# Collect all images
images = sorted(
p for p in IMAGE_DIR.rglob("*")
if p.suffix.lower() in {".jpg", ".jpeg", ".png", ".tif", ".bmp", ".webp"}
)
print(f"Found {len(images)} images")
# Upload
client.predict(
files=[handle_file(str(p)) for p in images],
api_name="/on_upload",
)
# Run pipeline
status, overlays, charts, table, zip_path = client.predict(
en=EXPERIMENT,
ed=START_DATE,
un="BatchUser",
pc=1,
api_name="/on_run",
)
print(status)
print(f"Results zip: {zip_path}")
# Access results as a DataFrame
import pandas as pd
df = pd.DataFrame(table["data"], columns=table["headers"])
print(df[["image_path", "area_mm2", "diameter_mm", "crack_coverage_pct"]].to_string())Output Columns
Metadata
Calibration
Colony Morphometry
Texture
Cracks
Hyphae
Time-Series
R Studio Integration
library(readr)
library(dplyr)
library(ggplot2)
df <- read_csv("analysis_full.csv")
# Growth curve
df %>%
filter(is.na(error) | error == "") %>%
ggplot(aes(x = days_since_start, y = area_mm2, color = experiment_name)) +
geom_line() + geom_point() +
labs(x = "Days", y = "Colony Area (mmΒ²)", title = "Magnaporthe Growth") +
theme_minimal()
# Morphology summary
df %>%
filter(is.na(error) | error == "") %>%
group_by(experiment_name) %>%
summarise(
n = n(),
mean_area = mean(area_mm2, na.rm = TRUE),
mean_roughness = mean(edge_roughness, na.rm = TRUE),
mean_crack_pct = mean(crack_coverage_pct, na.rm = TRUE),
total_hyphae = sum(hyph_hybrid_mm, na.rm = TRUE)
)
# RGR
df %>%
filter(!is.na(rgr_per_day) & rgr_per_day != "") %>%
mutate(rgr_per_day = as.numeric(rgr_per_day)) %>%
ggplot(aes(x = days_since_start, y = rgr_per_day)) +
geom_col(fill = "steelblue") +
facet_wrap(~ experiment_name) +
labs(x = "Days", y = "RGR (dayβ»ΒΉ)") +
theme_minimal()library(jsonlite)
df <- fromJSON("analysis_full.json")
meta <- read_csv("image_metadata.csv")Technical Notes
Segmentation Strategy
- Dish detection: OpenCV
GaussianBlurβHoughCircles(HOUGH_GRADIENT, dp=1.2) - Colony segmentation: Resize full image to 256Γ256 β
smp.Unet(resnet34)β sigmoid β threshold 0.5 - Resize mask back to original resolution (nearest-neighbour)
- Restrict to dish interior (95% of detected radius)
- Cleanup: OpenCV morphological close/open, keep largest connected component
Crack Detection
- Local adaptive thresholding inside colony mask
- Filter by elongation (aspect ratio > 2.5 or eccentricity > 0.85)
- Edge artefacts removed via erosion
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
Memory Management
- Max 2 parallel threads β prevents OOM on 16 GB
torch.mps.empty_cache()after each image- Thread-local model loading
- Float32 throughout
File Structure
your_image_folder/
βββ subdir_a/
β βββ mag01_20250401_01.jpg
β βββ mag01_20250402_01.jpg
βββ image_metadata.csv β input.py
βββ image_metadata.json β input.py
βββ reminders.ics β input.py (if reminders)
βββ analysis_full.csv β pipeline.py
βββ analysis_full.json β pipeline.pyTroubleshooting
Citation
@misc{rohan_r_2026,
author = { rohan r },
title = { fungal-colony-pipeline (Revision e51373b) },
year = 2026,
url = { https://huggingface.co/rotsl/fungal-colony-pipeline },
doi = { 10.57967/hf/8570 },
publisher = { Hugging Face }
}License
Apache License 2.0
