πŸ„ Fungal Colony Image Analysis Pipeline

Downloads (All Time) License DOI

End-to-end analysis pipeline for Magnaporthe (and other fungal) colony morphometry on 90 mm petri-dish images.

β–Ά Try the live demo β€” 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

Property Value
Architecture smp.Unet(encoder_name="resnet34") via segmentation-models-pytorch
Parameters 24.4M
Input 256Γ—256 RGB
Output 1-channel sigmoid mask (threshold 0.5)
Dish detection OpenCV HoughCircles on Gaussian-blurred grayscale
MPS compatible βœ… Pure PyTorch β€” no custom CUDA kernels

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

Panel Colour Shows
Raw + Dish Green circle, red contour Detected dish boundary + colony outline
Colony Mask White on black Binary segmentation mask
Colony Overlay Red 50% blend Colony area highlighted on raw image
Cracks Yellow Detected cracks inside colony (dilated for visibility)
Hyphae Cyan Hyphae skeleton (Frangi + Meijering hybrid filter)
All Combined Red + yellow + cyan Colony + cracks + hyphae together

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
  1. Open http://localhost:7860
  2. Paste image folder path β†’ πŸ“‚ Scan
  3. Fill experiment details (name, start date, user, plates count)
  4. Click thumbnails β†’ edit per-image dates β†’ πŸ’Ύ Save
  5. πŸ“₯ Export β†’ writes image_metadata.csv, .json, reminders.ics to your folder

Step 2: Run Analysis

IMG_DIR=./data python pipeline.py
Variable Default Description
IMG_DIR . Folder with image_metadata.csv + images
MAX_WORKERS 2 Parallel threads (hard cap 2 for 16 GB)
MODEL_REPO rotsl/grayleafspot-segmentation HF model repo
MODEL_FILE grayleafspot.pt Weight file

Outputs: 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_client

Quick 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.zip

Available API Endpoints

Endpoint Description Key Parameters
/on_upload Upload images β†’ gallery files: list of filepaths
/on_sel Select image in gallery ed: experiment date
/on_save Save per-image date/reminder nd: date, nr: reminder, ed: exp date
/on_export Export metadata CSV/JSON/ICS en, ed, un, pc
/on_run Run full pipeline (segmentation + morphometrics + charts) en, ed, un, pc

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

Column Description
image_path Relative path from IMG_DIR
experiment_name Experiment identifier
experiment_date Start date (YYYY-MM-DD)
image_date Auto-detected capture date
day_code d01, d02, …
user_name Researcher
plates_count Number of plates

Calibration

Column Unit Description
dish_cx, dish_cy px Dish centre
dish_radius_px px Dish radius
px_to_mm mm/px Scale factor
calibration_diameter_mm mm Should be β‰ˆ90.0
calibration_error_pct % Target <2%

Colony Morphometry

Column Unit Description
area_mm2 mmΒ² Colony area
diameter_mm mm Equivalent circular diameter
perimeter_mm mm Colony perimeter
eccentricity – 0=circle, 1=line
edge_roughness – Perimeter / equivalent circle perimeter
centre_delta_mm mm Colony centre to dish centre

Texture

Column Description
entropy Shannon entropy
texture_std Pixel intensity Οƒ

Cracks

Column Unit Description
crack_px px Total crack pixels
crack_area_mm2 mmΒ² Total crack area
crack_coverage_pct % Crack / colony area Γ— 100
crack_count – Distinct crack count

Hyphae

Column Unit Description
hyph_frangi_mm mm Frangi vesselness skeleton length
hyph_meijering_mm mm Meijering neuriteness skeleton length
hyph_hybrid_mm mm Union of both

Time-Series

Column Unit Description
days_since_start days From first image
rgr_per_day day⁻¹ (ln Aβ‚‚ βˆ’ ln A₁) / Ξ”days
relative_growth_per_day mmΒ²/day (Aβ‚‚ βˆ’ A₁) / Ξ”days

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

  1. Dish detection: OpenCV GaussianBlur β†’ HoughCircles (HOUGH_GRADIENT, dp=1.2)
  2. Colony segmentation: Resize full image to 256Γ—256 β†’ smp.Unet(resnet34) β†’ sigmoid β†’ threshold 0.5
  3. Resize mask back to original resolution (nearest-neighbour)
  4. Restrict to dish interior (95% of detected radius)
  5. 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.py

Troubleshooting

Issue Fix
torch.mps not available macOS 13+ and PyTorch 2.1+ required
OOM on 16 GB MAX_WORKERS=1
Model download fails Check internet + HF_TOKEN for gated repo
Dish not detected Full rim must be visible, avoid heavy shadows
Colony not detected Verify image has visible colony contrast against agar

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

Downloads last month
1
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support