squidpy.experimental.tl.calculate_tiling_qc

squidpy.experimental.tl.calculate_tiling_qc(sdata, labels_key, scale=None, tile_size=2048, overlap_margin='auto', downsample=1, outlier_use_cut=True, outlier_use_smoothed=True, nmads_cut=1.5, nmads_smoothed=3, n_neighbors=10, tiling_qc_params=None, n_jobs=-1, table_key_added=None, inplace=True)[source]

Score cells for tile-boundary segmentation artifacts.

Computes per-cell metrics that detect artificially straight edges caused by tiled segmentation. Large images are processed via the cell-aware tiling infrastructure in squidpy.experimental.im._tiling.

Results are stored in a QC table (default sdata.tables["{labels_key}_qc"]). Scores live in .obs; the .X matrix is empty. Algorithm parameters are recorded in .uns["tiling_qc"].

Parameters:
  • sdata (SpatialData) – SpatialData object.

  • labels_key (str) – Key in sdata.labels with segmentation masks.

  • scale (str | None) – Scale level for multi-scale labels.

  • tile_size (int) – Side length of the tiling grid (pixels).

  • overlap_margin (Union[int, Literal['auto']]) – Overlap around each tile. "auto" computes the minimum from the largest cell’s bounding box.

  • downsample (int) – Factor by which to downsample each cell’s bounding-box crop before contour extraction. Straightness is scale-invariant, so 24 is safe and much faster on large cells.

  • outlier_use_cut (bool) – Gate is_outlier on the per-cell cut_score exceeding its own MAD threshold. Requires the cell itself to have a straight cardinal-aligned edge.

  • outlier_use_smoothed (bool) – Gate is_outlier on the spatially smoothed score (smoothed_cut_score) exceeding its MAD threshold. Requires the cell to be in a spatial cluster of high-scorers.

  • nmads_cut (float) – Number of MADs for the cut_score outlier gate. Threshold is median + nmads_cut x MAD x 1.4826.

  • nmads_smoothed (float) – Number of MADs for the smoothed_cut_score outlier gate. Threshold is median + nmads_smoothed x MAD x 1.4826.

  • n_neighbors (int) – Number of nearest spatial neighbors used to compute smoothed_cut_score and nhood_outlier_fraction. In a perfect grid each cell has 8 immediate neighbours; the default of 10 leaves a little wiggle room for biological irregularity without wasting compute on distant cells.

  • tiling_qc_params (TilingQCParams | Mapping[str, Any] | None) – Advanced tuning knobs as a TilingQCParams instance or a Mapping of its field names to values. See TilingQCParams for each field’s meaning and default. None (default) uses all defaults.

  • n_jobs (int) – Number of threads for tile processing. -1 (default) uses all available CPUs. Ignored when an active dask.distributed.Client is in scope (the client’s own worker pool is used instead).

  • table_key_added (str | None) – Key under which to store the result in sdata.tables. Defaults to "{labels_key}_qc".

  • inplace (bool) – If True, store result in sdata.tables. Otherwise return the AnnData directly.

Return type:

AnnData | None

Returns:

AnnData when inplace=False, otherwise None. The AnnData .obs contains five scores per cell:

  • max_straight_edge_ratio: longest collinear boundary segment / equivalent diameter.

  • cardinal_alignment_score: axis-alignment of that segment (1 = cardinal, 0 = diagonal).

  • cut_score: product of the two.

  • smoothed_cut_score: cut_score x mean(neighbor cut_scores) over the n_neighbors nearest spatial neighbors. Amplifies cells on FOV boundaries while suppressing isolated high-scorers.

  • is_outlier: boolean, True when the enabled outlier gates are satisfied (cut_score and/or smoothed_cut_score exceeding their respective MAD thresholds).

  • nhood_outlier_fraction: fraction of n_neighbors nearest neighbors that are smoothed-score outliers (MAD-based). Bounded [0, 1]; high values trace the tile grid.

Notes

Tile processing is parallelised via dask.compute(). When an active dask.distributed.Client is in scope it is picked up automatically and used for execution; otherwise a local threaded scheduler with n_jobs workers is used.

If you invoke this function from inside a dask worker task (e.g., via client.submit(calculate_tiling_qc, ...)), wrap the call in distributed.secede / distributed.rejoin to release the worker slot before the inner tile tasks are submitted; without that, the cluster can deadlock when all workers are busy holding the outer job.