Skip to content
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ See the {doc}`extensibility guide </extensibility>` for how to implement a custo
experimental.tl.TilingQCParams
experimental.tl.assign_stitch_groups
experimental.tl.StitchParams
experimental.im.make_stitched_labels
experimental.pl.tiling_qc
experimental.im.fit_stain_reference
experimental.im.normalize_stains
Expand Down
1 change: 1 addition & 0 deletions docs/release/notes-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- Add {func}`squidpy.experimental.im.make_stitched_labels` to materialise a stitched labels element (and an optional collapsed table) from an {func}`squidpy.experimental.tl.assign_stitch_groups` result, completing the tile-cut stitching workflow.
- Fix {func}`squidpy.tl.var_by_distance` behaviour when providing {mod}`numpy` arrays of coordinates as anchor point.
- Update :attr:`squidpy.pl.var_by_distance` to show multiple variables on same plot.
[@LLehner](https://github.com/LLehner)
Expand Down
2 changes: 2 additions & 0 deletions src/squidpy/experimental/im/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
fit_stain_reference,
normalize_stains,
)
from ._stitched_labels import make_stitched_labels

__all__ = [
"BackgroundDetectionParams",
Expand All @@ -37,6 +38,7 @@
"detect_tissue",
"estimate_white_point",
"fit_stain_reference",
"make_stitched_labels",
"make_tiles",
"make_tiles_from_spots",
"qc_image",
Expand Down
546 changes: 546 additions & 0 deletions src/squidpy/experimental/im/_stitched_labels.py

Large diffs are not rendered by default.

15 changes: 3 additions & 12 deletions src/squidpy/experimental/tl/_tiling_qc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import math
from collections.abc import Mapping
from dataclasses import asdict, dataclass, fields
from dataclasses import asdict, dataclass
from typing import Any, Literal

import anndata as ad
Expand All @@ -54,6 +54,7 @@
)
from squidpy.experimental.tl._tiling_stitch import _STITCH_COLUMNS, _STITCH_PARAM_KEYS, StitchParams
from squidpy.experimental.utils._labels import resolve_labels_array
from squidpy.experimental.utils._params import resolve_params

__all__ = ["TilingQCParams", "calculate_tiling_qc"]

Expand Down Expand Up @@ -92,23 +93,13 @@ def __post_init__(self) -> None:


_QC_DEFAULTS = TilingQCParams()
_QC_FIELDS = frozenset(f.name for f in fields(TilingQCParams))


def _resolve_qc_params(qc_params: TilingQCParams | Mapping[str, Any] | None) -> TilingQCParams:
"""Normalise the ``tiling_qc_params`` argument to a :class:`TilingQCParams` instance."""
if qc_params is None:
return _QC_DEFAULTS
if isinstance(qc_params, TilingQCParams):
return qc_params
if isinstance(qc_params, Mapping):
unknown = set(qc_params) - _QC_FIELDS
if unknown:
raise ValueError(
f"Unknown `tiling_qc_params` field(s): {sorted(unknown)}; expected from {sorted(_QC_FIELDS)}."
)
return TilingQCParams(**qc_params)
raise TypeError(f"`tiling_qc_params` must be TilingQCParams, Mapping, or None; got {type(qc_params).__name__}.")
return resolve_params(qc_params, TilingQCParams, label="`tiling_qc_params`")


# Standard consistency factor sd ~ 1.4826 x MAD for normal distributions.
Expand Down
11 changes: 6 additions & 5 deletions src/squidpy/experimental/tl/_tiling_stitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@
from scipy.ndimage import binary_closing
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import connected_components
from skimage.measure import find_contours, regionprops
from skimage.measure import label as cc_label
from skimage.measure import regionprops
from skimage.morphology import disk as morph_disk
from spatialdata._logging import logger as logg

from squidpy.experimental.utils._geometry import equivalent_diameter, largest_contour
from squidpy.experimental.utils._labels import iter_chunked_regionprops, resolve_labels_array
from squidpy.experimental.utils._params import resolve_params

Expand Down Expand Up @@ -302,10 +301,12 @@ def _extract_cut_edges(
if not cell_mask.any():
continue
outlier_crops[lid] = cell_mask
# 1px zero-pad so cells filling their bbox still trace a closed contour.
mask = np.pad(cell_mask.astype(np.float32), 1, mode="constant", constant_values=0)
contour = largest_contour(mask)
if contour is None:
contours = find_contours(mask, 0.5)
if not contours: # degenerate mask traces nothing; skip it
continue
contour = max(contours, key=len)
contour_global = contour.copy()
contour_global[:, 0] += min_r - 1
contour_global[:, 1] += min_c - 1
Expand All @@ -315,7 +316,7 @@ def _extract_cut_edges(
cy = float(ys.mean()) + min_r - 1
cx = float(xs.mean()) + min_c - 1
area = float(mask.sum())
eq_diameter = equivalent_diameter(area)
eq_diameter = float(np.sqrt(4 * area / np.pi)) # diameter of the equal-area circle
min_len = max(min_edge_length, min_edge_length_ratio * eq_diameter)

# find_contours places level set 0.5 outside the integer pixel boundary.
Expand Down
30 changes: 0 additions & 30 deletions src/squidpy/experimental/utils/_geometry.py

This file was deleted.

Loading
Loading