Supervised deep-learning thresholding of imaging mass cytometry (IMC) channels.
DeepThresh replaces manual, per-channel intensity thresholding of IMC images with a bank of small segmentation networks: one binary U-Net per channel, trained on a handful of expert-thresholded regions of interest (ROIs) and then applied to every ROI in the dataset. It was developed to threshold extracellular-matrix (ECM) and cellular markers in mouse lung IMC data for the study below.
Parkinson JE, Bryant M, Ghafoor M, et al. Extracellular matrix phenotyping by imaging mass cytometry defines distinct cellular matrix environments associated with allergic airway inflammation. Molecular Systems Biology (2026). doi:10.1038/s44320-026-00234-5
| Stage | Detail |
|---|---|
| Input | A single IMC channel (one metal / protein) from each ROI, as a 2-D TIFF |
| Normalisation | Every image is histogram-matched to a reference ROI, then min-max scaled to [0, 1] |
| Tiling | Overlapping 224 × 224 windows (stride 28) for training; non-overlapping zero-padded 224 × 224 blocks at inference |
| Model | segmentation_models_pytorch U-Net with a ResNet-152 encoder (ImageNet weights, frozen), 1 input channel, 1 output channel, sigmoid activation |
| Training | Dice loss, Adam (lr 1e-4), batch 32, 10 epochs; the checkpoint with the best validation IoU (threshold 0.5) is kept |
| Augmentation | Horizontal flip, shift / scale (scale ±0.5, shift ±0.1), random 224 × 224 crop (albumentations) |
| Output | Binary mask per channel per ROI, reassembled from the predicted blocks |
One model is trained per channel, so a 37-channel panel yields 37 independent models. Each model only ever sees its own channel, which keeps the problem simple and lets a channel be re-trained on its own if the expert thresholds change.
The paper's IMC dataset comprises 36 ROIs of mouse lung (BALB/c and C57BL/6 mice, allergen-challenged vs. PBS control) acquired with a 37-marker panel that includes ECM targets (collagens I / III / IV / VI, fibronectin, laminin-γ1, hyaluronan-binding protein, heparan and chondroitin sulphate, fibrinogen) alongside immune, epithelial and stromal markers.
Six ROIs were thresholded by hand by an expert biologist and used as the supervised signal for every channel:
| Split | ROIs |
|---|---|
| Train | 4 |
| Validation | 1 |
| Test | 1 |
Raw images and expert masks are not distributed in this repository. Trained weights are available on request (see Contact).
DeepThresh/
├── utils/
│ ├── model_per_channel_utils.ipynb # preprocessing, tiling, dataset, training loop (train_single_models)
│ └── trained_model.ipynb # single_ch_model: load a trained .pth and threshold new images
├── Results/
│ ├── In115_prediction.png # example: Ym2 channel
│ └── Nd146_prediction.png # example: SPC channel
└── docs/
└── deepthresh_overview.svg
Python ≥ 3.8 with a CUDA-capable GPU is recommended. The training utilities rely
on the smp.utils training loop that was removed from later releases of
segmentation_models_pytorch, so the version is pinned:
pip install torch torchvision
pip install segmentation_models_pytorch==0.1.0 albumentations scikit-image tifffile scikit-learn pandas matplotlib tqdmutils/model_per_channel_utils.ipynb expects two directory trees with matching
file names per channel — raw channel TIFFs and the corresponding expert binary
masks — and globs them with the channel identifier (e.g. Nd146). Adjust the two
glob() paths at the top of preprocess_data() to point at your data, then:
targets = ['In115', 'Nd146', 'Tm169', ...] # one entry per channel / metal tag
train_single_models(targets)For each target this writes:
single_model_results_unet_v2/
├── trained_models/<target>_best_model.pth # best-validation-IoU checkpoint
├── model_results/<target>_metrics.csv # test-set accuracy / precision / recall / F1
└── test_predictions/<target>_prediction.png # raw | manual threshold | predicted threshold
utils/trained_model.ipynb defines single_ch_model, which loads a checkpoint,
normalises a list of images of that channel, predicts block-wise and stitches the
blocks back to full-ROI binary masks:
from tifffile import imread
imgs = [imread(p) for p in nd146_paths] # list of 2-D arrays, one per ROI
model = single_ch_model('trained_models/Nd146_best_model.pth', imgs)
model.predict()
masks = model.thresholded_images # (n_rois, H, W) binary arrayNote.
predict()currently reshapes each ROI into a fixed 4 × 3 grid of 224 × 224 blocks (the ROI size used in the paper). For ROIs of a different size, change the tworeshapecalls inpredict()to match the block grid returned byimage_to_blocks().
Raw channel, expert manual threshold and DeepThresh prediction on the held-out test ROI for two channels:
If you use DeepThresh, please cite the paper it was developed for:
@article{Parkinson2026_ECM_IMC,
title = {Extracellular matrix phenotyping by imaging mass cytometry defines distinct cellular matrix environments associated with allergic airway inflammation},
author = {Parkinson, James E. and Bryant, Morgan and Ghafoor, Mohamed and Dodd, Rebecca J. and Tompkins, Hannah E. and Fergie, Martin and Burgess, Matthew O. and Rattray, Magnus and Sutherland, Tara E.},
journal = {Molecular Systems Biology},
year = {2026},
doi = {10.1038/s44320-026-00234-5},
url = {https://doi.org/10.1038/s44320-026-00234-5}
}A machine-readable version is in CITATION.cff (GitHub's
"Cite this repository" button).
- Mantpy — scverse framework for ECM analysis in spatial proteomics, which builds on thresholded ECM channels such as those produced here.
Mohamed Ghafoor — moeghaf@gmail.com

