Skip to content
2 changes: 2 additions & 0 deletions src/squidpy/experimental/im/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
from ._make_tiles import make_tiles, make_tiles_from_spots
from ._qc_image import qc_image
from ._qc_metrics import QCMetric
from ._stitched_labels import make_stitched_labels

__all__ = [
"BackgroundDetectionParams",
"FelzenszwalbParams",
"QCMetric",
"WekaParams",
"make_stitched_labels",
"detect_tissue",
"make_tiles",
"make_tiles_from_spots",
Expand Down
421 changes: 421 additions & 0 deletions src/squidpy/experimental/im/_stitched_labels.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/squidpy/experimental/tl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from ._tiling_qc import calculate_tiling_qc
from ._tiling_stitch import stitch_tile_cuts

__all__ = ["calculate_tiling_qc"]
__all__ = ["calculate_tiling_qc", "stitch_tile_cuts"]
41 changes: 34 additions & 7 deletions src/squidpy/experimental/tl/_tiling_qc.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@

__all__ = ["calculate_tiling_qc"]


def resolve_labels_array(sdata: sd.SpatialData, labels_key: str, scale: str | None) -> xr.DataArray:
"""Resolve a labels element to its 2-D :class:`~xarray.DataArray`.

Single-scale elements pass through; multi-scale (:class:`xarray.DataTree`)
elements require a ``scale`` selector.

Shared by :func:`calculate_tiling_qc` and the downstream stitch helpers
so the multi-scale branch stays consistent.
"""
node = sdata.labels[labels_key]
if isinstance(node, xr.DataTree):
if scale is None:
raise ValueError(f"Labels '{labels_key}' is multi-scale; pass `scale` (e.g. 'scale0').")
return node[scale].ds["image"]
return node


# Minimum cell area in pixels - smaller cells produce noisy contours
_MIN_CELL_AREA = 20

Expand Down Expand Up @@ -531,13 +549,7 @@ def calculate_tiling_qc(
if n_neighbors < 1:
raise ValueError(f"n_neighbors must be >= 1, got {n_neighbors}.")

labels_node = sdata.labels[labels_key]
if isinstance(labels_node, xr.DataTree):
if scale is None:
raise ValueError("When using multi-scale labels, please specify the scale.")
labels_da = labels_node[scale].ds["image"]
else:
labels_da = labels_node
labels_da = resolve_labels_array(sdata, labels_key, scale)

cell_info = _compute_centroids_for_labels(sdata, labels_key, labels_da, scale)
if not cell_info:
Expand Down Expand Up @@ -663,6 +675,21 @@ def _process_one(spec):

if inplace:
table_key = adata_key_added if adata_key_added is not None else f"{labels_key}_qc"
# If a previous QC table at this key carried stitch results, those are
# stale relative to the new outlier set. The new AnnData doesn't
# include them; warn the user so they know to re-run stitch_tile_cuts.
if table_key in sdata.tables:
existing = sdata.tables[table_key]
stitch_cols = [
c
for c in ("stitch_group_id", "is_stitched", "n_pieces", "stitch_confidence")
if c in existing.obs.columns
]
if stitch_cols:
logg.warning(
f"Previous QC table at '{table_key}' carried stitch columns "
f"({stitch_cols}); re-run squidpy.experimental.tl.stitch_tile_cuts."
)
sdata.tables[table_key] = TableModel.parse(adata)
return None
return adata
Loading