Skip to main content

NEUSEG (Hitopathology WSI GM/WM Segmentation)

Interpretable Unsupervised GM/WM Segmentation in Brain Histopathology Using Nuclei Morphometrics

NEUSEG is a fully automated, unsupervised, and interpretable pipeline for gray matter (GM) and white matter (WM) segmentation in brain histopathology whole-slide images (WSIs). The method bridges cellular-scale nuclei morphometrics with tissue-scale segmentation, enabling robust GM/WM delineation across heterogeneous stains, cortical regions, and neurodegenerative pathologies without requiring training data or GPUs.


Motivation

Accurate GM/WM segmentation is a critical prerequisite for quantitative analysis of brain histopathology, particularly for studying neurodegenerative disease progression. However, WSIs pose several challenges:

  • Gigapixel-scale image resolution
  • Large variability in staining, scanners, tissue preparation, and pathology
  • Labor-intensive and subjective manual annotation
  • Limited generalization and interpretability of supervised deep learning approaches

NEUSEG addresses these challenges by providing a lightweight, CPU-operable, and biologically grounded alternative to supervised CNN-based pipelines.


Key Contributions

  • Unsupervised GM/WM segmentation using nuclei size and density, requiring no annotated training data
  • Interpretable feature design, grounded in known cytoarchitectural differences between GM and WM
  • Robust performance under domain shift, outperforming a supervised CNN baseline on out-of-distribution slides
  • Scalable CPU-only implementation, processing WSIs in ~1–2 minutes per slide
  • Extensive validation across 252 WSIs spanning multiple stains, cortical regions, and neurodegenerative pathologies

Method Overview

NEUSEG pipeline NEUSEG processes each WSI independently using the following steps:

  1. Tissue Extraction

    • Hematoxylin channel isolation via color deconvolution
    • Global thresholding to separate tissue from background
  2. Nuclei Segmentation & Feature Extraction

    • Enhance nuclear contrast (pre-processing): apply triangular thresholding to the hematoxylin channel within the tissue mask to generate an intensity map that highlights nuclei (neurons + glia)
    • Nuclei instance segmentation: perform watershed-based segmentation on non-overlapping patches
    • Morphometric feature extraction: compute
      • Nuclear size
      • Local nuclear density
  3. Feature Aggregation

    • Gaussian smoothing to generate spatial maps of nuclei size and density at lower resolution
  4. Unsupervised Clustering

    • Two-component Gaussian Mixture Model (GMM) fit to morphometric features
    • GM and WM labels assigned using biologically informed rules
  5. Spatial Refinement

    • Conditional Random Field (CRF) smoothing
    • Morphological post-processing to remove artifacts and small disconnected regions
  6. Contour Extraction

    • GM–WM, GM–background, and WM–background boundaries for evaluation and downstream analysis

Why NEUSEG?

  • ✅ No training data required
  • ✅ Interpretable and biologically grounded
  • ✅ Robust to staining and pathology variability
  • ✅ Scales efficiently to hundreds of WSIs
  • ✅ Suitable for downstream quantitative pathology analyses

NEUSEG is particularly well-suited for large-scale studies where reproducibility, robustness, and interpretability are essential.


Code Usage

This repository provides an end-to-end pipeline for unsupervised GM/WM segmentation from brain histopathology whole-slide images (WSIs) using nuclei morphometrics.

Core Scripts

  • script/Nuclei_Segmentation.py
    Performs nuclei segmentation from histopathology WSIs. This step generates nuclei masks and extracts morphometric features (e.g., nuclear size and local nuclear density), which are used as inputs for downstream tissue segmentation.

  • script/GMM_Segmentation.py
    Performs GM/WM segmentation using a Gaussian Mixture Model (GMM) based on features derived from the nuclei segmentation results.

  • script/neuseg_script.sh
    Wrapper shell script that runs the full NEUSEG pipeline, including nuclei segmentation followed by GMM-based GM/WM segmentation.

Running the Pipeline

  1. Prepare input data
    Place the .svs whole-slide images to be processed inside the Data/ directory:
NEUSEG/
├── Data/
│   └── *.svs
  1. Run NEUSEG From the root of the repository, first set up the Python environment and install all required dependencies.

    2-1. Create and activate the conda environment

    conda create -n neuseg python=3.12 -y
    conda activate neuseg
    

    2-2. Clone the NEUSEG repository and install dependencies

    git clone https://github.com/PennCompPathology/NEUSEG.git
    cd NEUSEG
    python -m pip install -r requirements.txt
    

    requirements.txt is a single file covering all three components: the core pipeline, the annotator, and the evaluation notebook. It is grouped into labelled sections so you can see which package belongs to which component, but everything installs in one command.

    2-3. Install the SANA dependency (required)

    git clone https://github.com/penndigitalneuropathlab/sana.git
    cd sana
    python -m pip install -r src/pdnl_sana/requirements.txt
    python -m pip install -e .
    

    Run the following command to confirm that sana was installed correctly:

    python3 -c "import pdnl_sana.image; import pdnl_sana.slide"
    

    To confirm it is this clone that is active, and not the PyPI build that step 2-2 pulls in as a transitive dependency of pdnl_extract:

    python3 -c "import pdnl_sana, os; print(os.path.dirname(pdnl_sana.__file__))"
    

    The printed path should point inside the sana/ directory you just cloned. An environment only ever holds one pdnl_sana, so the editable install above supersedes the PyPI copy; the pdnl_extract / pdnl_process / pdnl_aggregate tools used by the evaluation notebook resolve to this clone as well.

    2-4. Run the NEUSEG pipeline

    The pipeline is driven by neuseg/main.py, which processes one WSI per invocation. From the root of the repository:

    python neuseg/main.py \
      -i Data/slide.svs \
      -o GM_WM_Seg_Results/slide \
      --n_cores 8 \
      --debug_level debug
    

    Required arguments

    Flag Description
    -i, --input_slide Path to the input WSI (.svs, or any format OpenSlide can read). One file per run; to process a cohort, loop over slides or use neuseg/run_cohort.py.
    -o, --output_directory Directory that receives every output for this slide (see Outputs below). Created automatically if it does not exist. Every output file is named after the input slide, so several slides may safely share one output directory.

    Frequently used options

    Flag Default Description
    --n_cores 1 Number of worker processes used to segment nuclei and aggregate features. Set it to the number of cores you want to occupy; runtime for the nuclei stage scales close to linearly.
    --debug_level normal Console verbosity, and whether diagnostic figures are written. quiet → errors only; normal → progress bars and warnings; debug → adds per-stage parameters and statistics; full → maximum verbosity. debug and full additionally write the four diagnostic PNGs listed under Outputs.
    --debug_directory (= -o) Where those diagnostic PNGs are written. Give each slide its own directory here when several slides share one -o, since the figure filenames are fixed. Created automatically, and only when figures are actually written.
    --entrypoint cells Which stage to start from, reusing the intermediate files already in -o. See below.

    Stages and --entrypoint

    The pipeline runs in four stages, each writing arrays into the slide's outputs archive that the next one reads back. --entrypoint skips straight to a stage instead of recomputing everything, which makes tuning the segmentation cheap: the cells stage dominates runtime, while cortex takes seconds.

    Value Starts at Requires already in the archive
    cells (default) Nuclei segmentation over the whole WSI (the full pipeline) nothing
    features Aggregating nuclei into the feature heatmaps cells, thumbnail
    tissue Tissue-mask stage feature_heatmap
    cortex Tissue mask → GMM → CRF → contours feature_heatmap, thumbnail, tissue_mask

    So to re-run only the GM/WM segmentation after an initial full run, for example to inspect the diagnostic figures, reuse the existing outputs:

    python neuseg/main.py \
      -i Data/slide.svs \
      -o GM_WM_Seg_Results/slide \
      --entrypoint cortex \
      --debug_level debug
    

    Remaining options

    Flag Default Description
    --window_size 1000.0 Radius (µm) over which nuclei are aggregated into the feature heatmaps. Cells are Gaussian-weighted with σ = window_size / 5.
    --ds_thumbnail 1.0 Resolution of the feature heatmap relative to the thumbnail. 1.0 computes it at full thumbnail resolution; larger values downsample it.
    --frame_size 1024 Size (px) of the chunks the WSI is read in. Affects memory use and I/O only, not results.
    --tmp_directory (auto) Location for intermediate chunk files. Defaults to a temporary directory that is deleted on exit; set this to keep them.
  2. Outputs

    Every array the pipeline produces goes into a single compressed archive inside the directory given by -o:

   <output_directory>/<slide_name>_neuseg.npz

where <slide_name> is the input filename minus its extension, so Data/slide.svs yields slide_neuseg.npz. Each stage adds its arrays to the archive and leaves the earlier ones in place; which keys are present therefore depends on --entrypoint, and a default full run produces all of them.

Load it with np.load, which returns a dict-like object keyed by the names below:

import json
import numpy as np

with np.load('GM_WM_Seg_Results/slide/slide_neuseg.npz') as z:
    print(z.files)                            # which stages have run
    gm_mask = z['gm_mask']
    contours = json.loads(str(z['gmwm_contours']))

Archive contents (always written)

Key Stage Contents
thumbnail cells (h, w, 3) uint8 RGB thumbnail of the WSI. Every later stage works on this grid, and the final masks are returned at this resolution.
tissue_mask cells (h, w, 1) uint8 tissue mask at thumbnail resolution, 1 inside tissue, from tissue_extraction.py. Written once, while segmenting nuclei, and reused unchanged by cortex, so it always holds the mask the GM/WM segmentation was run against.
cells cells (N, 4) array, one row per segmented nucleus: x, y (level-0 slide pixels), soma area, and mean hematoxylin intensity.
feature_heatmap features (H, W, 2) float32 array of the aggregated morphometric maps: channel 0 soma density, 1 average soma size. (A third channel, average soma intensity, is computed for the diagnostic figure but not stored — the GMM does not use it.)
gm_mask cortex (h, w) boolean gray matter mask at thumbnail resolution.
wm_mask cortex (h, w) boolean white matter mask; together with gm_mask it partitions the tissue.
gmwm_contours cortex Boundary polygons as a JSON string in a 0-d array; recover it with json.loads(str(z['gmwm_contours'])). Gives {"shape": [h, w], "gm_wm": [...], "gm_csf": [...]} in thumbnail pixel coordinates, where each boundary is a list of [[x, y], ...] rings.

gmwm_contours is the last key written, which makes it the marker of a finished slide — run_cohort.py tests for it to skip slides already done and resume an interrupted cohort run.

Diagnostic figures (only when --debug_level is debug or full)

Written to --debug_directory, which defaults to -o.

File Stage What it shows
run_features_output.png features Four panels: the segmented nuclei plotted over the thumbnail, then the three feature maps (soma density, average soma size, average soma intensity). The title records window_size in µm, level-0 and thumbnail pixels, the Gaussian σ used to smooth them, and ds_thumbnail.
gmm_result.png cortex Eight panels tracing the GM/WM fit from raw cells to labels. Top row: thumbnail, segmented nuclei, and the density and size maps restricted to tissue, with pixels excluded from the fit marked in cyan. Bottom row: the resulting GM/WM labels over the thumbnail, then the standardized feature space as a scatter, coloured by fitted component with each component's mean and 1/2/3-σ ellipses, and again coloured by raw density and raw size. Useful for confirming the two components really separate.
post_process.png cortex Three panels showing spatial refinement step by step: the raw GMM labels, the result after CRF smoothing, and the result after small disconnected regions are pruned. Pixels changed by each step are marked in red and counted in the legend.
contour.png cortex The final segmentation: the thumbnail with GM and WM tinted, overlaid with the three extracted boundaries: GM–WM (green), GM–background (magenta), and WM–background (black).

A pickled logger state is also written beside the archive as <slide_name>_log.pkl, and a plain-text run log to NEUSEG.log in the current working directory rather than in -o.

Citation

NEUSEG: Interpretable Unsupervised Gray/White Matter Segmentation for Brain Histopathology WSIs IEEE ISBI 2026

If you use NEUSEG in your work, please cite:

Roh, Hyung Seok, et al. "NEUSEG: Interpretable Unsupervised GM/WM Segmentation in Brain Histopathology Using Nuclei Morphometrics." 2026 IEEE 23rd International Symposium on Biomedical Imaging (ISBI). IEEE, 2026.

Acknowledgments

This work was supported by the National Institutes of Health and institutional funding at the University of Pennsylvania.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neuseg-0.1.tar.gz (35.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

neuseg-0.1-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

Details for the file neuseg-0.1.tar.gz.

File metadata

  • Download URL: neuseg-0.1.tar.gz
  • Upload date:
  • Size: 35.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for neuseg-0.1.tar.gz
Algorithm Hash digest
SHA256 4427a16a74719e415138afeca76235912068267557c335a74a73a66906c4a393
MD5 f7b5d6ec645ea912ed40bc4a73d65c06
BLAKE2b-256 5b6a4202ba01a61fe8f3cc90cd99d1a5dac3f1d35cd1ee31f2e98328b6109c74

See more details on using hashes here.

File details

Details for the file neuseg-0.1-py3-none-any.whl.

File metadata

  • Download URL: neuseg-0.1-py3-none-any.whl
  • Upload date:
  • Size: 29.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for neuseg-0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 06b1d495e2f5e5b72f67e7797e91b4b6e0bc374ffdbd37db26bc8c79f378c03e
MD5 20122eb76c6fa4f8e0761c18bc1dcb2f
BLAKE2b-256 f083e2dd645c4ef0d65dec38741cd28541737fd805b70cf7796ef67946c9a746

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page