Skip to content

Functions to QC histopathology images - #1036

Merged
timtreis merged 16 commits into
mainfrom
bugfix/issue1034-function-to-qc-he-images
Apr 10, 2026
Merged

Functions to QC histopathology images#1036
timtreis merged 16 commits into
mainfrom
bugfix/issue1034-function-to-qc-he-images

Conversation

@timtreis

Copy link
Copy Markdown
Member

IMPORTANT: Please search among the Pull requests before creating one.

Description

How has this been tested?

Closes

@timtreis timtreis linked an issue Sep 14, 2025 that may be closed by this pull request
@timtreis timtreis changed the title MVP for image QC Functions to QC histopathology images Sep 14, 2025
@codecov

codecov Bot commented Oct 29, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.78505% with 167 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.56%. Comparing base (8f8f736) to head (4a6e266).
⚠️ Report is 36 commits behind head on main.

Files with missing lines Patch % Lines
src/squidpy/experimental/im/_sharpness_metrics.py 29.23% 45 Missing and 1 partial ⚠️
src/squidpy/experimental/pl/_qc_image.py 60.22% 23 Missing and 12 partials ⚠️
src/squidpy/experimental/im/_qc_image.py 82.48% 18 Missing and 13 partials ⚠️
src/squidpy/experimental/im/_utils.py 72.38% 21 Missing and 8 partials ⚠️
src/squidpy/experimental/im/_intensity_metrics.py 54.54% 24 Missing and 1 partial ⚠️
src/squidpy/experimental/im/_make_tiles.py 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1036      +/-   ##
==========================================
- Coverage   74.05%   73.56%   -0.50%     
==========================================
  Files          39       44       +5     
  Lines        6495     6929     +434     
  Branches     1122     1174      +52     
==========================================
+ Hits         4810     5097     +287     
- Misses       1230     1347     +117     
- Partials      455      485      +30     
Files with missing lines Coverage Δ
src/squidpy/experimental/im/_detect_tissue.py 67.77% <100.00%> (ø)
src/squidpy/experimental/im/_qc_metrics.py 100.00% <100.00%> (ø)
src/squidpy/experimental/im/_make_tiles.py 72.72% <90.90%> (-1.07%) ⬇️
src/squidpy/experimental/im/_intensity_metrics.py 54.54% <54.54%> (ø)
src/squidpy/experimental/im/_utils.py 61.33% <72.38%> (+23.03%) ⬆️
src/squidpy/experimental/im/_qc_image.py 82.48% <82.48%> (ø)
src/squidpy/experimental/pl/_qc_image.py 60.22% <60.22%> (ø)
src/squidpy/experimental/im/_sharpness_metrics.py 29.23% <29.23%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/squidpy/experimental/im/_detect_tissue.py Outdated
Comment thread src/squidpy/experimental/im/_detect_tissue.py Outdated
Comment thread src/squidpy/gr/_utils.py Outdated
Comment thread src/squidpy/gr/_ligrec.py Outdated
Comment thread src/squidpy/experimental/im/_utils.py Outdated
Comment thread src/squidpy/experimental/im/_utils.py Outdated
Comment thread src/squidpy/experimental/im/_utils.py Outdated
Comment thread src/squidpy/experimental/im/_sharpness_metrics.py Outdated
Comment thread src/squidpy/experimental/im/_sharpness_metrics.py Outdated
Comment thread src/squidpy/experimental/im/_qc_metrics.py Outdated
Comment thread src/squidpy/experimental/im/_qc_metrics.py
Comment thread src/squidpy/experimental/im/_sharpness_metrics.py
Comment thread src/squidpy/experimental/im/_sharpness_metrics.py Outdated
Comment thread hatch.toml
Comment thread tests/conftest.py

@selmanozleyen selmanozleyen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things to do:

  • Sync with main (some files are old)
  • Vectorize for loops I mentioned
  • Use scikit-image when we can

@timtreis
timtreis force-pushed the bugfix/issue1034-function-to-qc-he-images branch from e4890cc to 5952a86 Compare February 26, 2026 15:43
Replaces the earlier qc_sharpness prototype with a general-purpose
qc_image function that computes tile-based QC metrics on spatial images.

Compute (sq.experimental.im.qc_image):
- Tile-based metrics: sharpness (tenengrad, var_of_laplacian), intensity
  (brightness, entropy), staining (hematoxylin/eosin via HED deconvolution),
  and artifact detection (fold fraction, tissue fraction)
- QCMetric enum and registry mapping each metric to its input kind and
  callable
- Percentile-rank unfocus scoring within tissue tiles for outlier detection
- Preview overlay showing flagged tiles on the image
- Shared utilities in _utils.py: vectorized TileGrid (numpy + shapely.box),
  mask helpers, and shapes persistence (also used by make_tiles)

Plot (sq.experimental.pl.qc_image):
- Multi-panel summary: spatial view, KDE distribution (tissue vs background),
  and descriptive statistics per metric

Metrics use scikit-image filters (sobel_h/v, laplace) instead of hand-rolled
convolutions, and thread-safe HED caching avoids redundant deconvolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@timtreis
timtreis force-pushed the bugfix/issue1034-function-to-qc-he-images branch from 5952a86 to a5e4dfd Compare February 26, 2026 15:45
Comment thread src/squidpy/experimental/im/_qc_metrics.py Outdated
timtreis and others added 2 commits March 30, 2026 16:21
…ficiency

- Rename _ensure_tissue_mask → _resolve_tissue_mask (clearer intent)
- Split intensity/staining/artifact metrics into _intensity_metrics.py,
  keeping _qc_metrics.py as a lean registry + enums module
- Remove redundant tissue_similarity/background_similarity columns
- Simplify _detect_tissue_from_mask → _classify_tiles_by_tissue (accept
  TileGrid, return 2 arrays instead of 4, reuse pre-binarized mask)
- Cache FFT frequency grid via @lru_cache (avoid recomputing per tile)
- Pre-compute grayscale luminance weights as module-level constant
- Fix O(n) var_names lookup in plotting with dict
- Reduce allocations in _entropy
- Fix stats text indentation in plot panel
- Revert stale .pre-commit-config.yaml pins to match main
- Document _fold_fraction HSV thresholds as H&E-tuned

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- test_plot_calc_qc_image_not_hne: use metrics=None so is_hne=False
  actually exercises different default metrics (was identical to hne test)
- test_qc_image_rgb_metric: add explicit is_hne=True for clarity
- Add test_qc_image_outlier_detection_with_tissue: verify outlier and
  tissue columns are populated correctly
- Add test_qc_image_outlier_detection_without_tissue: verify
  detect_tissue=False path (no tissue columns, no NaN scores)
- Add test_qc_image_compute_only: verify minimal compute-only path

Note: QCImage_calc_qc_image_not_hne.png reference image needs
regeneration since the test now produces a different plot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@selmanozleyen

Copy link
Copy Markdown
Member

@timtreis could you resolve the conflicts please its hard to review like this

…tion-to-qc-he-images

# Conflicts:
#	.pre-commit-config.yaml
#	src/squidpy/experimental/im/_make_tiles.py
Comment thread src/squidpy/experimental/im/_qc_metrics.py Outdated
Comment thread src/squidpy/experimental/im/_sharpness_metrics.py Outdated
timtreis and others added 2 commits April 7, 2026 20:54
Functions exported across private modules (e.g. _sharpness_metrics,
_intensity_metrics, _utils) no longer use a leading underscore since
the module itself is already private. Also removes the unnecessary
lru_cache from fft_high_freq_mask — the mask computation is cheap
relative to the fft2 call it supports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@timtreis
timtreis requested a review from selmanozleyen April 7, 2026 21:26
Comment thread src/squidpy/experimental/im/_intensity_metrics.py Outdated
Comment thread src/squidpy/experimental/im/_intensity_metrics.py Outdated

@selmanozleyen selmanozleyen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all looks good except the points I made. We can put them as a TODO and move on if we want to

timtreis and others added 3 commits April 10, 2026 22:41
Co-authored-by: Selman Özleyen <32667648+selmanozleyen@users.noreply.github.com>
…-qc-he-images

Resolve conflict in _intensity_metrics.py: keep the batched hed_metrics()
function from the PR and remove the old thread-local cache approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@timtreis
timtreis merged commit 373228d into main Apr 10, 2026
14 checks passed
@timtreis
timtreis deleted the bugfix/issue1034-function-to-qc-he-images branch April 10, 2026 21:33
selmanozleyen pushed a commit to selmanozleyen/squidpy that referenced this pull request Apr 13, 2026
timtreis added a commit that referenced this pull request Jun 1, 2026
* init

* doc clarifications

* don't expose the builders

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* deduplicate

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* arrange builders

* better behaviour

* move classes

* cleanup

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add docs

* remove leftover

* resolution in reolve func

* reduce dup code

* deprecate invalid n_neighs

* expose new functions and deprecate the flat one

* move branches to classes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove radius abstraction

* add extensibility page

* unprivate methods and add extensibility page

* give a better example

* apply signature suggestion

* reorganize abstractions

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove unnecessary abstraction

* put the warning in resolve graph builder code

* remove n_neighs from RadiusBuilder

* definition of Adj, Dst and rename to adj, dst

* Generalize the base class for extensibility

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update docs

* clarify delauney vs grid

* dtype rec

* be a bit more verbose on funcitons

* spatial_neighbours_from_builder

* update docs to refer to each other

* refer to the classes properly

* mark as TODO's

* rename sugg

* add combine into API

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] pre-commit autoupdate (#1149)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/biomejs/pre-commit: v2.4.9 → v2.4.10](biomejs/pre-commit@v2.4.9...v2.4.10)
- [github.com/tox-dev/pyproject-fmt: v2.20.0 → v2.21.0](tox-dev/pyproject-fmt@v2.20.0...v2.21.0)
- [github.com/astral-sh/ruff-pre-commit: v0.15.8 → v0.15.9](astral-sh/ruff-pre-commit@v0.15.8...v0.15.9)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Functions to QC histopathology images (#1036)

* remove coord_type abstraction

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make builder positional consistently

* make more abstractions

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* graphmatrixT exposure

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add radius: float | int | tuple t

* 4 or 6

* mark snn as ilustrative

* add uns params

* deprecation warnings alignment

* set diag isnt defaulted to none

* fix

* radius is also positional

* n_neighs on grid mode

* make radius kw only

* put a working example

* fix api.md

* be more compatible with main

* align more to main

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tim Treis <tim.treis@helmholtz-muenchen.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Function to QC H&E images

2 participants