Source code for easydecon.segmentation

#!/usr/bin/env python
"""
Easydecon bin2cell segmentation helper script with CLI support
"""
import os
import argparse
import matplotlib.pyplot as plt
import scanpy as sc
import numpy as np
import tensorflow as tf
import bin2cell as b2c
from mpl_toolkits.axes_grid1.inset_locator import inset_axes


[docs] def parse_args(): parser = argparse.ArgumentParser( description="Helper script for bin2cell segmentation with StarDist via CLI parameters" ) parser.add_argument("--sample-id", required=True, help="Unique identifier for the sample (e.g. SampleD1_C2237F2_hirs)") parser.add_argument("--binned-002", required=True, help="Path to binned outputs at 0.02µm resolution") parser.add_argument("--full-image", required=True, help="Path to the raw source image (TIFF)") parser.add_argument("--spaceranger-image-path", required=True, help="Path to Spaceranger cropped spatial images directory") parser.add_argument("--mpp", type=float, default=0.5, help="Microns per pixel for HE scaling (default: 0.5)") parser.add_argument("--model","--stardist-model", default="2D_versatile_he", help="StarDist model name (default: 2D_versatile_he)") parser.add_argument("--min-cells", default=10, type=int, help="Minimum number of cells to filter genes (default: 10)") parser.add_argument("--min-counts", default=5, type=int, help="Minimum number of counts to filter cells (default: 5)") parser.add_argument("--prob-thresh", type=float, default=0.20, help="Probability threshold for StarDist (default: 0.20)") parser.add_argument("--out-dir", default="stardist", help="Directory to save stardist and outputs (default: ./stardist)") parser.add_argument("--device", choices=["cpu", "gpu"], default="gpu", help="Device to use for TensorFlow: 'gpu' or 'cpu' (default: gpu)") parser.add_argument("--nms-thresh", type=float, default=0.3, help="NMS threshold for StarDist (default: 0.3)") return parser.parse_args()
[docs] def run_bin2cell_segmentation(sample_id, binned_002, full_image, spaceranger_image_path, mpp=0.5, model="2D_versatile_he", prob_thresh=0.20, nms_thresh=0.3, min_cells=10, min_counts=5, out_dir="stardist", device="gpu"): # Create output directory os.makedirs(out_dir, exist_ok=True) # Configure TensorFlow # Configure TensorFlow device if device == 'cpu': # Disable all GPUs tf.config.set_visible_devices([], 'GPU') else: # Enable first GPU if available physical_devices = tf.config.list_physical_devices('GPU') if physical_devices: tf.config.set_visible_devices(physical_devices[0], 'GPU') # Read Visium data adata = b2c.read_visium( binned_002, source_image_path=full_image, spaceranger_image_path=spaceranger_image_path ) adata.var_names_make_unique() # Filter sc.pp.filter_genes(adata, min_cells=min_cells) sc.pp.filter_cells(adata, min_counts=min_counts) # Scale HE image he_path = os.path.join(out_dir, f"{sample_id}.he.tiff") b2c.scaled_he_image(adata, mpp=mpp, save_path=he_path) # Destripe b2c.destripe(adata) # StarDist segmentation npz_path = os.path.join(out_dir, f"{sample_id}.he.npz") b2c.stardist( image_path=he_path, labels_npz_path=npz_path, stardist_model=model, prob_thresh=prob_thresh, nms_thresh=nms_thresh, ) # Insert and expand labels b2c.insert_labels( adata, labels_npz_path=npz_path, basis="spatial", spatial_key="spatial_cropped", mpp=mpp, labels_key="labels_he" ) b2c.expand_labels( adata, labels_key='labels_he', expanded_labels_key="labels_he_expanded" ) # Filter background and convert to str bdata = adata[adata.obs['labels_he_expanded'] > 0].copy() bdata.obs['labels_he_expanded'] = bdata.obs['labels_he_expanded'].astype(str) # Bin-to-cell cdata = b2c.bin_to_cell( bdata, labels_key="labels_he_expanded", spatial_keys=["spatial", "spatial_cropped"] ) # Plot bin-count per segment fig, ax = plt.subplots(figsize=(20, 20)) sc.pl.spatial( cdata, color=["bin_count"], img_key=f"{mpp}_mpp", basis="spatial_cropped", ax=ax, show=False, colorbar_loc=None ) ax.set_title('Bin-Counts per Segments', fontsize=16) scatter = ax.collections[0] # Inset colorbar cax = inset_axes( ax, width=0.1, height=1.5, loc='upper right', bbox_to_anchor=(0.95, 0.95), bbox_transform=ax.transAxes, borderpad=0 ) cbar = plt.colorbar(scatter, cax=cax) cbar.ax.set_frame_on(True) cbar.ax.set_facecolor("white") cbar.outline.set_linewidth(1) cbar.outline.set_edgecolor('black') cbar.ax.tick_params(labelcolor='black') ax.axis('off') out_pdf = f"{sample_id}_bincounts.pdf" plt.savefig(out_pdf, dpi=300) # Write output out_h5ad = f"{sample_id}_bin2cell.h5ad" cdata.write_h5ad(out_h5ad) print(f"Outputs saved:\n HE TIFF: {he_path}\n Labels NPZ: {npz_path}\n PDF plot: {out_pdf}\n H5AD: {out_h5ad}") return cdata
[docs] def main(): args = parse_args() run_bin2cell_segmentation( sample_id=args.sample_id, binned_002=args.binned_002, full_image=args.full_image, spaceranger_image_path=args.spaceranger_image_path, mpp=args.mpp, model=args.model, prob_thresh=args.prob_thresh, nms_thresh=args.nms_thresh, out_dir=args.out_dir, device=args.device )
if __name__ == "__main__": main()