🆕 Add DeepSpot-M model (H&E → virtual spatial transcriptomics) - #1103
Conversation
DeepSpot-M (Nonchev et al., medRxiv 2026) maps a 224x224 H&E tile to transcriptome-wide spatial gene expression via a LoRA-adapted Midnight backbone and a cross-attention gene decoder. It is wrapped as a ModelABC and runs through the existing DeepFeatureExtractor engine, so a whole-slide image yields an (n_tiles, n_genes) expression matrix with tile coordinates. A `genes=[...]` subset computes only those gene queries (faster, lean output). The `deepspotm` package is an optional, lazily imported dependency; the tests inject a fake module so CI exercises the wrapper (gene subsetting, tile preprocessing, infer_batch contract, ImportError path) without the gated ratschlab/DeepSpotM weights. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1103 +/- ##
========================================
Coverage 99.88% 99.88%
========================================
Files 86 87 +1
Lines 11670 11710 +40
Branches 1533 1537 +4
========================================
+ Hits 11657 11697 +40
Misses 7 7
Partials 6 6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
isinstance(batch_data, torch.Tensor) -> .cpu().numpy() branch is exercised, restoring 100% patch coverage flagged by Codecov on TissueImageAnalytics#1103. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a torch.Tensor input case for infer_batch so the isinstance(batch_data, torch.Tensor) -> .cpu().numpy() branch is exercised, restoring 100% patch coverage flagged by Codecov on TissueImageAnalytics#1103. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
afe4934 to
d566604
Compare
|
Thanks @KalinNonchev Please can you also add a Jupyter Notebook? You can use one of the existing inference pipelines (https://github.com/TissueImageAnalytics/tiatoolbox/tree/develop/examples/inference-pipelines) Jupyter Notebooks as a template. |
Adds examples/inference-pipelines/deepspotm.ipynb, following the layout of the existing inference-pipeline notebooks: Colab badge, `remove-cell` setup and environment cells, device selection, `tmp/` cleanup, and the shared TCGA sample slide. The notebook builds a DeepSpotM model restricted to three marker genes, runs it over the whole slide through DeepFeatureExtractor with an IOPatchPredictorConfig at 224x224 and 0.5 mpp, reads the (n_tiles, n_genes) matrix back out of the zarr output, and overlays the predicted expression on the slide thumbnail. It closes with the `genes=None` full-panel form and the citation and licence terms. EPCAM, PTPRC and COL1A1 were chosen so each marker reports on a different tissue compartment, which makes the overlay readable as a sanity check rather than three similar-looking maps. Outputs are from a real run: 6,467 tissue tiles scored on one GPU, about 16 minutes end to end including the slide download and the model load. Also adds the notebook to the Inference Pipelines section of examples/README.md with a thumbnail at docs/images/deepspotm.png, matching the other entries.
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
|
Thanks @shaneahmed, added in 3ba95fb.
It builds a The committed outputs are from a real run rather than a cleared notebook: 6,467 tissue tiles scored on one GPU, roughly 16 minutes end to end including the slide download and the gated-weight load. I also added the entry to the Inference Pipelines section of
Two notes in case they matter for review:
|
`deepspotm` 1.0.0 was published to PyPI, so the optional dependency no longer
needs a git URL.
- setup.py declares `extras_require={"deepspotm": ["deepspotm>=1.0.0"]}`, so
`pip install tiatoolbox[deepspotm]` pulls the optional model dependency.
- The module docstring and the lazy-import ImportError point at the extra,
keeping `pip install deepspotm` as a fallback for anyone on a released
tiatoolbox that predates the extra.
- The notebook's Colab bootstrap installs from PyPI instead of the git URL.
Note that setup.py had no extras_require before; this adds the first one. The
blank line after the module docstring is what ruff-format requires on the
touched file.
|
@Jiaqi-Lv just approved your HF request. Please let me know if you need anything else. |
|
Looks great. I'm reviewing this PR.
Thank you! I'll review this soon. |
|
I've made some minor changes:
|
Jiaqi-Lv
left a comment
There was a problem hiding this comment.
Looks good, works well for me.
|
Merge? @Jiaqi-Lv |
What
Adds DeepSpot-M (Nonchev et al., medRxiv 2026) as a
ModelABCintiatoolbox.models.architecture.deepspotm. DeepSpot-M maps a 224×224 H&E tile totranscriptome-wide spatial gene expression via a LoRA-adapted Midnight backbone and
a cross-attention gene decoder, so one model predicts a ~19k-gene panel (and
zero-shot genes) from histology.
It runs through the existing
DeepFeatureExtractorengine — each output "feature"column is a predicted gene, so a whole-slide image yields an
(n_tiles, n_genes)expression matrix with matching tile coordinates:
Why
DeepFeatureExtractorDeepSpot-M is image-in and produces a per-tile vector, so it fits the
feature-extractor contract:
forwardreturns the expression tensor andinfer_batchapplies DeepSpot-M's own image transform to the NHWC patches andreturns
[features]. Agenes=[...]subset computes only those gene queries(faster, and far fewer columns to store). This mirrors how the foundation tile
encoders (UNI / Virchow / H-optimus via
TimmBackbone) are wired as models passedto the engine.
Licensing / access
ratschlab/DeepSpotMHugging Face repo (accept theterms and authenticate) — consistent with the existing gated encoders (UNI,
Virchow, …).
research use only.
deepspotmis an optional dependency, imported lazily with a clear installhint (
pip install git+https://github.com/ratschlab/DeepSpotM.git). It is not yeton PyPI; happy to add an extras entry once it is.
Testing
tests/models/test_arch_deepspotm.pyinjects a fakedeepspotmmodule, so thewrapper (gene subsetting, tile preprocessing,
infer_batchshape/contract, and theImportErrorpath) is exercised in CI without gated weights or the optionaldependency.
ruff checkandruff formatare clean against the repo config.Follow-ups (happy to add)
ModelTask.spatial_transcriptomicsif the maintainers prefer it overthe feature-extractor path.
🤖 Generated with Claude Code