Chapter 5 Cell segmentation
Cell / nuclei segmentation is the task of identifying cells / nuclei in an appropriate image (we will be referring to cell and nuclei segmentation interchangeably here). There are numerous tools available for this purpose, but we will be covering two Python-based models specifically: cellpose
(Stringer et al. 2021) and stardist
(Schmidt et al. 2018; Weigert and Schmidt 2022).
As cell segmentation is performed on DAPI images, it can be performed in parallel with spotcalling.
library(masmr)
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.2
5.1 Pre-processing
5.1.1 Establishing parameters
Similar to spotcalling, we first need to establish some parameters. The same code used for spotcalling can be repeated here.
= 'C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/'
data_dir establishParams(
imageDirs = paste0(data_dir, 'S10/'),
imageFormat = '.ome.tif',
codebookFileName = list.files(paste0(data_dir, 'codebook/'), pattern='codebook', full.names = TRUE),
codebookColumnNames = paste0(rep(c(0:3), 4), '_', rep(c('cy3', 'alexa594', 'cy5', 'cy7'), each=4) ),
outDir = 'C:/Users/kwaje/Downloads/JYOUT/',
dapiDir = paste0(data_dir, 'DAPI_1/'),
pythonLocation = 'C:/Users/kwaje/AppData/Local/miniforge3/envs/scipyenv/',
fpkmFileName = paste0(data_dir, "FPKM_data/Jin_FPKMData_B.tsv"),
nProbesFileName = paste0(data_dir, "ProbesPerGene.csv"),
verbose = FALSE
)
## Assuming .ome.tif is extension for meta files...
Of relevance to cell segmentation specifically are the pythonLocation
and dapiDir
parameters:
dapiDir
: location of DAPI images. [Technically, any cell-based imaging will likely work here – not just DAPI – as long as they are compatible with the cell segmentation models.]pythonLocation
: location of the user’s Python environment. The Python environment should contain all the packages necessary to run the cell segmentation model of choice.
Details on the setting up of the Python environment is beyond the scope of this book. Briefly, there exist various tools (e.g. miniconda
, anaconda
, miniforge
) that allow users to create a Python environment for installing necessary software. We will then interface with that environment through reticulate
. Below are a series of commands to set up a Python environment called scipyenv
that is compatible with stardist
and cellpose
. Users should take note of where the environment is created, and provide the location of the folder in pythonLocation
.
conda create -n scipyenv python=3.9 notebook pandas numpy=1.26.4
conda activate scipyenv
conda install pytorch=1.12.1 cudatoolkit=10.2 -c pytorch #1.12.1 is the earliest mps is supported
pip install cellpose==3.0.11
pip install tensorflow==2.17.0
pip install stardist==0.9.1
Additionally, similar to spotcalling, we also readImageMetaData
, though this time for DAPI images specifically. Because there will be no decoding, warnings about codebook column names not matching can be safely ignored.
readImageMetaData( 'dapi_dir' )
## Warning in readImageMetaData("dapi_dir"): Assuming this data has been acquired
## by Triton
## Warning in readImageMetaData("dapi_dir"): bit_name not matching
## codebookColumnNames: either edit GLOBALCOORD.csv or params$codebook_colnames
## (ignore this warning if codebook not needed, e.g. DAPI)
## Warning in readImageMetaData("dapi_dir"): Updating params$fov_names
## Warning in readImageMetaData("dapi_dir"): Unsuccessful attempt at correcting
## placeholder codebook column names (params$codebook_colnames)
As before, this adds additional metadata to the params
object, albeit this time for DAPI images specifically:
print( params$resolutions )
## $per_pixel_microns
## [1] "0.0706" "0.0706"
##
## $xydimensions_pixels
## [1] "2960" "2960"
##
## $xydimensions_microns
## [1] "208.976" "208.976"
##
## $channel_order
## [1] "dapi"
##
## $fov_names
## [1] "MMStack_1-Pos_000_000" "MMStack_1-Pos_001_000" "MMStack_1-Pos_002_000"
## [4] "MMStack_1-Pos_003_000" "MMStack_1-Pos_004_000" "MMStack_1-Pos_005_000"
## [7] "MMStack_1-Pos_006_000" "MMStack_1-Pos_007_000" "MMStack_1-Pos_008_000"
## [10] "MMStack_1-Pos_009_000" "MMStack_1-Pos_009_001" "MMStack_1-Pos_008_001"
## [13] "MMStack_1-Pos_007_001" "MMStack_1-Pos_006_001" "MMStack_1-Pos_005_001"
## [16] "MMStack_1-Pos_004_001" "MMStack_1-Pos_003_001" "MMStack_1-Pos_002_001"
## [19] "MMStack_1-Pos_001_001" "MMStack_1-Pos_000_001" "MMStack_1-Pos_000_002"
## [22] "MMStack_1-Pos_001_002" "MMStack_1-Pos_002_002" "MMStack_1-Pos_003_002"
## [25] "MMStack_1-Pos_004_002" "MMStack_1-Pos_005_002" "MMStack_1-Pos_006_002"
## [28] "MMStack_1-Pos_007_002" "MMStack_1-Pos_008_002" "MMStack_1-Pos_009_002"
## [31] "MMStack_1-Pos_009_003" "MMStack_1-Pos_008_003" "MMStack_1-Pos_007_003"
## [34] "MMStack_1-Pos_006_003" "MMStack_1-Pos_005_003" "MMStack_1-Pos_004_003"
## [37] "MMStack_1-Pos_003_003" "MMStack_1-Pos_002_003" "MMStack_1-Pos_001_003"
## [40] "MMStack_1-Pos_000_003" "MMStack_1-Pos_000_004" "MMStack_1-Pos_001_004"
## [43] "MMStack_1-Pos_002_004" "MMStack_1-Pos_003_004" "MMStack_1-Pos_004_004"
## [46] "MMStack_1-Pos_005_004" "MMStack_1-Pos_006_004" "MMStack_1-Pos_007_004"
## [49] "MMStack_1-Pos_008_004" "MMStack_1-Pos_009_004" "MMStack_1-Pos_009_005"
## [52] "MMStack_1-Pos_008_005" "MMStack_1-Pos_007_005" "MMStack_1-Pos_006_005"
## [55] "MMStack_1-Pos_005_005" "MMStack_1-Pos_004_005" "MMStack_1-Pos_003_005"
## [58] "MMStack_1-Pos_002_005" "MMStack_1-Pos_001_005" "MMStack_1-Pos_000_005"
## [61] "MMStack_1-Pos_000_006" "MMStack_1-Pos_001_006" "MMStack_1-Pos_002_006"
## [64] "MMStack_1-Pos_003_006" "MMStack_1-Pos_004_006" "MMStack_1-Pos_005_006"
## [67] "MMStack_1-Pos_006_006" "MMStack_1-Pos_007_006" "MMStack_1-Pos_008_006"
## [70] "MMStack_1-Pos_009_006" "MMStack_1-Pos_009_007" "MMStack_1-Pos_008_007"
## [73] "MMStack_1-Pos_007_007" "MMStack_1-Pos_006_007" "MMStack_1-Pos_005_007"
## [76] "MMStack_1-Pos_004_007" "MMStack_1-Pos_003_007" "MMStack_1-Pos_002_007"
## [79] "MMStack_1-Pos_001_007" "MMStack_1-Pos_000_007" "MMStack_1-Pos_000_008"
## [82] "MMStack_1-Pos_001_008" "MMStack_1-Pos_002_008" "MMStack_1-Pos_003_008"
## [85] "MMStack_1-Pos_004_008" "MMStack_1-Pos_005_008" "MMStack_1-Pos_006_008"
## [88] "MMStack_1-Pos_007_008" "MMStack_1-Pos_008_008" "MMStack_1-Pos_009_008"
## [91] "MMStack_1-Pos_009_009" "MMStack_1-Pos_008_009" "MMStack_1-Pos_007_009"
## [94] "MMStack_1-Pos_006_009" "MMStack_1-Pos_005_009" "MMStack_1-Pos_004_009"
## [97] "MMStack_1-Pos_003_009" "MMStack_1-Pos_002_009" "MMStack_1-Pos_001_009"
## [100] "MMStack_1-Pos_000_009"
##
## $zslices
## [1] "1"
::kable( head( params$global_coords ), format = 'html', booktabs = TRUE ) knitr
x_microns | y_microns | z_microns | image_file | fov | bit_name_full | bit_name_alias | bit_name |
---|---|---|---|---|---|---|---|
-2645.753 | 3421.847 | 5678.52 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_000.ome.tif | MMStack_1-Pos_000_000 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
-2645.753 | 3609.926 | 5678.54 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_001.ome.tif | MMStack_1-Pos_000_001 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
-2645.753 | 3798.004 | 5678.56 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_002.ome.tif | MMStack_1-Pos_000_002 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
-2645.753 | 3986.082 | 5678.58 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_003.ome.tif | MMStack_1-Pos_000_003 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
-2645.753 | 4174.161 | 5678.60 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_004.ome.tif | MMStack_1-Pos_000_004 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
-2645.753 | 4362.239 | 5678.38 | C:/Users/kwaje/Downloads/JIN_SIM/20190718_WK_T2_a_B2_R8_12_d50/DAPI_1/DAPI_1_MMStack_1-Pos_000_005.ome.tif | MMStack_1-Pos_000_005 | DAPI_1_dapi | 0_dapi | DAPI_1_dapi |
5.1.2 Establishing the cell segmentation model
Unique to the cell segmentation script, users must load their desired cell segmentation model into the environment. This is accomplished with the establishCellSegModel
function, which returns a new environment object called cellSeg
.
If no other parameters are specified, the nuclei
model in cellpose
is loaded. [We will showcase loading different models later in this chapter.] Additionally, GPU acceleration is turned off as default – users able to access their computer’s GPU may turn this on for time saving.
establishCellSegModel()
##
## Preparing cell segmentation model(s)...
## Cellpose packaged model...
The cellSeg
environment keeps track of which model has been loaded and the outputs expected from that model. Users may load multiple models if they so wish but this will increase the run time: all models in cellSeg
will be run on DAPI images. Below, we have a single model CELLPOSE
, within which we have the model and the expected outputs.
ls(cellSeg)
## [1] "CELLPOSE"
$CELLPOSE cellSeg
## $model
## <cellpose.models.CellposeModel object at 0x000001DA04F987C0>
##
## $outputs
## [1] "masks" "flows" "styles" "diams"
5.2 Running cell segmentation
For memory management, it is recommended that FOVs are processed as individual tiles. Below, we read in a specific FOV (and as recommended, save the current FOV in params$current_fov
). We can use readImage
for a specific file, or – if we know which FOV we want – with readImageList
.
NOTE: below may be more complicated for Z-stacks and multiple channels. Process accordingly so thatim
is a single matrix of the DAPI channel.
$current_fov <- params$fov_names[38]
params<- readImageList( params$current_fov )[[1]] #List of 1
im plot(imager::as.cimg(im))
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
5.3 Running segmentation model
Once the necessary parameters have been established, we are ready to run our cell segmentation model with the runCellSegModel
function. Further image processing of DAPI images generally is not necessary as these models tend to have in-built image processing functions. However, we have found that modifying image parameters can influence cell segmentation; a more detailed discussion is provided below.
5.3.1 cellpose
cellpose
is our default choice owing to it being applicable to both nuclei and cellular segmentation. It is a deep-learning based segmentation model trained on a variety of image types including DAPI images alone (nuclei
), and multi-channel images with both nuclei and cell boundary stains (cyto
, cyto2
, and cyto3
).
An example of how to run the model within masmr
is shown below. [As default, only the mask
output is returned; this can be turned off by setting masksOnly
to FALSE
.]
<- runCellSegModel(im, masksOnly = TRUE)
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
The default nuclei
model called for cellpose
does not seem to be working very well for these images. There are several ways to troubleshoot:
Dilating existing masks
If the issue is that cell masks are accurate, but too small – which would lower the number of counts assigned to each cell – one quick solution would be to dilate the existing masks.
There are several ways to do this. Below is an example of how to do this quickly with imager
– which is a dependency of masmr
. [Possible alternatives: Voronoi tessellation, setting a radius threshold from the cell segmentation mask centre.]
<- c(0L, 50L)
dilation_sizes <- list()
plotList for(i in 1:length(dilation_sizes)){
## Dilate a cellMask by a certain size
<- imager::as.cimg(cellMask)
original_cellMask <- imager::dilate_square(original_cellMask, size = dilation_sizes[i] )
dilated_cellMask
## Plot the masks atop the original image
<- as.data.frame(dilated_cellMask)
df <- ggplot() +
plotList[[i]] geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none') +
ggtitle( paste0('Dilate size: ', dilation_sizes[i] ))
}::plot_grid(plotlist = plotList) cowplot
Note: the choice of the size
parameter for imager::dilate_square
is slightly non-intuitive: (1) input values are rounded down to the nearest integer, and (2) odd numbers provide isotropic dilations (even values dilate to the bottom right).
<- imager::as.cimg( array(0, dim=c(9, 9)) )
pretendImage 5,5] <- 1
pretendImage[<- c(0, 1, 2, 3, 3.9, 4, 5, 6, 7)
dilation_sizes par(mfrow=c(3,3))
for( i in 1:length(dilation_sizes) ){
plot( imager::dilate_square(pretendImage, size=dilation_sizes[i]), interpolate=F,
main = paste0('Size: ', dilation_sizes[i]) )
}
par(mfrow=c(1,1))
Altering model parameters
The default evaluation for cellpose
assumes a diameter of 30 pixels. If we wish to increase this value, this can be accomplished by adding model parameters to runCellSegModel
. Below, we increase the diameter
parameter (specific to the cellpose
model) to 50. [masksOnly
is default TRUE
, will not reproduce that parameter below.]
<- runCellSegModel(im, diameter = 50)
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
Image processing
For some models, altering the intensity values (e.g. by brightening) can influence model performance. For cellpose
, the model is generally robust to additional image processing. [Compare to stardist
below.]
<- im^0.5
norm par(mfrow=c(1,2))
plot(imager::as.cimg(im), main = 'Original image')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
plot(imager::as.cimg(norm), main = 'Brightened image')
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
par(mfrow=c(1,1))
<- runCellSegModel( norm )
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
Loading a retrained model
If users have the resources to retrain the default cellpose
model (Pachitariu and Stringer 2022), this retrained model can be loaded in lieu of the default models. To do so, user’s model can be specified in establishCellSegModel
.
[Model retraining is beyond the scope of this book. Users are encouraged to look up the documentation for cellpose
.]
establishCellSegModel(
cellposePreTrainedModel =
'C:/Users/kwaje/OneDrive - A STAR/Documents/Workplace/Liu_Connect/Code/jinyue_cellpose_models/CP_model3_newB4_800epoch_lr0pt01_FINAL'
)
##
## Preparing cell segmentation model(s)...
## User-pretrained Cellpose model...
<- runCellSegModel(im)
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
5.3.2 stardist
An alternative for nuclei segmentation is to use stardist
: a deep-learning segmentation model that relies on star-convex polygons. The model has been trained on DAPI images (2D_versatile_fluo
), and H&E stains (2D_versatile_he
). It is typically faster than cellpose
, allowing for looping through multiple hyperparameters – should users be interested.
[Tip: Windows users have reported running into [WinError 1314] A required privilege is not held by the client
error when attempting to load stardist
. To get around the problem, manually unzip the downloaded model (file location reported in error message). For some reason, 2D_versatile_fluo.zip
is erroneously unzipped as 2D_versatile_fluo_extracted
instead of 2D_versatile_fluo
in some cases.]
establishCellSegModel( 'stardist' )
##
## Preparing cell segmentation model(s)...
## Stardist packaged model...
## Found model '2D_versatile_fluo' for 'StarDist2D'.
## Loading network weights from 'weights_best.h5'.
## Loading thresholds from 'thresholds.json'.
## Using default values: prob_thresh=0.479071, nms_thresh=0.3.
<- runCellSegModel(im)
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
As with cellpose
, there are various ways to troubleshoot base stardist
performance.
Like cellpose
, users can tweak stardist
model parameters during runCellSegModel
. For instance, decreasing prob_thresh
(default: 0.48) increases sensitivity.
<- runCellSegModel(im, prob_thresh = 0.1)
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
We have also found stardist
to be more sensitive to brightness values / image processing than cellpose
. For instance, brightening the image as before greatly impairs nuclei detection:
<- im^0.5
norm par(mfrow=c(1,2))
plot(imager::as.cimg(im), main = 'Original image')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
plot(imager::as.cimg(norm), main = 'Brightened image')
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
par(mfrow=c(1,1))
<- runCellSegModel( norm )
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
And darkening the image greatly increases sensitivity:
<- im^1.5
norm par(mfrow=c(1,2))
plot(imager::as.cimg(im), main = 'Original image')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
plot(imager::as.cimg(norm), main = 'Darkened image')
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
par(mfrow=c(1,1))
<- runCellSegModel( norm )
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
Given this sensitivity, and the relatively faster speed of stardist
, users might wish to try different image processing prior to cell segmentation. This can be accomplished as below. Note that runCellSegModel
accepts both a single image matrix, and a list of images. [The same is achievable for cellpose
if desired, but users should be aware of the longer runtime.]
<- seq(0.5, 2, length.out = 6)
powerValues par(mfrow=c(2, 3))
<- lapply( powerValues, function(x){
imList <- im^x
norm plot( imager::as.cimg(norm), main = paste0('Image ^ ', round(x, digits=1) ) )
return(norm)
})
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
## Warning in as.cimg.array(norm): Assuming third dimension corresponds to
## time/depth
names( imList ) <- paste0("Power", round(powerValues, digits=1))
<- runCellSegModel( imList )
cellMasks for(i in 1:length(cellMasks)){
plot( imager::as.cimg(cellMasks[[i]]), main = paste0('Masks of ', names(cellMasks)[i]) )
}
par(mfrow=c(1, 1))
To summarise a list of masks into a single mask matrix, we provide the consolidateCellSegMasks
function. This works by overlaying all masks in the list, and only accepting image pixels that have been masked at least \(n\) times. As default, \(n = 1\), so if a pixel is masked at least once, it is brought forwards.
<- consolidateCellSegMasks( cellMasks )
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
Increasing \(n\) means pixels need to be masked more than once, and thus decreases the number of masks identified. Below shows \(n = 2\):
<- consolidateCellSegMasks( cellMasks, minFlags = 2 )
cellMask <- as.data.frame(imager::as.cimg(cellMask))
df ggplot() +
geom_raster( data = as.data.frame(imager::as.cimg(im)),
aes(x=x, y=y, fill=value) ) +
scale_fill_gradient(low='black', high='white') +
geom_point( data=df[df$value>0,],
aes(x=x, y=y, colour=factor(value)),
shape = '.') +
scale_colour_viridis_d( option='turbo' ) +
theme_void( base_size = 14 ) +
scale_y_reverse() +
coord_fixed() +
theme(legend.position = 'none')
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
5.4 Saving cell masks
If users are satisfied with model performance, they can use saveCellSegMasks
to save masks as a .csv.gz
file. Additionally, it is recommended that users save the DAPI image used for cell segmentation. As default, the last image to be processed is stored under params$cell_seg_image
, and will be saved if no other images are specified for saveCellSegMasks
.
par(mfrow=c(1,2))
plot( imager::as.cimg(im), main = 'Original image' )
## Warning in as.cimg.array(im): Assuming third dimension corresponds to
## time/depth
plot( imager::as.cimg(params$cell_seg_image), main = 'Last image processed' )
## Warning in as.cimg.array(params$cell_seg_image): Assuming third dimension
## corresponds to time/depth
par(mfrow=c(1,1))
Saving cell masks creates a CELLSEG_{ Cell segmentation model }_{ FOV name }.csv.gz
file containing masks, and a REGIM_{ FOV Name }.png
image (depending on what is input into the im
parameter).
saveCellSegMasks(cellMask, im = im)
file.exists( paste0(params$out_dir, 'REGIM_', params$current_fov, '.png') )
## [1] TRUE