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 processes each WSI independently using the following steps:
-
Tissue Extraction
- Hematoxylin channel isolation via color deconvolution
- Global thresholding to separate tissue from background
-
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
-
Feature Aggregation
- Gaussian smoothing to generate spatial maps of nuclei size and density at lower resolution
-
Unsupervised Clustering
- Two-component Gaussian Mixture Model (GMM) fit to morphometric features
- GM and WM labels assigned using biologically informed rules
-
Spatial Refinement
- Conditional Random Field (CRF) smoothing
- Morphological post-processing to remove artifacts and small disconnected regions
-
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
- Prepare input data
Place the.svswhole-slide images to be processed inside theData/directory:
NEUSEG/
├── Data/
│ └── *.svs
-
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 neuseg2-2. Clone the NEUSEG repository and install dependencies
git clone https://github.com/PennCompPathology/NEUSEG.git cd NEUSEG python -m pip install -r requirements.txtrequirements.txtis 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 onepdnl_sana, so the editable install above supersedes the PyPI copy; thepdnl_extract/pdnl_process/pdnl_aggregatetools 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 debugRequired arguments
Flag Description -i,--input_slidePath to the input WSI ( .svs, or any format OpenSlide can read). One file per run; to process a cohort, loop over slides or useneuseg/run_cohort.py.-o,--output_directoryDirectory 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_cores1Number 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_levelnormalConsole 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.debugandfulladditionally 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.--entrypointcellsWhich stage to start from, reusing the intermediate files already in -o. See below.Stages and
--entrypointThe pipeline runs in four stages, each writing arrays into the slide's outputs archive that the next one reads back.
--entrypointskips straight to a stage instead of recomputing everything, which makes tuning the segmentation cheap: thecellsstage dominates runtime, whilecortextakes seconds.Value Starts at Requires already in the archive cells(default)Nuclei segmentation over the whole WSI (the full pipeline) nothing featuresAggregating nuclei into the feature heatmaps cells,thumbnailtissueTissue-mask stage feature_heatmapcortexTissue mask → GMM → CRF → contours feature_heatmap,thumbnail,tissue_maskSo 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 debugRemaining options
Flag Default Description --window_size1000.0Radius (µm) over which nuclei are aggregated into the feature heatmaps. Cells are Gaussian-weighted with σ = window_size/ 5.--ds_thumbnail1.0Resolution of the feature heatmap relative to the thumbnail. 1.0computes it at full thumbnail resolution; larger values downsample it.--frame_size1024Size (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. -
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4427a16a74719e415138afeca76235912068267557c335a74a73a66906c4a393
|
|
| MD5 |
f7b5d6ec645ea912ed40bc4a73d65c06
|
|
| BLAKE2b-256 |
5b6a4202ba01a61fe8f3cc90cd99d1a5dac3f1d35cd1ee31f2e98328b6109c74
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06b1d495e2f5e5b72f67e7797e91b4b6e0bc374ffdbd37db26bc8c79f378c03e
|
|
| MD5 |
20122eb76c6fa4f8e0761c18bc1dcb2f
|
|
| BLAKE2b-256 |
f083e2dd645c4ef0d65dec38741cd28541737fd805b70cf7796ef67946c9a746
|