3D Pipeline Tutorial#
This tutorial demonstrates how to use InterSCellar for 3D spatial omics analysis. The 3D pipeline has three steps:
Neighbor Graph: detect neighboring cells and build a cell neighbor graph.
Volume: compute the interscellar volume between each pair of neighboring cells, using either the absolute or the adaptive pathway.
Score: quantify biomarker expression inside each interscellar volume.
Step 1: Neighbor Graph#
First, detect cell neighbors in 3D space:
import interscellar
neighbors_3d, adata, conn = interscellar.find_cell_neighbors_3d(
ome_zarr_path="data/segmentation.zarr",
metadata_csv_path="data/cell_metadata.csv",
max_distance_um=0.5,
voxel_size_um=(0.56, 0.28, 0.28),
db_path="results/sample_neighbor_graph.db",
output_csv="results/sample_neighbors_3d.csv",
n_jobs=8
)
Parameters#
ome_zarr_path: Path to OME-Zarr file containing 3D segmentationmetadata_csv_path: Path to CSV file with cell metadatamax_distance_um: Maximum surface-to-surface distance in micrometers for neighbor detectionvoxel_size_um: Tuple of (z, y, x) voxel sizes in micrometersdb_path: Output neighbor graph databaseoutput_csv: Output CSV of neighbor pairsn_jobs: Number of parallel jobs
Output#
The function returns:
neighbors_3d: DataFrame with neighbor pairsadata: AnnData object (if available) with graph informationconn: Database connection object
Step 2: Volume#
After detecting neighbors, compute the interscellar volumes. There are two pathways, which differ in how far each volume reaches into its two cells:
(a) Absolute: dilates inwards from the cell surface by a fixed, user-defined distance.
(b) Adaptive: dilates inwards from the cell surface by a depth ratio relative to the cell's maximum reach.
(a) Absolute#
volumes_3d, adata, conn = interscellar.compute_interscellar_volumes_3d(
ome_zarr_path="data/segmentation.zarr",
neighbor_pairs_csv="results/sample_neighbors_3d.csv",
neighbor_db_path="results/sample_neighbor_graph.db",
voxel_size_um=(0.56, 0.28, 0.28),
max_distance_um=3.0,
intracellular_threshold_um=1.0,
n_jobs=8
)
Parameters
ome_zarr_path: Path to OME-Zarr file containing 3D segmentationneighbor_pairs_csv: Path to CSV file with neighbor pairs from step 1neighbor_db_path: Path to database file with neighbor pairs from step 1voxel_size_um: Tuple of (z, y, x) voxel sizes in micrometersmax_distance_um: Maximum distance for volume computationintracellular_threshold_um: Fixed depth the volume reaches into each celln_jobs: Number of parallel jobs
Output
The function returns:
volumes_3d: DataFrame with interscellar volume measurementsadata: AnnData object with volume informationconn: Database connection object
Outputs are written next to the neighbor pairs CSV as sample_absolute_volumes.csv, sample_absolute_interscellar_volumes.zarr and sample_absolute_cell_only_volumes.zarr.
(b) Adaptive#
volumes_3d = interscellar.compute_interscellar_volumes_3d_adaptive(
ome_zarr_path="data/segmentation.zarr",
neighbor_pairs_csv="results/sample_neighbors_3d.csv",
voxel_size_um=(0.56, 0.28, 0.28),
max_distance_um=3.0,
rho_threshold=0.5,
n_jobs=8
)
Parameters
ome_zarr_path: Path to OME-Zarr file containing 3D segmentationneighbor_pairs_csv: Path to CSV file with neighbor pairs from step 1voxel_size_um: Tuple of (z, y, x) voxel sizes in micrometersmax_distance_um: Maximum distance for volume computationrho_threshold: Depth ratio, relative to each cell's maximum reach, that the volume extends into the celln_jobs: Number of parallel jobs
Output
The function returns:
volumes_3d: DataFrame with interscellar volume measurements
Outputs are written next to the neighbor pairs CSV as sample_adaptive_volumes.csv and sample_adaptive_interscellar_volumes.zarr. Pairs with no extracellular corridor connecting the two cells are rejected and listed in sample_adaptive_rejected_pairs.csv.
Cell-only volumes#
After computing interscellar volumes with either pathway, you can compute cell-only volumes by subtracting the interscellar volumes from the original segmentation. Every voxel that lies in any interscellar volume is removed from its cell, so a voxel shared by overlapping adaptive volumes is removed once:
cellonly_3d = interscellar.compute_cell_only_volumes_3d(
ome_zarr_path="data/segmentation.zarr",
interscellar_volumes_zarr="results/sample_adaptive_interscellar_volumes.zarr",
n_jobs=8
)
Parameters
ome_zarr_path: Path to original OME-Zarr file containing 3D segmentationinterscellar_volumes_zarr: Interscellar volumes zarr from either pathway of step 2nuclei_segmentation_zarr(optional): Nuclei segmentation zarr on the same grid; every voxel inside a nucleus is also removedn_jobs: Number of parallel jobs
The output zarr is saved next to the interscellar volumes zarr, with _interscellar_volumes replaced by _cell_only_volumes (e.g. sample_adaptive_cell_only_volumes.zarr).
Output
The function returns:
cellonly_3d: DataFrame with one row per cell:cell_id,cell_only_volume_voxels,cell_only_volume_um3andcell_type
The same computation is available from the command line:
compute-cell-only-volumes-3d \
--cell-segmentation-zarr "data/segmentation.zarr" \
--interscellar-zarr "results/sample_adaptive_interscellar_volumes.zarr" \
--n-jobs 8
Step 3: Score#
Finally, quantify biomarker expression inside each interscellar volume.
Points (transcriptomics, punctate proteomics)#
The centrality score counts each spot inside a volume, weighted by its distance from the pair's contact interface:
scores_3d = interscellar.calculate_interscellar_scores_3d(
interscellar_volumes_zarr="results/sample_adaptive_interscellar_volumes.zarr",
spot_zarrs={"GZMB": "data/GZMB_spots.zarr"},
n_jobs=8
)
Parameters
interscellar_volumes_zarr: Interscellar volumes zarr from the adaptive pathway of step 2spot_zarrs: Mapping of biomarker name to spot mask zarr on the same voxel gridn_jobs: Number of parallel jobs
Output
The function returns:
scores_3d: DataFrame with one row per pair and biomarker, also written assample_adaptive_scores.csv
Immunofluorescence (metabolomics, intensity proteomics)#
Intensity statistics per volume are computed from a raw expression image:
python -m interscellar.core.calculate_interscellar_scores_3d_intensity \
--segmentation-zarr "results/sample_adaptive_interscellar_volumes.zarr" \
--raw-expression-zarr "data/raw_expression.zarr" \
--n-jobs 8
Parameters
--segmentation-zarr: Interscellar volumes zarr from either pathway of step 2, or a cell-only volumes zarr--raw-expression-zarr: Raw expression OME-Zarr on the same voxel grid--channels/--channel-names(optional): Channels to measure and names for them--object-id-column(optional): ID column name; usecell_idfor cell-only volumes--n-jobs: Number of parallel jobs
Output
A CSV with one row per volume and, for each channel, the sum, mean, std, min, max and nonzero_fraction of the raw intensities inside it. Statistics include every voxel of the volume, zero-intensity voxels too. Adaptive volumes are read from their lossless per-pair archive, so overlapping pairs each keep their shared voxels. By default the CSV is written next to the input as sample_adaptive_interscellar_volumes_features_3d.csv.