Performing Resegmentation and Cell Assignment Using Python
If you want to improve the segmentation of your data, then re-run segmentation with the AVITI24™ output data. One common framework for resegmentation is Cellpose, which allows you to apply various pre-trained segmentation models, or customize a model for your data. Additionally, you can try a different pre-trained Element Biosciences™ segmentation model than was used on instrument.
This tutorial provides steps on how to use Python-based tools to apply Cellpose resegmentation models to AVITI24 cytoprofiling run data. To perform additional analyses for morphology or image quantification metrics, see the Running CellProfiler with AVITI24 Data tutorial.
Before You Begin
The following are required prerequisites for this tutorial:
- Python v3 or later
- Anaconda Distribution
- Jupyter Notebook
- The following required Python packages:
To install the required Python packages and enable all prerequisites, run the following commands in a command-line interface (CLI) with Anaconda:
conda create --name resegmentation python=3.10
conda activate resegmentation
conda install -c conda-forge cellpose=3.1.0 scikit-image=0.24.0
conda install -c anaconda ipykernel
pip install notebook
python -m ipykernel install --user --name=resegmentation
Re-run Segmentation with a Custom Model
In the CLI terminal with Anaconda, run the following command to open Jupyter Notebook:
jupyter notebook
To download Element custom Cellpose models, install the Element cytoprofiling package.
Select New, and then select Notebook.
In the drop-down menu, select the resegmentation kernel.
To import required packages, copy the following code into the first cell:
from cellpose import models, transforms
import skimage
import os
import numpy as np
import json
import warnings
warnings.filterwarnings("ignore", message=".*weights_only=False.*")
warnings.filterwarnings("ignore", message=".*low contrast image.*")To run segmentation, copy the following code into a second cell and update the following variables:
- Update
run_path
to the run output files folder. - Update
model_dir
to the path of your installation of the cytoprofiling repository. - Update
output_location
to your preferred output location for the resegmentation files. - Update
cell_types
andcell_diameters
based on your samples in each well. - To use your own segmenation models, change
model_dir
andelement_models
to point to your model files.
# define the cell type per well
cell_types = {
"A1":"HELA",
"A2":"HELA",
"B1":"HUVEC",
"B2":"OTHER",
"C1":"MCF7",
"C2":"PC3",
"D1":"HEK293",
"D2":"OTHER",
"E1":"JURKAT",
"E2":"HUVEC",
"F1":"MCF7",
"F2":"HEPG2"
}
# set the cell diameter to be used for segmentation of "OTHER" cell types
cell_diameters = {
"B2":100,
"D2":75
}
# Element Biosciences pre-trained nuclear segmentation model
nuclear_model = "20240629_cellpose_nuc_8diam"
# Element Biosciences pre-trained segmentation models
element_models = {
"OTHER":"20240905_general_15diam_3ch",
"HELA":"20240905_hela_15diam_3ch",
"HUVEC":"20240905_huvec_15diam_3ch",
"MCF7":"20240924_mcf7_15diam_3ch",
"PC3":"20240924_pc3_15diam_3ch",
"HEK293":"20240924_hek293_15diam_3ch",
"JURKAT":"20240924_jurkat_15diam_3ch",
"HEPG2":"20240924_hepg2_15diam_3ch",
"HCT116":"20240924_hct116_15diam_3ch"
}
model_dir = "/cytoprofiling/Package/Path/segmentationModels"
run_path = "/Run/Output/Folder/Location"
output_location = "/Output/Location/CellSegmentation_modelv2"
def normalize_image(image, region_size=1824):
"""
Normalize an image within a defined region size.
"""
image_norm = np.zeros_like(image, np.single)
for xi in range(int(image.shape[1] / region_size)):
for yi in range(int(image.shape[0] / region_size)):
cropped = image[
yi * region_size: (yi + 1) * region_size, xi * region_size: (xi + 1) * region_size
]
cropped = transforms.normalize_img(cropped.reshape(
cropped.shape[0], cropped.shape[1], 1)).reshape(cropped.shape[0], cropped.shape[1])
image_norm[
yi * region_size: (yi + 1) * region_size, xi * region_size: (xi + 1) * region_size
] = cropped
return image_norm
def segment_cells(cell_image, nuclear_image, actin_image, cell_model_path, nuclear_model_path, cell_diameter, normalization=False, use_gpu=False):
cell_image = normalize_image(cell_image)
nuclear_image = normalize_image(nuclear_image)
actin_image = normalize_image(actin_image)
segment_channel = 1
nuclear_channel = 2
channels = [segment_channel, nuclear_channel]
composite = np.zeros((cell_image.shape[0], cell_image.shape[1], 3))
composite[:, :, 0] = cell_image
composite[:, :, 1] = nuclear_image
composite[:, :, 2] = actin_image
print(f"using {cell_model_path} for cell segmentation")
# segment cells
if cell_model_path in ["cyto3","cyto2"]:
"""
Use the default Cellpose3 model.
Cell diameter from the user is used here.
"""
model = models.Cellpose(gpu=use_gpu, model_type=cell_model_path)
cell_mask, _, _, _ = model.eval(
composite,
diameter=cell_diameter,
channels=channels,
normalize=normalization,
resample=False
)
elif cell_model_path == "20240903_general_15diam_3ch":
"""
Use the general Element Biosciences model.
Cell diameter from the user is used here.
"""
model = models.CellposeModel(gpu=use_gpu,pretrained_model=False,nchan=3,
model_type=cell_model_path)
cell_mask, _, _ = model.eval(
composite,
diameter=cell_diameter,
channels=None,
normalize=normalization,
resample=False
)
else:
"""
Use a custom Cellpose model trained trained on 3 channels.
Cell diameter from the user is not used and the mean diameter from the training set is used instead.
"""
model = models.CellposeModel(gpu=use_gpu,pretrained_model=False,nchan=3,
model_type=cell_model_path)
cell_mask, _, _ = model.eval(
composite,
channels=None,
normalize=normalization,
resample=False
)
cell_mask = cell_mask.astype(np.uint32)
print(f"using {nuclear_model_path} for nuclear segmentation")
# segment nuclei
model = models.CellposeModel(pretrained_model=False, model_type=nuclear_model_path)
nuclear_mask, _, _ = model.eval(
nuclear_image,
resample=False
)
binary_nuclei = nuclear_mask.copy()
binary_nuclei[nuclear_mask > 0] = 1
return cell_mask, binary_nuclei
# load run paramters file
with open(os.path.join(run_path,"RunParameters.json")) as f:
run_parameters = json.load(f)
tile2well = {}
tiles = []
for well in run_parameters["Wells"]:
for tile in well["Tiles"]:
tile2well[tile["Name"]] = well["WellLocation"]
tiles.append(tile["Name"])
# do segmentation
for tile in tiles:
well = tile2well[tile]
cell_type = cell_types[well]
cell_diameter = cell_diameters.get(well, 0)
cell_model_path = os.path.join(model_dir,element_models.get(cell_type.upper(),element_models["OTHER"]))
nuclear_model_path = os.path.join(model_dir,nuclear_model)
os.makedirs(os.path.join(run_path,output_location,f"Well{well}"), exist_ok=True)
cell_image = skimage.io.imread(os.path.join(run_path,"Projection",f"Well{well}",f"CP01_{tile}_Cell-Membrane.tif"))
nuclear_image = skimage.io.imread(os.path.join(run_path,"Projection",f"Well{well}",f"CP01_{tile}_Nucleus.tif"))
actin_image = skimage.io.imread(os.path.join(run_path,"Projection",f"Well{well}",f"CP01_{tile}_Actin.tif"))
cell_mask, binary_nuclei = segment_cells(cell_image, nuclear_image, actin_image, cell_model_path, nuclear_model_path, cell_diameter)
skimage.io.imsave(os.path.join(run_path,output_location,f"Well{well}",f"{tile}_Cell.tif"), cell_mask)
skimage.io.imsave(os.path.join(run_path,output_location,f"Well{well}",f"{tile}_Nuclear.tif"), binary_nuclei)
print(f"finished segmenting {tile}")- Update
Select the Run icon to test the workflow.
The resegmentation output files appear in the output path location.
Cell Assignment
After you complete resegmentation, use Cells2Stats with the --segmentation argument to re-assign cell barcodes and re-generate the cell table.