From 3dbe7687be03bd33c4b70a4094a94ffdc2b76c56 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:22:50 -0500 Subject: [PATCH 01/21] change: segmentation param changes --- NEWS.md | 6 +- R/cell_segmentation.R | 189 +++++++++++++++++++++++++----------------- 2 files changed, 116 insertions(+), 79 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1fa9f3c82..11c795336 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# Giotto 4.1.7 + +## Changes +* GiottoUtils req raised to 0.2.3 # Giotto 4.1.6 (2024/12/09) @@ -15,8 +19,8 @@ * re-export of `dotPlot()` from GiottoVisuals ## Changes -* GiottoClass req raised to 0.4.5 * GiottoUtils req raised to 0.2.2 +* GiottoClass req raised to 0.4.5 * GiottoVisuals req raised to 0.2.10 diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index 81e1a85f6..6922aa5a5 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -92,15 +92,14 @@ doCellSegmentation <- function( #' #' @title perform cellpose segmentation #' @description -#' -#' perform the Giotto Wrapper of cellpose segmentation. This is for a model +#' Perform the Giotto Wrapper of cellpose segmentation. This is for a model #' inference to generate segmentation mask file from input image. #' main parameters needed #' @name doCellposeSegmentation -#' @param image_dir character, required. Provide a path to a gray scale or a +#' @param input character, required. Provide a path to a gray scale or a #' three channel image. #' @param python_path python environment with cellpose installed. -#' default = "giotto_cellpose". +#' default = "giotto_segmentation". #' @param mask_output required. Provide a path to the output mask file. #' @param channel_1 channel number for cytoplasm, default to 0(gray scale) #' @param channel_2 channel number for Nuclei, default to 0(gray scale) @@ -110,41 +109,70 @@ doCellSegmentation <- function( #' @param batch_size Cellpose Parameter, Number of 224x224 patches to run #' simultaneously on the GPU. Can make smaller or bigger depending on GPU #' memory usage. Defaults to 8. -#' @param resample Cellpose Parameter -#' @param channel_axis Cellpose Parameter -#' @param z_axis Cellpose Parameter -#' @param normalize Cellpose Parameter -#' @param invert Cellpose Parameter -#' @param rescale Cellpose Parameter -#' @param diameter Cellpose Parameter -#' @param flow_threshold Cellpose Parameter -#' @param cellprob_threshold Cellpose Parameter -#' @param do_3D Cellpose Parameter -#' @param anisotropy Cellpose Parameter -#' @param stitch_threshold Cellpose Parameter -#' @param min_size Cellpose Parameter -#' @param niter Cellpose Parameter -#' @param augment Cellpose Parameter -#' @param tile Cellpose Parameter -#' @param tile_overlap Cellpose Parameter -#' @param bsize Cellpose Parameter -#' @param interp Cellpose Parameter -#' @param compute_masks Cellpose Parameter -#' @param progress Cellpose Parameter +#' @param resample (logical, optional) – run dynamics at original image size +#' (will be slower but create more accurate boundaries). Defaults to True. +#' @param channel_axis (int, optional) – channel axis in element of list x, or +#' of np.ndarray x. if NULL, channels dimension is attempted to be +#' automatically determined. Defaults to NULL. +#' @param z_axis (int, optional) – z axis in element of list x, or of +#' np.ndarray x. if NULL, z dimension is attempted to be automatically +#' determined. Defaults to NULL. +#' @param normalize Logical or list. Controls image normalization: +#' If TRUE, normalizes to 1st-99th percentile +#' Can be a list with parameters: +#' * lowhigh: Numeric vector c(low, high) for manual normalization values +#' * sharpen: Numeric, image sharpening factor (1/4-1/8 of cell diameter in pixels) +#' * normalize: Logical, whether to run normalization +#' * percentile: Numeric vector c(low_perc, high_perc) +#' * tile_norm: Integer, window size in pixels for tile normalization +#' * norm3D: Logical, normalize across full z-stack +#' Default: TRUE +#' @param invert Logical. Inverts pixel intensity before processing. +#' Default: FALSE +#' @param rescale Numeric. Resize factor for images. +#' Default: NULL (sets to 1.0) +#' @param diameter Numeric. Cell diameter for each image. +#' Default: NULL (uses diam_mean or diam_train if available) +#' @param flow_threshold Numeric. Flow error threshold for cell retention (2D only). +#' Default: 0.4 +#' @param cellprob_threshold Numeric. Threshold for mask pixel retention. +#' Default: 0.0 +#' @param do_3D Logical. Enable 3D segmentation for 3D/4D inputs. +#' Default: FALSE +#' @param anisotropy Numeric. Z-axis rescaling factor for 3D segmentation. +#' Default: NULL +#' @param stitch_threshold Numeric. Threshold for 3D mask stitching (2D mode only). +#' Default: 0.0 +#' @param min_size Integer. Minimum ROI size in pixels. +#' Default: 15 +#' @param max_size_fraction Numeric. Maximum mask size as fraction of image. +#' Default: 0.4 +#' @param niter Integer. Number of dynamics computation iterations. +#' Default: NULL (set based on diameter) +#' @param augment Logical. Enable tile augmentation with overlapping. +#' Default: FALSE +#' @param tile_overlap Numeric. Tile overlap fraction for flow computation. +#' Default: 0.1 +#' @param bsize Integer. Block size for tiles (recommended: 224). +#' Default: 224 +#' @param dP_smooth Integer. Gaussian smoothing standard deviation for 3D flows. +#' @param interp Logical. Enable interpolation for 2D dynamics. +#' Default: TRUE +#' @param compute_masks Logical. Compute dynamics and return masks. +#' Default: TRUE +#' @param progress progress bar. Defaults to NULL #' @returns No return variable, as this will write directly to output path #' provided. #' @examples -#' # example code #' doCellposeSegmentation( -#' image_dir = input_image, +#' input = input_image, #' mask_output = output, channel_1 = 2, #' channel_2 = 1, model_name = "cyto3", batch_size = 4 #' ) #' @export -doCellposeSegmentation <- function( - python_env = "giotto_cellpose", - image_dir, +doCellposeSegmentation <- function(input, mask_output, + python_env = "giotto_segmentation", channel_1 = 0, channel_2 = 0, model_name = "cyto3", @@ -162,22 +190,26 @@ doCellposeSegmentation <- function( anisotropy = NULL, stitch_threshold = 0.0, min_size = 15, + max_size_fraction = 0.4, niter = NULL, augment = FALSE, - tile = TRUE, tile_overlap = 0.1, bsize = 224, + dP_smooth, interp = TRUE, compute_masks = TRUE, progress = NULL, - verbose = TRUE, ...) { + verbose = NULL, + ...) { + ## Load required python libraries + GiottoClass::set_giotto_python_path(python_env) + GiottoUtils::package_check("cellpose>=3.1.0", repository = "pip") + # Check Input arguments model_name <- match.arg( model_name, unique(c("cyto3", "cyto2", "cyto", "nuclei", model_name)) ) - ## Load required python libraries - GiottoClass::set_giotto_python_path(python_env) - GiottoUtils::package_check("cellpose", repository = "pip") + if (!is.null(channel_axis)) channel_axis <- as.integer(channel_axis) cellpose <- reticulate::import("cellpose") np <- reticulate::import("numpy") @@ -186,15 +218,15 @@ doCellposeSegmentation <- function( message("successfully loaded giotto environment with cellpose.") if (!(torch$cuda$is_available())) { - warning("GPU is not available for this session, inference may be slow.") + warning( + "GPU is not available for this session, inference may be slow.") } GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, "Loading Image from ", - image_dir + .v = verbose, .is_debug = FALSE, "Loading Image from ", input ) - img <- cellpose$io$imread(image_dir) + img <- cellpose$io$imread(input) GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Loading Model...") model_to_seg <- cellpose$models$Cellpose( @@ -209,7 +241,7 @@ doCellposeSegmentation <- function( result <- segmentation(img, diameter = diameter, channels = channel_to_seg, - batch_size = batch_size, + batch_size = as.integer(batch_size), resample = resample, channel_axis = channel_axis, z_axis = z_axis, @@ -221,12 +253,13 @@ doCellposeSegmentation <- function( do_3D = do_3D, anisotropy = anisotropy, stitch_threshold = stitch_threshold, - min_size = min_size, + min_size = as.integer(min_size), + max_size_fraction = max_size_fraction, niter = niter, augment = augment, - tile = tile, tile_overlap = tile_overlap, - bsize = bsize, + bsize = as.integer(bsize), + dP_smooth = as.integer(dP_smooth), interp = interp, compute_masks = compute_masks, progress = progress @@ -247,37 +280,38 @@ doCellposeSegmentation <- function( #' #' @title perform Mesmer(Deepcell) segmentation #' @description -#' -#' perform the Giotto Wrapper of mesmer segmentation. This is for a model +#' Perform the Giotto Wrapper of mesmer segmentation. This is for a model #' inference to generate segmentation mask file from input image. #' main parameters needed #' @name doMesmerSegmentation -#' @param Image_dir character, required. Provide a path to a IF image. +#' @param input character, required. Provide a path to a IF image. #' @param python_env python environment with deepcell installed. #' default = "giotto_segmentation". See deepcell official website for more details. #' @param mask_output required. Provide a path to the output mask file. -#' @param Nucleus_channel channel number for Nuclei, default to 1 -#' @param Memberane_channel channel number for cell boundary, default to 2 +#' @param nucleus_channel channel number for Nuclei, default to 1 +#' @param membrane_channel channel number for cell boundary, default to 2 #' @param pixel_per_micron physical micron size per pixel, default to 0.25 #' @returns No return variable, as this will write directly to output path #' provided. #' @examples #' # example code #' doMesmerSegmentation( -#' Image_dir = input_image, +#' input = input_image, #' mask_output = output, -#' Nucleus_channel = 1, -#' Memberane_channel = 2, +#' nucleus_channel = 1, +#' membrane_channel = 2, #' pixel_per_micron = 0.5 #' ) #' @export -doMesmerSegmentation <- function(Image_dir, - python_env = 'giotto_segmentation', - Nucleus_channel = 1, - Memberane_channel = 2, - pixel_per_micron = 0.25, - mask_output, - verbose = F, ...){ +doMesmerSegmentation <- function(input, + mask_output, + python_env = 'giotto_segmentation', + nucleus_channel = 1, + membrane_channel = 2, + pixel_per_micron = 0.25, + verbose = NULL, + ... +){ ## Load required python libraries GiottoClass::set_giotto_python_path(python_env) GiottoUtils::package_check("deepcell", repository = "pip") @@ -293,11 +327,11 @@ doMesmerSegmentation <- function(Image_dir, .v = verbose, .is_debug = FALSE, "Loading Image... ", ) GiottoUtils::package_check("terra") - rast = terra::rast(Image_dir) + rast = terra::rast(input) # Convert the R matrix to a NumPy array explicitly - Nucleus_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(Nucleus_channel)]]))) - Membrane_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(Memberane_channel)]]))) - stacked_array <- np$stack(list(Nucleus_channel_np, Membrane_channel_np), axis = as.integer(-1)) + nucleus_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(nucleus_channel)]]))) + membrane_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(membrane_channel)]]))) + stacked_array <- np$stack(list(nucleus_channel_np, membrane_channel_np), axis = as.integer(-1)) # Add a new axis to the stacked array to fit Mesmer input stacked_array <- np$expand_dims(stacked_array, axis = as.integer(0)) @@ -321,12 +355,11 @@ doMesmerSegmentation <- function(Image_dir, #' #' @title perform Stardist segmentation #' @description -#' -#' perform the Giotto Wrapper of Stardist 2D segmentation. This is for a model +#' Perform the Giotto wrapper of Stardist 2D segmentation. This is for a model #' inference to generate segmentation mask file from input image. #' main parameters needed #' @name doStardistSegmentation -#' @param Image_dir character, required. Provide a path to an image. +#' @param input character, required. Provide a path to an image. #' @param python_env python environment with Stardist installed. #' default = "giotto_segmentation". See Stardist official website for more details. #' @param mask_output required. Provide a path to the output mask file. @@ -339,22 +372,22 @@ doMesmerSegmentation <- function(Image_dir, #' @examples #' # example code #' doStardistSegmentation( -#' Image_dir = input_image, +#' input = input_image, #' mask_output = output, #' model_name = '2D_versatile_fluo', #' nuclei_channel = 3 #' ) #' #' @export -doStardistSegmentation <- function(Image_dir, - python_env = 'giotto_segmentation', - mask_output, - model_name = '2D_versatile_fluo', - nuclei_channel = NULL, - prob_thresh = NULL, - nms_thresh = NULL, - verbose = F, - ...){ +doStardistSegmentation <- function(input, + mask_output, + python_env = 'giotto_segmentation', + model_name = '2D_versatile_fluo', + nuclei_channel = NULL, + prob_thresh = NULL, + nms_thresh = NULL, + verbose = NULL, + ...){ # Import the necessary Python modules ## Load required python libraries GiottoClass::set_giotto_python_path(python_env) @@ -377,11 +410,11 @@ doStardistSegmentation <- function(Image_dir, # Load the image GiottoUtils::vmsg( .v = verbose, .is_debug = FALSE, "Loading Image from ", - Image_dir + input ) GiottoUtils::package_check("terra") - rast = terra::rast(Image_dir) - if (model_name != '2D_versatile_he' & is.null(nuclei_channel)){ + rast = terra::rast(input) + if (model_name != '2D_versatile_he' && is.null(nuclei_channel)){ stop('using IF based nuclei segmentation, please specify nuclei channel') } else if( model_name == '2D_versatile_he'){ From dd25227c9b273d1ad246cc6147d8937aa016c546 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:11:14 -0500 Subject: [PATCH 02/21] chore: docs --- R/cell_segmentation.R | 12 +++-- man/doCellposeSegmentation.Rd | 92 +++++++++++++++++++++++------------ man/doMesmerSegmentation.Rd | 36 +++++++------- man/doStardistSegmentation.Rd | 16 +++--- 4 files changed, 95 insertions(+), 61 deletions(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index 6922aa5a5..8f900213e 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -290,17 +290,17 @@ doCellposeSegmentation <- function(input, #' @param mask_output required. Provide a path to the output mask file. #' @param nucleus_channel channel number for Nuclei, default to 1 #' @param membrane_channel channel number for cell boundary, default to 2 -#' @param pixel_per_micron physical micron size per pixel, default to 0.25 +#' @param micron_scale numeric. Multiplicative scalefactor to convert pixel +#' dimensions to physical microns. #' @returns No return variable, as this will write directly to output path #' provided. #' @examples -#' # example code #' doMesmerSegmentation( #' input = input_image, #' mask_output = output, #' nucleus_channel = 1, #' membrane_channel = 2, -#' pixel_per_micron = 0.5 +#' micron_scale = 0.5 #' ) #' @export doMesmerSegmentation <- function(input, @@ -308,7 +308,7 @@ doMesmerSegmentation <- function(input, python_env = 'giotto_segmentation', nucleus_channel = 1, membrane_channel = 2, - pixel_per_micron = 0.25, + micron_scale = 0.25, verbose = NULL, ... ){ @@ -337,7 +337,9 @@ doMesmerSegmentation <- function(input, GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Segmenting Image...") - segmentation_predictions = mesmer$predict(stacked_array, image_mpp=pixel_per_micron) + segmentation_predictions = mesmer$predict( + stacked_array, image_mpp = micron_scale + ) mask <- segmentation_predictions[1,,,1] mask_r <- reticulate::py_to_r(mask) diff --git a/man/doCellposeSegmentation.Rd b/man/doCellposeSegmentation.Rd index f7cd9e0e3..a63c72e15 100644 --- a/man/doCellposeSegmentation.Rd +++ b/man/doCellposeSegmentation.Rd @@ -5,9 +5,9 @@ \title{perform cellpose segmentation} \usage{ doCellposeSegmentation( - python_env = "giotto_cellpose", - image_dir, + input, mask_output, + python_env = "giotto_segmentation", channel_1 = 0, channel_2 = 0, model_name = "cyto3", @@ -25,20 +25,21 @@ doCellposeSegmentation( anisotropy = NULL, stitch_threshold = 0, min_size = 15, + max_size_fraction = 0.4, niter = NULL, augment = FALSE, - tile = TRUE, tile_overlap = 0.1, bsize = 224, + dP_smooth, interp = TRUE, compute_masks = TRUE, progress = NULL, - verbose = TRUE, + verbose = NULL, ... ) } \arguments{ -\item{image_dir}{character, required. Provide a path to a gray scale or a +\item{input}{character, required. Provide a path to a gray scale or a three channel image.} \item{mask_output}{required. Provide a path to the output mask file.} @@ -55,64 +56,95 @@ if you want to run cutomized trained model, place your model file in simultaneously on the GPU. Can make smaller or bigger depending on GPU memory usage. Defaults to 8.} -\item{resample}{Cellpose Parameter} +\item{resample}{(logical, optional) – run dynamics at original image size +(will be slower but create more accurate boundaries). Defaults to True.} -\item{channel_axis}{Cellpose Parameter} +\item{channel_axis}{(int, optional) – channel axis in element of list x, or +of np.ndarray x. if NULL, channels dimension is attempted to be +automatically determined. Defaults to NULL.} -\item{z_axis}{Cellpose Parameter} +\item{z_axis}{(int, optional) – z axis in element of list x, or of +np.ndarray x. if NULL, z dimension is attempted to be automatically +determined. Defaults to NULL.} -\item{normalize}{Cellpose Parameter} +\item{normalize}{Logical or list. Controls image normalization: +If TRUE, normalizes to 1st-99th percentile +Can be a list with parameters: +* lowhigh: Numeric vector c(low, high) for manual normalization values +* sharpen: Numeric, image sharpening factor (1/4-1/8 of cell diameter in pixels) +* normalize: Logical, whether to run normalization +* percentile: Numeric vector c(low_perc, high_perc) +* tile_norm: Integer, window size in pixels for tile normalization +* norm3D: Logical, normalize across full z-stack +Default: TRUE} -\item{invert}{Cellpose Parameter} +\item{invert}{Logical. Inverts pixel intensity before processing. +Default: FALSE} -\item{rescale}{Cellpose Parameter} +\item{rescale}{Numeric. Resize factor for images. +Default: NULL (sets to 1.0)} -\item{diameter}{Cellpose Parameter} +\item{diameter}{Numeric. Cell diameter for each image. +Default: NULL (uses diam_mean or diam_train if available)} -\item{flow_threshold}{Cellpose Parameter} +\item{flow_threshold}{Numeric. Flow error threshold for cell retention (2D only). +Default: 0.4} -\item{cellprob_threshold}{Cellpose Parameter} +\item{cellprob_threshold}{Numeric. Threshold for mask pixel retention. +Default: 0.0} -\item{do_3D}{Cellpose Parameter} +\item{do_3D}{Logical. Enable 3D segmentation for 3D/4D inputs. +Default: FALSE} -\item{anisotropy}{Cellpose Parameter} +\item{anisotropy}{Numeric. Z-axis rescaling factor for 3D segmentation. +Default: NULL} -\item{stitch_threshold}{Cellpose Parameter} +\item{stitch_threshold}{Numeric. Threshold for 3D mask stitching (2D mode only). +Default: 0.0} -\item{min_size}{Cellpose Parameter} +\item{min_size}{Integer. Minimum ROI size in pixels. +Default: 15} -\item{niter}{Cellpose Parameter} +\item{max_size_fraction}{Numeric. Maximum mask size as fraction of image. +Default: 0.4} -\item{augment}{Cellpose Parameter} +\item{niter}{Integer. Number of dynamics computation iterations. +Default: NULL (set based on diameter)} -\item{tile}{Cellpose Parameter} +\item{augment}{Logical. Enable tile augmentation with overlapping. +Default: FALSE} -\item{tile_overlap}{Cellpose Parameter} +\item{tile_overlap}{Numeric. Tile overlap fraction for flow computation. +Default: 0.1} -\item{bsize}{Cellpose Parameter} +\item{bsize}{Integer. Block size for tiles (recommended: 224). +Default: 224} -\item{interp}{Cellpose Parameter} +\item{dP_smooth}{Integer. Gaussian smoothing standard deviation for 3D flows.} -\item{compute_masks}{Cellpose Parameter} +\item{interp}{Logical. Enable interpolation for 2D dynamics. +Default: TRUE} -\item{progress}{Cellpose Parameter} +\item{compute_masks}{Logical. Compute dynamics and return masks. +Default: TRUE} + +\item{progress}{progress bar. Defaults to NULL} \item{python_path}{python environment with cellpose installed. -default = "giotto_cellpose".} +default = "giotto_segmentation".} } \value{ No return variable, as this will write directly to output path provided. } \description{ -perform the Giotto Wrapper of cellpose segmentation. This is for a model +Perform the Giotto Wrapper of cellpose segmentation. This is for a model inference to generate segmentation mask file from input image. main parameters needed } \examples{ -# example code doCellposeSegmentation( - image_dir = input_image, + input = input_image, mask_output = output, channel_1 = 2, channel_2 = 1, model_name = "cyto3", batch_size = 4 ) diff --git a/man/doMesmerSegmentation.Rd b/man/doMesmerSegmentation.Rd index b300e775a..7aeaebe3f 100644 --- a/man/doMesmerSegmentation.Rd +++ b/man/doMesmerSegmentation.Rd @@ -5,46 +5,46 @@ \title{perform Mesmer(Deepcell) segmentation} \usage{ doMesmerSegmentation( - Image_dir, - python_env = "giotto_segmentation", - Nucleus_channel = 1, - Memberane_channel = 2, - pixel_per_micron = 0.25, + input, mask_output, - verbose = F, + python_env = "giotto_segmentation", + nucleus_channel = 1, + membrane_channel = 2, + micron_scale = 0.25, + verbose = NULL, ... ) } \arguments{ -\item{Image_dir}{character, required. Provide a path to a IF image.} +\item{input}{character, required. Provide a path to a IF image.} + +\item{mask_output}{required. Provide a path to the output mask file.} \item{python_env}{python environment with deepcell installed. default = "giotto_segmentation". See deepcell official website for more details.} -\item{Nucleus_channel}{channel number for Nuclei, default to 1} - -\item{Memberane_channel}{channel number for cell boundary, default to 2} +\item{nucleus_channel}{channel number for Nuclei, default to 1} -\item{pixel_per_micron}{physical micron size per pixel, default to 0.25} +\item{membrane_channel}{channel number for cell boundary, default to 2} -\item{mask_output}{required. Provide a path to the output mask file.} +\item{micron_scale}{numeric. Multiplicative scalefactor to convert pixel +dimensions to physical microns.} } \value{ No return variable, as this will write directly to output path provided. } \description{ -perform the Giotto Wrapper of mesmer segmentation. This is for a model +Perform the Giotto Wrapper of mesmer segmentation. This is for a model inference to generate segmentation mask file from input image. main parameters needed } \examples{ -# example code doMesmerSegmentation( - Image_dir = input_image, + input = input_image, mask_output = output, - Nucleus_channel = 1, - Memberane_channel = 2, - pixel_per_micron = 0.5 + nucleus_channel = 1, + membrane_channel = 2, + micron_scale = 0.5 ) } diff --git a/man/doStardistSegmentation.Rd b/man/doStardistSegmentation.Rd index dbe02c9d5..3e4e23f84 100644 --- a/man/doStardistSegmentation.Rd +++ b/man/doStardistSegmentation.Rd @@ -5,25 +5,25 @@ \title{perform Stardist segmentation} \usage{ doStardistSegmentation( - Image_dir, - python_env = "giotto_segmentation", + input, mask_output, + python_env = "giotto_segmentation", model_name = "2D_versatile_fluo", nuclei_channel = NULL, prob_thresh = NULL, nms_thresh = NULL, - verbose = F, + verbose = NULL, ... ) } \arguments{ -\item{Image_dir}{character, required. Provide a path to an image.} +\item{input}{character, required. Provide a path to an image.} + +\item{mask_output}{required. Provide a path to the output mask file.} \item{python_env}{python environment with Stardist installed. default = "giotto_segmentation". See Stardist official website for more details.} -\item{mask_output}{required. Provide a path to the output mask file.} - \item{model_name}{Name of the model to run inference. Default to '2D_versatile_fluo'. If using HE model, input image must be RGB, else the nuclei_channel must be given} @@ -37,14 +37,14 @@ If using HE model, input image must be RGB, else the nuclei_channel must be give No return variable, as this will write directly to output path provided. } \description{ -perform the Giotto Wrapper of Stardist 2D segmentation. This is for a model +Perform the Giotto wrapper of Stardist 2D segmentation. This is for a model inference to generate segmentation mask file from input image. main parameters needed } \examples{ # example code doStardistSegmentation( - Image_dir = input_image, + input = input_image, mask_output = output, model_name = '2D_versatile_fluo', nuclei_channel = 3 From 7daa9919e5adc74296e2f6ccaff2b92c9e0647fd Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:12:15 -0500 Subject: [PATCH 03/21] chore: styler --- R/cell_segmentation.R | 200 +++++++++++++++++++----------------------- 1 file changed, 92 insertions(+), 108 deletions(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index 8f900213e..e6bc0ed4c 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -16,12 +16,11 @@ #' of the tile: sx (start x), ex (end x), sy, and ey. #' #' @export -doCellSegmentation <- function( - raster_img, - folder_path, - reduce_resolution = 4, - overlapping_pixels = 50, - python_path = NULL) { +doCellSegmentation <- function(raster_img, + folder_path, + reduce_resolution = 4, + overlapping_pixels = 50, + python_path = NULL) { package_check("deepcell", repository = "pip") package_check("PIL", repository = "pip") @@ -109,13 +108,13 @@ doCellSegmentation <- function( #' @param batch_size Cellpose Parameter, Number of 224x224 patches to run #' simultaneously on the GPU. Can make smaller or bigger depending on GPU #' memory usage. Defaults to 8. -#' @param resample (logical, optional) – run dynamics at original image size +#' @param resample (logical, optional) – run dynamics at original image size #' (will be slower but create more accurate boundaries). Defaults to True. -#' @param channel_axis (int, optional) – channel axis in element of list x, or -#' of np.ndarray x. if NULL, channels dimension is attempted to be +#' @param channel_axis (int, optional) – channel axis in element of list x, or +#' of np.ndarray x. if NULL, channels dimension is attempted to be #' automatically determined. Defaults to NULL. -#' @param z_axis (int, optional) – z axis in element of list x, or of -#' np.ndarray x. if NULL, z dimension is attempted to be automatically +#' @param z_axis (int, optional) – z axis in element of list x, or of +#' np.ndarray x. if NULL, z dimension is attempted to be automatically #' determined. Defaults to NULL. #' @param normalize Logical or list. Controls image normalization: #' If TRUE, normalizes to 1st-99th percentile @@ -127,7 +126,7 @@ doCellSegmentation <- function( #' * tile_norm: Integer, window size in pixels for tile normalization #' * norm3D: Logical, normalize across full z-stack #' Default: TRUE -#' @param invert Logical. Inverts pixel intensity before processing. +#' @param invert Logical. Inverts pixel intensity before processing. #' Default: FALSE #' @param rescale Numeric. Resize factor for images. #' Default: NULL (sets to 1.0) @@ -171,40 +170,40 @@ doCellSegmentation <- function( #' ) #' @export doCellposeSegmentation <- function(input, - mask_output, - python_env = "giotto_segmentation", - channel_1 = 0, - channel_2 = 0, - model_name = "cyto3", - batch_size = 8, - resample = TRUE, - channel_axis = NULL, - z_axis = NULL, - normalize = TRUE, - invert = FALSE, - rescale = NULL, - diameter = NULL, - flow_threshold = 0.4, - cellprob_threshold = 0.0, - do_3D = FALSE, - anisotropy = NULL, - stitch_threshold = 0.0, - min_size = 15, - max_size_fraction = 0.4, - niter = NULL, - augment = FALSE, - tile_overlap = 0.1, - bsize = 224, - dP_smooth, - interp = TRUE, - compute_masks = TRUE, - progress = NULL, - verbose = NULL, - ...) { + mask_output, + python_env = "giotto_segmentation", + channel_1 = 0, + channel_2 = 0, + model_name = "cyto3", + batch_size = 8, + resample = TRUE, + channel_axis = NULL, + z_axis = NULL, + normalize = TRUE, + invert = FALSE, + rescale = NULL, + diameter = NULL, + flow_threshold = 0.4, + cellprob_threshold = 0.0, + do_3D = FALSE, + anisotropy = NULL, + stitch_threshold = 0.0, + min_size = 15, + max_size_fraction = 0.4, + niter = NULL, + augment = FALSE, + tile_overlap = 0.1, + bsize = 224, + dP_smooth, + interp = TRUE, + compute_masks = TRUE, + progress = NULL, + verbose = NULL, + ...) { ## Load required python libraries GiottoClass::set_giotto_python_path(python_env) GiottoUtils::package_check("cellpose>=3.1.0", repository = "pip") - + # Check Input arguments model_name <- match.arg( model_name, unique(c("cyto3", "cyto2", "cyto", "nuclei", model_name)) @@ -219,7 +218,8 @@ doCellposeSegmentation <- function(input, if (!(torch$cuda$is_available())) { warning( - "GPU is not available for this session, inference may be slow.") + "GPU is not available for this session, inference may be slow." + ) } GiottoUtils::vmsg( @@ -297,21 +297,21 @@ doCellposeSegmentation <- function(input, #' @examples #' doMesmerSegmentation( #' input = input_image, -#' mask_output = output, +#' mask_output = output, #' nucleus_channel = 1, #' membrane_channel = 2, #' micron_scale = 0.5 #' ) #' @export -doMesmerSegmentation <- function(input, - mask_output, - python_env = 'giotto_segmentation', - nucleus_channel = 1, - membrane_channel = 2, - micron_scale = 0.25, - verbose = NULL, - ... -){ +doMesmerSegmentation <- function( + input, + mask_output, + python_env = "giotto_segmentation", + nucleus_channel = 1, + membrane_channel = 2, + micron_scale = 0.25, + verbose = NULL, + ...) { ## Load required python libraries GiottoClass::set_giotto_python_path(python_env) GiottoUtils::package_check("deepcell", repository = "pip") @@ -319,35 +319,36 @@ doMesmerSegmentation <- function(input, np <- reticulate::import("numpy") deepcell <- reticulate::import("deepcell.applications") message("successfully loaded giotto environment with deepcell.") - + # Initialize the Mesmer application from DeepCell mesmer <- deepcell$Mesmer() - + GiottoUtils::vmsg( .v = verbose, .is_debug = FALSE, "Loading Image... ", ) GiottoUtils::package_check("terra") - rast = terra::rast(input) + rast <- terra::rast(input) # Convert the R matrix to a NumPy array explicitly nucleus_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(nucleus_channel)]]))) membrane_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(membrane_channel)]]))) stacked_array <- np$stack(list(nucleus_channel_np, membrane_channel_np), axis = as.integer(-1)) # Add a new axis to the stacked array to fit Mesmer input stacked_array <- np$expand_dims(stacked_array, axis = as.integer(0)) - + GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Segmenting Image...") - segmentation_predictions = mesmer$predict( - stacked_array, image_mpp = micron_scale + segmentation_predictions <- mesmer$predict( + stacked_array, + image_mpp = micron_scale ) - mask <- segmentation_predictions[1,,,1] + mask <- segmentation_predictions[1, , , 1] mask_r <- reticulate::py_to_r(mask) - + GiottoUtils::vmsg( .v = verbose, .is_debug = FALSE, "Segmentation finished... Saving mask file..." ) - + rast <- terra::rast(mask_r) terra::writeRaster(rast, mask_output, overwrite = TRUE) } @@ -365,7 +366,7 @@ doMesmerSegmentation <- function(input, #' @param python_env python environment with Stardist installed. #' default = "giotto_segmentation". See Stardist official website for more details. #' @param mask_output required. Provide a path to the output mask file. -#' @param model_name Name of the model to run inference. Default to '2D_versatile_fluo'. +#' @param model_name Name of the model to run inference. Default to '2D_versatile_fluo'. #' If using HE model, input image must be RGB, else the nuclei_channel must be given #' @param nuclei_channel Required using IF based nuclei segmentation, channel number of the nuclei staining. #' @param prob_thresh prob_thresh for model (if not given use model default) @@ -375,21 +376,22 @@ doMesmerSegmentation <- function(input, #' # example code #' doStardistSegmentation( #' input = input_image, -#' mask_output = output, -#' model_name = '2D_versatile_fluo', +#' mask_output = output, +#' model_name = "2D_versatile_fluo", #' nuclei_channel = 3 #' ) -#' +#' #' @export -doStardistSegmentation <- function(input, - mask_output, - python_env = 'giotto_segmentation', - model_name = '2D_versatile_fluo', - nuclei_channel = NULL, - prob_thresh = NULL, - nms_thresh = NULL, - verbose = NULL, - ...){ +doStardistSegmentation <- function( + input, + mask_output, + python_env = "giotto_segmentation", + model_name = "2D_versatile_fluo", + nuclei_channel = NULL, + prob_thresh = NULL, + nms_thresh = NULL, + verbose = NULL, + ...) { # Import the necessary Python modules ## Load required python libraries GiottoClass::set_giotto_python_path(python_env) @@ -397,8 +399,8 @@ doStardistSegmentation <- function(input, stardist <- reticulate::import("stardist.models") csbdeep <- reticulate::import("csbdeep.utils") np <- reticulate::import("numpy") - - # Load the StarDist2D model + + # Load the StarDist2D model model_name <- match.arg( model_name, unique(c("2D_versatile_fluo", "2D_versatile_he", "2D_paper_dsb2018", "2D_demo", model_name)) ) @@ -406,34 +408,33 @@ doStardistSegmentation <- function(input, .v = verbose, .is_debug = FALSE, "Loading model ", model_name ) - + model <- stardist$StarDist2D$from_pretrained(model_name) - + # Load the image GiottoUtils::vmsg( .v = verbose, .is_debug = FALSE, "Loading Image from ", input ) GiottoUtils::package_check("terra") - rast = terra::rast(input) - if (model_name != '2D_versatile_he' && is.null(nuclei_channel)){ - stop('using IF based nuclei segmentation, please specify nuclei channel') - } - else if( model_name == '2D_versatile_he'){ + rast <- terra::rast(input) + if (model_name != "2D_versatile_he" && is.null(nuclei_channel)) { + stop("using IF based nuclei segmentation, please specify nuclei channel") + } else if (model_name == "2D_versatile_he") { img <- np$array(terra::as.array(rast)) - } - else { + } else { img <- np$array(drop(terra::as.array(rast[[as.numeric(nuclei_channel)]]))) } # Normalize the image normalized_img <- csbdeep$normalize(img) - + # Perform prediction with StarDist2D model results <- model$predict_instances(normalized_img, - prob_thresh = prob_thresh, - nms_thresh = nms_thresh) - + prob_thresh = prob_thresh, + nms_thresh = nms_thresh + ) + # Extract the labels (first output from predict_instances) mask <- results[[1]] GiottoUtils::vmsg( @@ -443,20 +444,3 @@ doStardistSegmentation <- function(input, rast_m <- terra::rast(mask) terra::writeRaster(rast_m, mask_output, overwrite = TRUE) } - - - - - - - - - - - - - - - - - From 0d65d3ec0e2eb8a23076f3a0974f8a367026cafd Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:17:37 -0500 Subject: [PATCH 04/21] chore: version for dev and news update --- DESCRIPTION | 2 +- NEWS.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index a869dc843..7508674ea 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: Giotto Title: Spatial Single-Cell Transcriptomics Toolbox -Version: 4.1.6 +Version: 4.1.7 Authors@R: c( person("Ruben", "Dries", email = "rubendries@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7650-7754")), diff --git a/NEWS.md b/NEWS.md index 11c795336..753da362f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # Giotto 4.1.7 +## Breaking Changes +* Param naming changes for segmentation wrapper functions `doMesmerSegmentation()`, `doCellposeSegmentation()` `doStardistSegmentation()` + ## Changes * GiottoUtils req raised to 0.2.3 From 4c38bc9c5f826fd52d1ae05e2f9bdc59090c6ad5 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:22:12 -0500 Subject: [PATCH 05/21] Update DESCRIPTION --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7508674ea..bb88cb70a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,7 @@ Imports: dbscan (>= 1.1-3), ggraph, ggplot2 (>= 3.1.1), - GiottoUtils (>= 0.2.2), + GiottoUtils (>= 0.2.3), GiottoVisuals (>= 0.2.10), igraph (>= 1.2.4.1), Matrix (>= 1.6-2), From e6ded33419cc8761564e01a25ec3e12cdf238f9a Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:11:31 -0500 Subject: [PATCH 06/21] chore: docs and cleanup code --- R/cell_segmentation.R | 56 +++++++++++------------------------ man/doCellposeSegmentation.Rd | 12 ++++---- man/doMesmerSegmentation.Rd | 2 +- man/doStardistSegmentation.Rd | 6 ++-- 4 files changed, 28 insertions(+), 48 deletions(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index e6bc0ed4c..39876668e 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -201,8 +201,8 @@ doCellposeSegmentation <- function(input, verbose = NULL, ...) { ## Load required python libraries - GiottoClass::set_giotto_python_path(python_env) - GiottoUtils::package_check("cellpose>=3.1.0", repository = "pip") + set_giotto_python_path(python_env) + package_check("cellpose>=3.1.0", repository = "pip") # Check Input arguments model_name <- match.arg( @@ -222,12 +222,10 @@ doCellposeSegmentation <- function(input, ) } - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, "Loading Image from ", input - ) + vmsg(.v = verbose, "Loading Image from ", input) img <- cellpose$io$imread(input) - GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Loading Model...") + vmsg(.v = verbose, "Loading Model...") model_to_seg <- cellpose$models$Cellpose( model_type = model_name, @@ -235,7 +233,7 @@ doCellposeSegmentation <- function(input, ) channel_to_seg <- as.integer(c(channel_1, channel_2)) - GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Segmenting Image...") + vmsg(.v = verbose, "Segmenting Image...") segmentation <- model_to_seg$eval result <- segmentation(img, @@ -265,11 +263,9 @@ doCellposeSegmentation <- function(input, progress = progress ) masks <- result[[1]] - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, - "Segmentation finished... Saving mask file..." + vmsg(.v = verbose, .is_debug = FALSE, + "Segmentation finished... Saving mask file..." ) - GiottoUtils::package_check("terra") rast <- terra::rast(masks) terra::writeRaster(rast, mask_output, overwrite = TRUE) } @@ -313,20 +309,17 @@ doMesmerSegmentation <- function( verbose = NULL, ...) { ## Load required python libraries - GiottoClass::set_giotto_python_path(python_env) - GiottoUtils::package_check("deepcell", repository = "pip") + set_giotto_python_path(python_env) + package_check("deepcell", repository = "pip") tiff <- reticulate::import("tifffile") np <- reticulate::import("numpy") deepcell <- reticulate::import("deepcell.applications") - message("successfully loaded giotto environment with deepcell.") + vmsg(.v = verbose, "successfully loaded giotto environment with deepcell.") # Initialize the Mesmer application from DeepCell mesmer <- deepcell$Mesmer() - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, "Loading Image... ", - ) - GiottoUtils::package_check("terra") + vmsg(.v = verbose, "Loading Image... ") rast <- terra::rast(input) # Convert the R matrix to a NumPy array explicitly nucleus_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(nucleus_channel)]]))) @@ -335,7 +328,7 @@ doMesmerSegmentation <- function( # Add a new axis to the stacked array to fit Mesmer input stacked_array <- np$expand_dims(stacked_array, axis = as.integer(0)) - GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Segmenting Image...") + vmsg(.v = verbose, "Segmenting Image...") segmentation_predictions <- mesmer$predict( stacked_array, @@ -344,10 +337,7 @@ doMesmerSegmentation <- function( mask <- segmentation_predictions[1, , , 1] mask_r <- reticulate::py_to_r(mask) - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, - "Segmentation finished... Saving mask file..." - ) + vmsg(.v = verbose, "Segmentation finished... Saving mask file...") rast <- terra::rast(mask_r) terra::writeRaster(rast, mask_output, overwrite = TRUE) @@ -394,8 +384,8 @@ doStardistSegmentation <- function( ...) { # Import the necessary Python modules ## Load required python libraries - GiottoClass::set_giotto_python_path(python_env) - GiottoUtils::package_check("stardist", repository = "pip") + set_giotto_python_path(python_env) + package_check("stardist", repository = "pip") stardist <- reticulate::import("stardist.models") csbdeep <- reticulate::import("csbdeep.utils") np <- reticulate::import("numpy") @@ -404,19 +394,12 @@ doStardistSegmentation <- function( model_name <- match.arg( model_name, unique(c("2D_versatile_fluo", "2D_versatile_he", "2D_paper_dsb2018", "2D_demo", model_name)) ) - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, "Loading model ", - model_name - ) + vmsg(.v = verbose, "Loading model ", model_name) model <- stardist$StarDist2D$from_pretrained(model_name) # Load the image - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, "Loading Image from ", - input - ) - GiottoUtils::package_check("terra") + vmsg(.v = verbose, "Loading Image from ", input) rast <- terra::rast(input) if (model_name != "2D_versatile_he" && is.null(nuclei_channel)) { stop("using IF based nuclei segmentation, please specify nuclei channel") @@ -437,10 +420,7 @@ doStardistSegmentation <- function( # Extract the labels (first output from predict_instances) mask <- results[[1]] - GiottoUtils::vmsg( - .v = verbose, .is_debug = FALSE, - "Segmentation finished... Saving mask file..." - ) + vmsg(.v = verbose, "Segmentation finished... Saving mask file...") rast_m <- terra::rast(mask) terra::writeRaster(rast_m, mask_output, overwrite = TRUE) } diff --git a/man/doCellposeSegmentation.Rd b/man/doCellposeSegmentation.Rd index a63c72e15..950cd30b6 100644 --- a/man/doCellposeSegmentation.Rd +++ b/man/doCellposeSegmentation.Rd @@ -56,15 +56,15 @@ if you want to run cutomized trained model, place your model file in simultaneously on the GPU. Can make smaller or bigger depending on GPU memory usage. Defaults to 8.} -\item{resample}{(logical, optional) – run dynamics at original image size +\item{resample}{(logical, optional) – run dynamics at original image size (will be slower but create more accurate boundaries). Defaults to True.} -\item{channel_axis}{(int, optional) – channel axis in element of list x, or -of np.ndarray x. if NULL, channels dimension is attempted to be +\item{channel_axis}{(int, optional) – channel axis in element of list x, or +of np.ndarray x. if NULL, channels dimension is attempted to be automatically determined. Defaults to NULL.} -\item{z_axis}{(int, optional) – z axis in element of list x, or of -np.ndarray x. if NULL, z dimension is attempted to be automatically +\item{z_axis}{(int, optional) – z axis in element of list x, or of +np.ndarray x. if NULL, z dimension is attempted to be automatically determined. Defaults to NULL.} \item{normalize}{Logical or list. Controls image normalization: @@ -78,7 +78,7 @@ Can be a list with parameters: * norm3D: Logical, normalize across full z-stack Default: TRUE} -\item{invert}{Logical. Inverts pixel intensity before processing. +\item{invert}{Logical. Inverts pixel intensity before processing. Default: FALSE} \item{rescale}{Numeric. Resize factor for images. diff --git a/man/doMesmerSegmentation.Rd b/man/doMesmerSegmentation.Rd index 7aeaebe3f..30a190391 100644 --- a/man/doMesmerSegmentation.Rd +++ b/man/doMesmerSegmentation.Rd @@ -42,7 +42,7 @@ main parameters needed \examples{ doMesmerSegmentation( input = input_image, - mask_output = output, + mask_output = output, nucleus_channel = 1, membrane_channel = 2, micron_scale = 0.5 diff --git a/man/doStardistSegmentation.Rd b/man/doStardistSegmentation.Rd index 3e4e23f84..450cfacb2 100644 --- a/man/doStardistSegmentation.Rd +++ b/man/doStardistSegmentation.Rd @@ -24,7 +24,7 @@ doStardistSegmentation( \item{python_env}{python environment with Stardist installed. default = "giotto_segmentation". See Stardist official website for more details.} -\item{model_name}{Name of the model to run inference. Default to '2D_versatile_fluo'. +\item{model_name}{Name of the model to run inference. Default to '2D_versatile_fluo'. If using HE model, input image must be RGB, else the nuclei_channel must be given} \item{nuclei_channel}{Required using IF based nuclei segmentation, channel number of the nuclei staining.} @@ -45,8 +45,8 @@ main parameters needed # example code doStardistSegmentation( input = input_image, - mask_output = output, - model_name = '2D_versatile_fluo', + mask_output = output, + model_name = "2D_versatile_fluo", nuclei_channel = 3 ) From 8dcc263f2d9f1be6adbc1a02debf93a50b2401b7 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:12:22 -0500 Subject: [PATCH 07/21] chore: styler --- R/cell_segmentation.R | 104 +++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index 39876668e..e411b3397 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -169,37 +169,38 @@ doCellSegmentation <- function(raster_img, #' channel_2 = 1, model_name = "cyto3", batch_size = 4 #' ) #' @export -doCellposeSegmentation <- function(input, - mask_output, - python_env = "giotto_segmentation", - channel_1 = 0, - channel_2 = 0, - model_name = "cyto3", - batch_size = 8, - resample = TRUE, - channel_axis = NULL, - z_axis = NULL, - normalize = TRUE, - invert = FALSE, - rescale = NULL, - diameter = NULL, - flow_threshold = 0.4, - cellprob_threshold = 0.0, - do_3D = FALSE, - anisotropy = NULL, - stitch_threshold = 0.0, - min_size = 15, - max_size_fraction = 0.4, - niter = NULL, - augment = FALSE, - tile_overlap = 0.1, - bsize = 224, - dP_smooth, - interp = TRUE, - compute_masks = TRUE, - progress = NULL, - verbose = NULL, - ...) { +doCellposeSegmentation <- function( + input, + mask_output, + python_env = "giotto_segmentation", + channel_1 = 0, + channel_2 = 0, + model_name = "cyto3", + batch_size = 8, + resample = TRUE, + channel_axis = NULL, + z_axis = NULL, + normalize = TRUE, + invert = FALSE, + rescale = NULL, + diameter = NULL, + flow_threshold = 0.4, + cellprob_threshold = 0.0, + do_3D = FALSE, + anisotropy = NULL, + stitch_threshold = 0.0, + min_size = 15, + max_size_fraction = 0.4, + niter = NULL, + augment = FALSE, + tile_overlap = 0.1, + bsize = 224, + dP_smooth, + interp = TRUE, + compute_masks = TRUE, + progress = NULL, + verbose = NULL, + ...) { ## Load required python libraries set_giotto_python_path(python_env) package_check("cellpose>=3.1.0", repository = "pip") @@ -263,8 +264,9 @@ doCellposeSegmentation <- function(input, progress = progress ) masks <- result[[1]] - vmsg(.v = verbose, .is_debug = FALSE, - "Segmentation finished... Saving mask file..." + vmsg( + .v = verbose, .is_debug = FALSE, + "Segmentation finished... Saving mask file..." ) rast <- terra::rast(masks) terra::writeRaster(rast, mask_output, overwrite = TRUE) @@ -299,15 +301,14 @@ doCellposeSegmentation <- function(input, #' micron_scale = 0.5 #' ) #' @export -doMesmerSegmentation <- function( - input, - mask_output, - python_env = "giotto_segmentation", - nucleus_channel = 1, - membrane_channel = 2, - micron_scale = 0.25, - verbose = NULL, - ...) { +doMesmerSegmentation <- function(input, + mask_output, + python_env = "giotto_segmentation", + nucleus_channel = 1, + membrane_channel = 2, + micron_scale = 0.25, + verbose = NULL, + ...) { ## Load required python libraries set_giotto_python_path(python_env) package_check("deepcell", repository = "pip") @@ -372,16 +373,15 @@ doMesmerSegmentation <- function( #' ) #' #' @export -doStardistSegmentation <- function( - input, - mask_output, - python_env = "giotto_segmentation", - model_name = "2D_versatile_fluo", - nuclei_channel = NULL, - prob_thresh = NULL, - nms_thresh = NULL, - verbose = NULL, - ...) { +doStardistSegmentation <- function(input, + mask_output, + python_env = "giotto_segmentation", + model_name = "2D_versatile_fluo", + nuclei_channel = NULL, + prob_thresh = NULL, + nms_thresh = NULL, + verbose = NULL, + ...) { # Import the necessary Python modules ## Load required python libraries set_giotto_python_path(python_env) From 01500090c8825a46f9a83ad809e38381cc05efeb Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:25:04 -0500 Subject: [PATCH 08/21] fix: cellpose version req --- R/cell_segmentation.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index e411b3397..21829845e 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -203,7 +203,7 @@ doCellposeSegmentation <- function( ...) { ## Load required python libraries set_giotto_python_path(python_env) - package_check("cellpose>=3.1.0", repository = "pip") + package_check("cellpose", repository = "pip:cellpose>=3.1.0") # Check Input arguments model_name <- match.arg( From 90e20a39a404fdcf61779d2e8eea6c85e695627a Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:00:07 -0500 Subject: [PATCH 09/21] fix: cellpose default for dP_smooth --- R/cell_segmentation.R | 2 +- man/doCellposeSegmentation.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index 21829845e..cdf8a5912 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -195,7 +195,7 @@ doCellposeSegmentation <- function( augment = FALSE, tile_overlap = 0.1, bsize = 224, - dP_smooth, + dP_smooth = 0L, interp = TRUE, compute_masks = TRUE, progress = NULL, diff --git a/man/doCellposeSegmentation.Rd b/man/doCellposeSegmentation.Rd index 950cd30b6..f62bdefbb 100644 --- a/man/doCellposeSegmentation.Rd +++ b/man/doCellposeSegmentation.Rd @@ -30,7 +30,7 @@ doCellposeSegmentation( augment = FALSE, tile_overlap = 0.1, bsize = 224, - dP_smooth, + dP_smooth = 0L, interp = TRUE, compute_masks = TRUE, progress = NULL, From 16ad1807b0f8e290a3fd554c3cc67b97fc964279 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:53:09 -0500 Subject: [PATCH 10/21] enh: area calculation through `addStatistics()` --- R/auxiliary_giotto.R | 128 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 27 deletions(-) diff --git a/R/auxiliary_giotto.R b/R/auxiliary_giotto.R index cba88dd3d..3681d0406 100644 --- a/R/auxiliary_giotto.R +++ b/R/auxiliary_giotto.R @@ -585,21 +585,28 @@ addCellStatistics <- function(gobject, #' @param gobject giotto object #' @param spat_unit spatial unit #' @param feat_type feature type +#' @param stats character. What statistics to add. +#' default = c("cell", "feature") See details #' @param expression_values expression values to use #' @param detection_threshold detection threshold to consider a feature detected #' @param return_gobject boolean: return giotto object (default = TRUE) #' @param verbose be verbose #' @returns giotto object if return_gobject = TRUE, else a list with results -#' @details See \code{\link{addFeatStatistics}} and -#' \code{\link{addCellStatistics}} +#' @details +#' # `stats` options +#' "feature" - includes \code{\link{addFeatStatistics}} results +#' "cell" - includes \code{\link{addCellStatistics}} results +#' "area" - includes polygon areas #' @examples #' g <- GiottoData::loadGiottoMini("visium") #' #' addStatistics(g) +#' @md #' @export addStatistics <- function(gobject, feat_type = NULL, spat_unit = NULL, + stats = c("feature", "cell"), expression_values = c("normalized", "scaled", "custom"), detection_threshold = 0, return_gobject = TRUE, @@ -614,38 +621,63 @@ addStatistics <- function(gobject, spat_unit = spat_unit, feat_type = feat_type ) - - # get feats statistics - feat_stats <- addFeatStatistics( - gobject = gobject, - feat_type = feat_type, - spat_unit = spat_unit, - expression_values = expression_values, - detection_threshold = detection_threshold, - return_gobject = return_gobject, - verbose = verbose + + stat_choices <- c("cell", "feature", "area") + stats <- match.arg( + tolower(stats), choices = stat_choices, several.ok = TRUE ) - if (return_gobject == TRUE) { - gobject <- feat_stats + if ("feature" %in% stats) { + # get feats statistics + feat_stats <- addFeatStatistics(gobject, + feat_type = feat_type, + spat_unit = spat_unit, + expression_values = expression_values, + detection_threshold = detection_threshold, + return_gobject = return_gobject, + verbose = verbose + ) + if (isTRUE(return_gobject)) { + gobject <- feat_stats + } } - # get cell statistics - cell_stats <- addCellStatistics( - gobject = gobject, - feat_type = feat_type, - spat_unit = spat_unit, - expression_values = expression_values, - detection_threshold = detection_threshold, - return_gobject = return_gobject, - verbose = verbose - ) + if ("cell" %in% stats) { + # get cell statistics + cell_stats <- addCellStatistics(gobject, + feat_type = feat_type, + spat_unit = spat_unit, + expression_values = expression_values, + detection_threshold = detection_threshold, + return_gobject = return_gobject, + verbose = verbose + ) + if (isTRUE(return_gobject)) { + gobject <- cell_stats + } + } - if (return_gobject == TRUE) { - gobject <- cell_stats + if (any("area" %in% stats)) { + poly_stats <- .add_poly_statistics(gobject, + spat_unit = spat_unit, + stats = stats, + return_gobject = return_gobject + ) + if (isTRUE(return_gobject)) { + gobject <- poly_stats + } + } + + + if (isTRUE(return_gobject)) { return(gobject) } else { - return(feat_stats = feat_stats, cell_stats = cell_stats) + out <- list( + feat_stats = feat_stats, + cell_stats = cell_stats, + poly_stats = poly_stats + ) + return(out) } } @@ -737,6 +769,48 @@ addFeatsPerc <- function(gobject, } } +.add_poly_statistics <- function(gobject, + spat_unit = "cell", + stats = c("area"), + return_gobject = TRUE +) { + stat_choices <- c("area") + stats <- match.arg( + tolower(stats), choices = stat_choices, several.ok = TRUE + ) + + p <- getPolygonInfo(gobject, + polygon_name = spat_unit, + return_giottoPolygon = TRUE + ) + x <- p[] + + # accumulate results values + # results order must be identical to the order of x + all_res <- list(cell_ID = x$poly_ID) + + if ("area" %in% stats) { + terra::crs(x) <- "local" + a <- terra::expanse(x, transform = FALSE) + all_res$area <- a + } + + res_dt <- do.call(data.table::data.table, all_res) + + if (isTRUE(return_gobject)) { + # append results if there are any + if (ncol(res_dt) > 1L) { + gobject <- addCellMetadata(gobject, + new_metadata = res_dt, + by_column = TRUE, + column_cell_ID = "cell_ID" + ) + } + return(gobject) + } else { + return(res_dt) + } +} From 88932f223ff17fe6aea21cba515e4c69896fed3a Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:56:48 -0500 Subject: [PATCH 11/21] chore: docs --- NEWS.md | 4 ++++ man/addStatistics.Rd | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 753da362f..896fc1593 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,10 @@ ## Breaking Changes * Param naming changes for segmentation wrapper functions `doMesmerSegmentation()`, `doCellposeSegmentation()` `doStardistSegmentation()` +## Enhancements +* new `stats` param in `addStatistics()` to control which statistics are calculated. +* `"area"` calculation added as an `addStatistics()` `stats` selection + ## Changes * GiottoUtils req raised to 0.2.3 diff --git a/man/addStatistics.Rd b/man/addStatistics.Rd index 9fee79234..9b88cf066 100644 --- a/man/addStatistics.Rd +++ b/man/addStatistics.Rd @@ -8,6 +8,7 @@ addStatistics( gobject, feat_type = NULL, spat_unit = NULL, + stats = c("feature", "cell"), expression_values = c("normalized", "scaled", "custom"), detection_threshold = 0, return_gobject = TRUE, @@ -21,6 +22,9 @@ addStatistics( \item{spat_unit}{spatial unit} +\item{stats}{character. What statistics to add. +default = c("cell", "feature") See details} + \item{expression_values}{expression values to use} \item{detection_threshold}{detection threshold to consider a feature detected} @@ -35,10 +39,12 @@ giotto object if return_gobject = TRUE, else a list with results \description{ Adds feature and cell statistics to the giotto object } -\details{ -See \code{\link{addFeatStatistics}} and -\code{\link{addCellStatistics}} +\section{\code{stats} options}{ +"feature" - includes \code{\link{addFeatStatistics}} results +"cell" - includes \code{\link{addCellStatistics}} results +"area" - includes polygon areas } + \examples{ g <- GiottoData::loadGiottoMini("visium") From 7a794d0467b22fca68478f26ba09379061f4da50 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:18:27 -0500 Subject: [PATCH 12/21] enh: gracefully ignore area calc when no polys are available - area is also added as a default calculation. This is pretty fast so there's not much loss here. --- R/auxiliary_giotto.R | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/R/auxiliary_giotto.R b/R/auxiliary_giotto.R index 3681d0406..76cebb0cd 100644 --- a/R/auxiliary_giotto.R +++ b/R/auxiliary_giotto.R @@ -606,7 +606,7 @@ addCellStatistics <- function(gobject, addStatistics <- function(gobject, feat_type = NULL, spat_unit = NULL, - stats = c("feature", "cell"), + stats = c("feature", "cell", "area"), expression_values = c("normalized", "scaled", "custom"), detection_threshold = 0, return_gobject = TRUE, @@ -769,6 +769,7 @@ addFeatsPerc <- function(gobject, } } +# this doesn't take much time .add_poly_statistics <- function(gobject, spat_unit = "cell", stats = c("area"), @@ -779,19 +780,23 @@ addFeatsPerc <- function(gobject, tolower(stats), choices = stat_choices, several.ok = TRUE ) - p <- getPolygonInfo(gobject, - polygon_name = spat_unit, - return_giottoPolygon = TRUE - ) - x <- p[] + poly_list <- gobject[["spatial_info", spat_unit]] + if (length(poly_list) > 0L) { + gpoly <- poly_list[[1L]] # extract from list + } else { + # if no polys available, return early + if (isTRUE(return_gobject)) return(gobject) + else return(data.table::data.table(cell_ID = spatIDs(gobject))) + } + sv <- gpoly[] # accumulate results values - # results order must be identical to the order of x - all_res <- list(cell_ID = x$poly_ID) + # results order must be identical to the order of sv + all_res <- list(cell_ID = sv$poly_ID) if ("area" %in% stats) { - terra::crs(x) <- "local" - a <- terra::expanse(x, transform = FALSE) + terra::crs(sv) <- "local" + a <- terra::expanse(sv, transform = FALSE) all_res$area <- a } From 343578b77eb86359ccd3b5a41bae574ce18152ea Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:34:32 -0500 Subject: [PATCH 13/21] enh: `addStatistics()` - add descriptive message about which expression info you are using to calc expression stats. - should make clearer that you can calc stats on other expression information. normalized is just the default, not what you need in order to use the function.( - fix `return_gobject = FALSE` outputs when not all stats options are calculated. --- R/auxiliary_giotto.R | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/R/auxiliary_giotto.R b/R/auxiliary_giotto.R index 76cebb0cd..4383a17ac 100644 --- a/R/auxiliary_giotto.R +++ b/R/auxiliary_giotto.R @@ -626,7 +626,21 @@ addStatistics <- function(gobject, stats <- match.arg( tolower(stats), choices = stat_choices, several.ok = TRUE ) + # expression values to be used + expression_values <- match.arg( + expression_values, + unique(c("normalized", "scaled", "custom", expression_values)) + ) + + if (any(c("feature", "cell") %in% stats)) { + vmsg( + .v = verbose, + sprintf("calculating statistics for \"%s\" expression", + expression_values) + ) + } + feat_stats <- NULL if ("feature" %in% stats) { # get feats statistics feat_stats <- addFeatStatistics(gobject, @@ -642,6 +656,7 @@ addStatistics <- function(gobject, } } + cell_stats <- NULL if ("cell" %in% stats) { # get cell statistics cell_stats <- addCellStatistics(gobject, @@ -656,7 +671,8 @@ addStatistics <- function(gobject, gobject <- cell_stats } } - + + poly_stats <- NULL if (any("area" %in% stats)) { poly_stats <- .add_poly_statistics(gobject, spat_unit = spat_unit, @@ -672,11 +688,10 @@ addStatistics <- function(gobject, if (isTRUE(return_gobject)) { return(gobject) } else { - out <- list( - feat_stats = feat_stats, - cell_stats = cell_stats, - poly_stats = poly_stats - ) + out <- list() + out$feat_stats <- feat_stats + out$cell_stats <- cell_stats + out$poly_stats <- poly_stats return(out) } } From e542968a2e78d8b160cb1d8eb65ee81c82d5f838 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:35:34 -0500 Subject: [PATCH 14/21] chore: docs --- man/addStatistics.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/addStatistics.Rd b/man/addStatistics.Rd index 9b88cf066..328183177 100644 --- a/man/addStatistics.Rd +++ b/man/addStatistics.Rd @@ -8,7 +8,7 @@ addStatistics( gobject, feat_type = NULL, spat_unit = NULL, - stats = c("feature", "cell"), + stats = c("feature", "cell", "area"), expression_values = c("normalized", "scaled", "custom"), detection_threshold = 0, return_gobject = TRUE, From 1e8613ad95131f40693c897898ea448fad38ea09 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:09:59 -0500 Subject: [PATCH 15/21] small updates to `adjustGiottoMatrix()` --- NEWS.md | 2 ++ R/auxiliary_giotto.R | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 896fc1593..1cb8c8015 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,9 +6,11 @@ ## Enhancements * new `stats` param in `addStatistics()` to control which statistics are calculated. * `"area"` calculation added as an `addStatistics()` `stats` selection +* `adjustGiottoMatrix()` now outputs a `Matrix` structure instead of a base `matrix` ## Changes * GiottoUtils req raised to 0.2.3 +* `adjustGiottoMatrix()` `update_slot` param deprecated in favor of `name`. # Giotto 4.1.6 (2024/12/09) diff --git a/R/auxiliary_giotto.R b/R/auxiliary_giotto.R index 4383a17ac..9818897f4 100644 --- a/R/auxiliary_giotto.R +++ b/R/auxiliary_giotto.R @@ -15,8 +15,10 @@ #' batch (max = 2) #' @param covariate_columns metadata columns that represent covariates to #' regress out +#' @param name character. Name to assign to adjusted matrix +#' (default = "custom") #' @param return_gobject boolean: return giotto object (default = TRUE) -#' @param update_slot expression slot that will be updated (default = custom) +#' @param update_slot deprecated. #' @returns giotto object or exprObj #' @details This function implements the \code{\link[limma]{removeBatchEffect}} #' function to remove known batch effects and to adjust expression values @@ -32,15 +34,24 @@ adjustGiottoMatrix <- function(gobject, expression_values = c("normalized", "scaled", "custom"), batch_columns = NULL, covariate_columns = NULL, + name = "custom", return_gobject = TRUE, - update_slot = c("custom")) { + update_slot = deprecated()) { # Catch for both batch and covariate being null - if (is.null(batch_columns) & is.null(covariate_columns)) { + if (is.null(batch_columns) && is.null(covariate_columns)) { stop("Metadata for either different batches or covariates must be provided.") } package_check("limma") + + name <- deprecate_param( + update_slot, name, fun = "adjustGiottoMatrix", when = "4.1.7" + ) + + name <- match.arg( + name, c("normalized", "scaled", "custom", name) + ) # Set feat_type and spat_unit spat_unit <- set_default_spat_unit( @@ -74,10 +85,6 @@ adjustGiottoMatrix <- function(gobject, } } - update_slot <- match.arg( - update_slot, c("normalized", "scaled", "custom", update_slot) - ) - # expression values to be used values <- match.arg( expression_values, @@ -123,10 +130,12 @@ adjustGiottoMatrix <- function(gobject, batch2 = batch_column_2, covariates = covariates ) + + adjusted_matrix <- Matrix::Matrix(adjusted_matrix) if (return_gobject == TRUE) { adjusted_matrix <- create_expr_obj( - name = update_slot, + name = name, exprMat = adjusted_matrix, spat_unit = spat_unit, feat_type = feat_type, From ac9d25734891658cd45113a351612ad8773232b1 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 31 Dec 2024 22:25:18 -0500 Subject: [PATCH 16/21] update landmark alignment --- R/image_registration.R | 100 ++++++++++++++-------- man/adjustGiottoMatrix.Rd | 8 +- man/calculateAffineMatrixFromLandmarks.Rd | 42 ++++++--- man/interactiveLandmarkSelection.Rd | 20 +++-- 4 files changed, 115 insertions(+), 55 deletions(-) diff --git a/R/image_registration.R b/R/image_registration.R index fbca01844..bbd633859 100644 --- a/R/image_registration.R +++ b/R/image_registration.R @@ -1020,18 +1020,19 @@ registerImagesFIJI <- function( -#' @title title Record landmarks by interactive selection +#' @title Record landmarks by interactive selection #' @name interactiveLandmarkSelection -#' @description Record landmarks by interactive selection -#' @param source_image the image to be plotted on the left, and landmarks will +#' @description Interactively select manual paired landmarks. +#' @param source the image to be plotted on the left, and landmarks will #' output in the first of the list. Input can be a ggplot object, -#' a GiottoImage, or a character represent a path to a image -#' @param target_image the image to be plotted on the right, and landmarks will -#' output in the second of the list. Input can be a ggplot object, a -#' GiottoImage, or a character represent a path to a image -#' -#' @returns a list of landmarks -#' +#' a `giottoLargeImage`-inheriting object, or a character filepath to an image +#' @param target the image to be plotted on the right, and landmarks will +#' output in the second of the list. Input can be a ggplot object, +#' a `giottoLargeImage`-inheriting object, or a character filepath to an image +#' @returns a list of 2 `data.frames` with columns x and y, designating paired +#' sets of landmarks between list 1 (from source) and list 2 (from target) +#' @seealso [calculateAffineMatrixFromLandmarks()] +#' @md #' @export interactiveLandmarkSelection <- function(source, target) { GiottoUtils::package_check("shiny") @@ -1215,34 +1216,63 @@ interactiveLandmarkSelection <- function(source, target) { -#' @title Calculate a affine transformation matrix from two set of landmarks +#' @title Calculate an affine transformation matrix from two sets of landmarks #' @name calculateAffineMatrixFromLandmarks -#' @description calculate a affine transformation matrix from two set of -#' landmarks -#' @param source_df source landmarks, two columns, first column represent -#' x coordinate and second column represent y coordinate. -#' @param target_df target landmarks, two columns, first column represent -#' x coordinate and second column represent y coordinate. -#' -#' @returns a 3 by 3 matrix with the third row close to (0,0,1) -#' +#' @description Calculate an affine transformation matrix from two sets of +#' landmarks. Source is the item that is transformed to align to target. The +#' The generated affine transformation matrix is the affine transform that +#' defines this transformation. +#' @param source,target `matrix` or `data.frame`-like. Source and target +#' landmarks provided as paired x (first) y (second) columns. +#' @returns a 3x3 `matrix` that defines an affine transform +#' @md +#' @seealso [interactiveLandmarkSelection()] [affine()] +#' @examples +#' m1 <- matrix(c( +#' 4211.476, 4925.052, +#' 5906.339, 4251.025, +#' 4830.097, 3342.676, +#' 5954.410, 3393.603 +#' ), +#' 4, 2, byrow = TRUE +#' ) +#' +#' m2 <- matrix(c( +#' 7218.570, -11999.111, +#' 8647.143, -15008.278, +#' 10150.203, -12953.273, +#' 10172.141, -15008.059 +#' ), +#' 4, 2, byrow = TRUE +#' ) +#' calculateAffineMatrixFromLandmarks(m1, m2) #' @export -calculateAffineMatrixFromLandmarks <- function(source_df, target_df) { - source_landmarks_matrix <- as.matrix(source_df) - source_landmarks_matrix <- cbind( - source_landmarks_matrix, rep(1, nrow(source_landmarks_matrix)) - ) - ## Create landmark matrix for the target image - target_landmarks_matrix <- as.matrix(target_df) - target_landmarks_matrix <- cbind( - target_landmarks_matrix, rep(1, nrow(target_landmarks_matrix)) - ) - ## Compute the affine matrix - source_dp <- t(source_landmarks_matrix) %*% source_landmarks_matrix - source_target_dp <- t(source_landmarks_matrix) %*% target_landmarks_matrix +calculateAffineMatrixFromLandmarks <- function(source, target) { + src <- as.matrix(source) + tgt <- as.matrix(target) + + # Compute centroids + source_centroid <- colMeans(src) + target_centroid <- colMeans(tgt) + + # Center the points + source_centered <- sweep(src, 2, source_centroid) + target_centered <- sweep(tgt, 2, target_centroid) + + # Compute 2x2 transformation + source_dp <- t(source_centered) %*% source_centered + source_target_dp <- t(source_centered) %*% target_centered source_dp_inv <- solve(source_dp) - Affine_matrix <- t(source_dp_inv %*% source_target_dp) - return(Affine_matrix) + A <- t(source_dp_inv %*% source_target_dp) + + # Compute translation + xyshift <- target_centroid - (A %*% source_centroid) + + # Build 3x3 affine matrix + affine_mtx <- cbind(A, xyshift) + affine_mtx <- rbind(affine_mtx, c(0, 0, 1)) + + return(affine_mtx) } diff --git a/man/adjustGiottoMatrix.Rd b/man/adjustGiottoMatrix.Rd index f14bab1f1..c7a5be9a0 100644 --- a/man/adjustGiottoMatrix.Rd +++ b/man/adjustGiottoMatrix.Rd @@ -11,8 +11,9 @@ adjustGiottoMatrix( expression_values = c("normalized", "scaled", "custom"), batch_columns = NULL, covariate_columns = NULL, + name = "custom", return_gobject = TRUE, - update_slot = c("custom") + update_slot = deprecated() ) } \arguments{ @@ -30,9 +31,12 @@ batch (max = 2)} \item{covariate_columns}{metadata columns that represent covariates to regress out} +\item{name}{character. Name to assign to adjusted matrix +(default = "custom")} + \item{return_gobject}{boolean: return giotto object (default = TRUE)} -\item{update_slot}{expression slot that will be updated (default = custom)} +\item{update_slot}{deprecated.} } \value{ giotto object or exprObj diff --git a/man/calculateAffineMatrixFromLandmarks.Rd b/man/calculateAffineMatrixFromLandmarks.Rd index 268b6a0d1..7022b6aa1 100644 --- a/man/calculateAffineMatrixFromLandmarks.Rd +++ b/man/calculateAffineMatrixFromLandmarks.Rd @@ -2,21 +2,43 @@ % Please edit documentation in R/image_registration.R \name{calculateAffineMatrixFromLandmarks} \alias{calculateAffineMatrixFromLandmarks} -\title{Calculate a affine transformation matrix from two set of landmarks} +\title{Calculate an affine transformation matrix from two sets of landmarks} \usage{ -calculateAffineMatrixFromLandmarks(source_df, target_df) +calculateAffineMatrixFromLandmarks(source, target) } \arguments{ -\item{source_df}{source landmarks, two columns, first column represent -x coordinate and second column represent y coordinate.} - -\item{target_df}{target landmarks, two columns, first column represent -x coordinate and second column represent y coordinate.} +\item{source, target}{\code{matrix} or \code{data.frame}-like. Source and target +landmarks provided as paired x (first) y (second) columns.} } \value{ -a 3 by 3 matrix with the third row close to (0,0,1) +a 3x3 \code{matrix} that defines an affine transform } \description{ -calculate a affine transformation matrix from two set of -landmarks +Calculate an affine transformation matrix from two sets of +landmarks. Source is the item that is transformed to align to target. The +The generated affine transformation matrix is the affine transform that +defines this transformation. +} +\examples{ +m1 <- matrix(c( + 4211.476, 4925.052, + 5906.339, 4251.025, + 4830.097, 3342.676, + 5954.410, 3393.603 + ), + 4, 2, byrow = TRUE +) + +m2 <- matrix(c( + 7218.570, -11999.111, + 8647.143, -15008.278, + 10150.203, -12953.273, + 10172.141, -15008.059 + ), + 4, 2, byrow = TRUE +) +calculateAffineMatrixFromLandmarks(m1, m2) +} +\seealso{ +\code{\link[=interactiveLandmarkSelection]{interactiveLandmarkSelection()}} \code{\link[=affine]{affine()}} } diff --git a/man/interactiveLandmarkSelection.Rd b/man/interactiveLandmarkSelection.Rd index 416e4f28a..5b9561bd6 100644 --- a/man/interactiveLandmarkSelection.Rd +++ b/man/interactiveLandmarkSelection.Rd @@ -2,22 +2,26 @@ % Please edit documentation in R/image_registration.R \name{interactiveLandmarkSelection} \alias{interactiveLandmarkSelection} -\title{title Record landmarks by interactive selection} +\title{Record landmarks by interactive selection} \usage{ interactiveLandmarkSelection(source, target) } \arguments{ -\item{source_image}{the image to be plotted on the left, and landmarks will +\item{source}{the image to be plotted on the left, and landmarks will output in the first of the list. Input can be a ggplot object, -a GiottoImage, or a character represent a path to a image} +a \code{giottoLargeImage}-inheriting object, or a character filepath to an image} -\item{target_image}{the image to be plotted on the right, and landmarks will -output in the second of the list. Input can be a ggplot object, a -GiottoImage, or a character represent a path to a image} +\item{target}{the image to be plotted on the right, and landmarks will +output in the second of the list. Input can be a ggplot object, +a \code{giottoLargeImage}-inheriting object, or a character filepath to an image} } \value{ -a list of landmarks +a list of 2 \code{data.frames} with columns x and y, designating paired +sets of landmarks between list 1 (from source) and list 2 (from target) } \description{ -Record landmarks by interactive selection +Interactively select manual paired landmarks. +} +\seealso{ +\code{\link[=calculateAffineMatrixFromLandmarks]{calculateAffineMatrixFromLandmarks()}} } From 9e363f74f715ab4de7dc7dc26e1b241eee234447 Mon Sep 17 00:00:00 2001 From: josschavezf Date: Mon, 6 Jan 2025 14:36:14 -0500 Subject: [PATCH 17/21] fix license badge --- README.Rmd | 2 +- README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.Rmd b/README.Rmd index 7a649574f..a2a376ff1 100644 --- a/README.Rmd +++ b/README.Rmd @@ -16,7 +16,7 @@ knitr::opts_chunk$set( # Giotto Suite: an open-source and technology-agnostic spatial multi-omics analysis platform -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://tlo.mit.edu/understand-ip/exploring-mit-open-source-license-comprehensive-guide) ![Last Commit](https://badgen.net/github/last-commit/drieslab/Giotto/suite) ![Commits Since Latest](https://img.shields.io/github/commits-since/drieslab/Giotto/latest/suite) [![R-CMD-check](https://github.com/drieslab/Giotto/actions/workflows/main_check.yaml/badge.svg?branch=suite)](https://github.com/drieslab/Giotto/actions/workflows/main_check.yaml) diff --git a/README.md b/README.md index f9dc9cadf..8cf7670d1 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ -[![License: GPL -v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://tlo.mit.edu/understand-ip/exploring-mit-open-source-license-comprehensive-guide) ![Last Commit](https://badgen.net/github/last-commit/drieslab/Giotto/suite) ![Commits Since From 85136a2378fd9b63efbd518e67f66ba7b95687e9 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:31:24 -0500 Subject: [PATCH 18/21] fix: spdep listw conversion --- NEWS.md | 4 ++++ R/spdep.R | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 1cb8c8015..18e6ed1ba 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,9 @@ ## Breaking Changes * Param naming changes for segmentation wrapper functions `doMesmerSegmentation()`, `doCellposeSegmentation()` `doStardistSegmentation()` +## Bug fixes +* `callSpdep()` should also automatically convert *Matrix* classes to `listw` + ## Enhancements * new `stats` param in `addStatistics()` to control which statistics are calculated. * `"area"` calculation added as an `addStatistics()` `stats` selection @@ -12,6 +15,7 @@ * GiottoUtils req raised to 0.2.3 * `adjustGiottoMatrix()` `update_slot` param deprecated in favor of `name`. + # Giotto 4.1.6 (2024/12/09) ## Bug fixes diff --git a/R/spdep.R b/R/spdep.R index 84b1b9ed9..55dab8cc9 100644 --- a/R/spdep.R +++ b/R/spdep.R @@ -175,7 +175,7 @@ callSpdep <- function(method, ...) { listw_arg <- methodparam$listw # Check if listw_arg is a matrix - if (is.matrix(listw_arg)) { + if (inherits(listw_arg, c("matrix", "Matrix"))) { # Convert the matrix to a listw object listw_arg <- spdep::mat2listw(listw_arg, style = "W") } else if (!inherits(listw_arg, "listw")) { From 6053d5888a74f0dfe02397b5a845488a41d1310b Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:51:47 -0500 Subject: [PATCH 19/21] fix: obey use_parallel flag --- R/variable_genes.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/variable_genes.R b/R/variable_genes.R index a29ae5247..3e832cf1d 100644 --- a/R/variable_genes.R +++ b/R/variable_genes.R @@ -108,7 +108,7 @@ # NSE vars var <- selected <- NULL - if (isTRUE(use_parallel)) { + if (!isTRUE(use_parallel)) { test <- apply(X = scaled_matrix, MARGIN = 1, FUN = function(x) var(x)) } else { test <- future.apply::future_apply( From bd8b9babcb2d23671ff2271b1b3da1b5ebe505a7 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:52:45 -0500 Subject: [PATCH 20/21] update: cosmx convenience function --- DESCRIPTION | 2 +- NEWS.md | 4 +- R/convenience_cosmx.R | 1095 ++++------------- man/createGiottoCosMxObject.Rd | 140 ++- man/dot-createGiottoCosMxObject_aggregate.Rd | 29 - man/dot-createGiottoCosMxObject_all.Rd | 59 - ...dot-createGiottoCosMxObject_subcellular.Rd | 42 - man/dot-load_cosmx_folder_aggregate.Rd | 21 - man/dot-load_cosmx_folder_subcellular.Rd | 25 - man/dot-read_cosmx_folder.Rd | 24 - man/importCosMx.Rd | 4 +- 11 files changed, 332 insertions(+), 1113 deletions(-) delete mode 100644 man/dot-createGiottoCosMxObject_aggregate.Rd delete mode 100644 man/dot-createGiottoCosMxObject_all.Rd delete mode 100644 man/dot-createGiottoCosMxObject_subcellular.Rd delete mode 100644 man/dot-load_cosmx_folder_aggregate.Rd delete mode 100644 man/dot-load_cosmx_folder_subcellular.Rd delete mode 100644 man/dot-read_cosmx_folder.Rd diff --git a/DESCRIPTION b/DESCRIPTION index bb88cb70a..be11769fc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: Giotto Title: Spatial Single-Cell Transcriptomics Toolbox -Version: 4.1.7 +Version: 4.2.0 Authors@R: c( person("Ruben", "Dries", email = "rubendries@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7650-7754")), diff --git a/NEWS.md b/NEWS.md index 18e6ed1ba..fea0eb026 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,9 +1,11 @@ -# Giotto 4.1.7 +# Giotto 4.2.0 (2024/01/17) ## Breaking Changes +* Large changes to `createGiottoCosMxObject()` that better reflect NanoString provided outputs. * Param naming changes for segmentation wrapper functions `doMesmerSegmentation()`, `doCellposeSegmentation()` `doStardistSegmentation()` ## Bug fixes +* fix `importCosMx()` fov shifts file detection * `callSpdep()` should also automatically convert *Matrix* classes to `listw` ## Enhancements diff --git a/R/convenience_cosmx.R b/R/convenience_cosmx.R index 01f0dd22c..fba38d090 100644 --- a/R/convenience_cosmx.R +++ b/R/convenience_cosmx.R @@ -5,17 +5,19 @@ setClass( "CosmxReader", slots = list( cosmx_dir = "character", + version = "character", slide = "numeric", fovs = "numeric", micron = "logical", - px2mm = "numeric", + px2um = "numeric", offsets = "ANY", calls = "list" ), prototype = list( + version = "default", slide = 1, micron = FALSE, - px2mm = 0.12028, # from cosmx output help files + px2um = 0.12028, # from cosmx output help files offsets = NULL, calls = list() ) @@ -24,7 +26,8 @@ setClass( # * show #### setMethod("show", signature("CosmxReader"), function(object) { cat(sprintf("Giotto <%s>\n", "CosmxReader")) - print_slots <- c("dir", "slide", "fovs", "micron", "offsets", "funs") + print_slots <- c("version", "dir", "slide", "fovs", + "micron", "offsets", "funs") pre <- sprintf( "%s :", format(print_slots) ) @@ -39,6 +42,10 @@ setMethod("show", signature("CosmxReader"), function(object) { } else { cat(pre["dir"], "\n") } + + # version + v <- object@version + cat(pre["version"], v, "\n") # slide slide <- object@slide @@ -49,7 +56,7 @@ setMethod("show", signature("CosmxReader"), function(object) { cat(pre["fovs"], paste(fovs, collapse = ", "), "\n") # micron scaling - micron <- ifelse(object@micron, object@px2mm / 1000, FALSE) + micron <- ifelse(object@micron, object@px2um, FALSE) cat(pre["micron"], micron, "\n") # offsets @@ -70,6 +77,12 @@ setMethod( a <- list(...) dat <- x@offsets + if (x@micron) { + px2um <- x@px2um + dat$x <- dat$x * px2um + dat$y <- dat$y * px2um + } + if (is.null(dat)) { # don't run if no offsets cat("no offsets to plot\n") return(invisible(NULL)) @@ -100,7 +113,7 @@ setMethod( #' Otherwise, all FOVs will be loaded #' @param micron logical. Whether to scale spatial information as micron #' instead of the default pixels -#' @param px2mm numeric. Scalefactor from pixels to mm. Defaults to 0.12028 +#' @param px2um numeric. Scalefactor from pixels to micron. Defaults to 0.12028 #' based on `CosMx-ReadMe.html` info #' @details #' Loading functions are generated after the `cosmx_dir` is added. @@ -134,7 +147,7 @@ setMethod( #' } #' @export importCosMx <- function(cosmx_dir = NULL, slide = 1, fovs = NULL, - micron = FALSE, px2mm = 0.12028) { + micron = FALSE, px2um = 0.12028) { # get params a <- list(Class = "CosmxReader") if (!is.null(cosmx_dir)) { @@ -145,7 +158,7 @@ importCosMx <- function(cosmx_dir = NULL, slide = 1, fovs = NULL, } a$slide <- slide a$micron <- micron - a$px2mm <- px2mm + a$px2um <- px2um do.call(new, args = a) } @@ -153,12 +166,18 @@ importCosMx <- function(cosmx_dir = NULL, slide = 1, fovs = NULL, # * init #### setMethod( "initialize", signature("CosmxReader"), - function(.Object, cosmx_dir, slide, fovs, micron, px2mm) { + function(.Object, cosmx_dir, version, slide, fovs, micron, px2um) { # provided params (if any) if (!missing(cosmx_dir)) { checkmate::assert_directory_exists(cosmx_dir) .Object@cosmx_dir <- cosmx_dir } + if (!missing(version)) { + version <- match.arg(tolower(version), + c("default", "v6", "legacy") + ) + .Object@version <- version + } if (!missing(slide)) { .Object@slide <- slide } @@ -168,8 +187,8 @@ setMethod( if (!missing(micron)) { .Object@micron <- micron } - if (!missing(px2mm)) { - .Object@px2mm <- px2mm + if (!missing(px2um)) { + .Object@px2um <- px2um } # NULL case @@ -184,6 +203,9 @@ setMethod( .detect_in_dir(pattern = pattern, path = p, platform = "CosMx") } + v <- .Object@version + if (v == "default") v <- "v6" + shifts_path <- .cosmx_detect("fov_positions_file") meta_path <- .cosmx_detect("metadata_file") tx_path <- .cosmx_detect("tx_file") @@ -199,12 +221,8 @@ setMethod( pos <- NULL if (!is.null(shifts_path)) { - fov_shifts <- data.table::fread(shifts_path) - if (!"X_mm" %in% colnames(fov_shifts)) { - # older version has fov, x, y (all numeric) in px shifts - data.table::setnames(fov_shifts, new = c("fov", "x", "y")) - pos <- fov_shifts - } + pos <- try(.cosmx_fov_shift(shifts_path), silent = TRUE) + if (inherits(pos, "try-error")) pos <- NULL } # proceed with other possible methods of inferring shifts if present @@ -222,7 +240,7 @@ setMethod( tx_dt = data.table::fread(tx_path), flip_loc_y = TRUE ) - } else { + } else if (is.null(pos)) { pos <- data.table::data.table() warning(wrap_txt( "NO FOV SHIFTS. @@ -240,16 +258,16 @@ setMethod( # transcripts load call + dcols <- switch(v, + "v6" = c("x_local_px", "y_local_px", "cell_ID", "cell"), + "legacy" = c("x_local_px", "y_local_px", "cell_ID") + ) tx_fun <- function( path = tx_path, feat_type = c("rna", "negprobes"), split_keyword = list("NegPrb"), - dropcols = c( - "x_local_px", - "y_local_px", - "cell_ID", - "cell" - ), + dropcols = dcols, + cores = determine_cores(), verbose = NULL) { .cosmx_transcript( path = path, @@ -258,8 +276,8 @@ setMethod( split_keyword = split_keyword, dropcols = dropcols, micron = .Object@micron, - px2mm = .Object@px2mm, - cores = determine_cores(), + px2um = .Object@px2um, + cores = cores, verbose = verbose ) } @@ -268,12 +286,17 @@ setMethod( # mask load call + vstep <- switch(v, + "v6" = FALSE, + "legacy" = 1 + ) + mask_fun <- function( path = mask_dir, # VERTICAL FLIP + NO VERTICAL SHIFT flip_vertical = TRUE, flip_horizontal = FALSE, - shift_vertical_step = FALSE, + shift_vertical_step = vstep, shift_horizontal_step = FALSE, remove_background_polygon = TRUE, verbose = NULL) { @@ -286,7 +309,7 @@ setMethod( shift_horizontal_step = shift_horizontal_step, remove_background_polygon = remove_background_polygon, micron = .Object@micron, - px2mm = .Object@px2mm, + px2um = .Object@px2um, offsets = .Object@offsets, verbose = verbose ) @@ -310,11 +333,16 @@ setMethod( # images load call + negy <- switch(v, + "v6" = TRUE, + "legacy" = FALSE, + ) + img_fun <- function( path = composite_img_dir, img_type = "composite", img_name_fmt = paste0(img_type, "_fov%03d"), - negative_y = TRUE, + negative_y = negy, flip_vertical = FALSE, flip_horizontal = FALSE, verbose = NULL) { @@ -327,7 +355,7 @@ setMethod( flip_vertical = flip_vertical, flip_horizontal = flip_horizontal, micron = .Object@micron, - px2mm = .Object@px2mm, + px2um = .Object@px2um, offsets = .Object@offsets, verbose = verbose ) @@ -338,6 +366,7 @@ setMethod( # meta load call meta_fun <- function( path = meta_path, + cores = determine_cores(), dropcols = c( "CenterX_local_px", "CenterY_local_px", @@ -350,7 +379,7 @@ setMethod( path = path, fovs = .Object@fovs %none% NULL, dropcols = dropcols, - cores = determine_cores(), + cores = cores, verbose = verbose ) } @@ -372,8 +401,10 @@ setMethod( overlay = "overlay" ), load_expression = FALSE, - load_cellmeta = FALSE, - instructions = NULL) { + load_cellmeta = TRUE, + instructions = NULL, + cores = determine_cores(), + verbose = NULL) { load_expression <- as.logical(load_expression) load_cellmeta <- as.logical(load_cellmeta) @@ -397,7 +428,9 @@ setMethod( tx_list <- funs$load_transcripts( path = transcript_path, feat_type = feat_type, - split_keyword = split_keyword + split_keyword = split_keyword, + cores = cores, + verbose = verbose ) for (tx in tx_list) { g <- setGiotto(g, tx) @@ -408,7 +441,7 @@ setMethod( path = cell_labels_dir, verbose = FALSE ) - g <- setGiotto(g, polys) + g <- setGiotto(g, polys, verbose = verbose) # images if (!is.null(load_images)) { @@ -422,10 +455,12 @@ setMethod( dir_imgs <- funs$load_images( path = load_images[[imdir_i]], img_type = dirnames[[imdir_i]], + verbose = verbose ) imglist <- c(imglist, dir_imgs) } - g <- addGiottoLargeImage(g, largeImages = imglist) + g <- addGiottoLargeImage(g, largeImages = imglist, + verbose = FALSE) } # expression & meta @@ -443,18 +478,26 @@ setMethod( for (ex in exlist) { bool <- colnames(ex[]) %in% allowed_ids ex[] <- ex[][, bool] - g <- setGiotto(g, ex) + g <- setGiotto(g, ex, verbose = verbose) } } if (load_cellmeta) { cx <- funs$load_cellmeta( - path = metadata_path + path = metadata_path, + cores = cores, + verbose = verbose ) cx[] <- cx[][cell_ID %in% allowed_ids, ] - g <- setGiotto(g, cx) + g <- setGiotto(g, cx, verbose = verbose) } + + # create spatlocs + g <- addSpatialCentroidLocations(g, verbose = FALSE) + + # add fovs + g$fov <- gsub("^c_\\d+_(\\d+)_\\d+$", "\\1", pDataDT(g)$cell_ID) return(g) } @@ -472,7 +515,8 @@ setMethod( #' @export setMethod("$", signature("CosmxReader"), function(x, name) { - basic_info <- c("cosmx_dir", "slide", "fovs", "micron", "px2mm", "offsets") + basic_info <- c("cosmx_dir", "version", "slide", "fovs", + "micron", "px2um", "offsets") if (name %in% basic_info) { return(methods::slot(x, name)) } @@ -482,14 +526,16 @@ setMethod("$", signature("CosmxReader"), function(x, name) { #' @export setMethod("$<-", signature("CosmxReader"), function(x, name, value) { - basic_info <- c("cosmx_dir", "slide", "fovs", "micron", "px2mm") + basic_info <- c("cosmx_dir", "version", "slide", "fovs", "micron", "px2um") if (name %in% basic_info) { methods::slot(x, name) <- value return(initialize(x)) } if (name == "offsets") { - methods::slot(x, name) <- data.table::setDT(value) + value <- data.table::setDT(value) + data.table::setnames(value, new = c("fov", "x", "y")) + methods::slot(x, name) <- value return(initialize(x)) } @@ -501,7 +547,8 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { #' @export `.DollarNames.CosmxReader` <- function(x, pattern) { - dn <- c("cosmx_dir", "slide", "fovs", "micron", "px2mm", "offsets") + dn <- c("cosmx_dir", "version", "slide", "fovs", + "micron", "px2um", "offsets") if (length(methods::slot(x, "calls")) > 0) { dn <- c(dn, paste0(names(methods::slot(x, "calls")), "()")) } @@ -514,6 +561,29 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { # MODULAR #### +.cosmx_fov_shift <- function(path) { + if (is.null(path)) return(NULL) # return early (empty case) + fov_shifts <- data.table::fread(path) + fs_colnames <- colnames(fov_shifts) + if (!"X_mm" %in% fs_colnames) return(NULL) # return early (WTX) + + offset_colnames <- c("fov", "x", "y") + + if (identical(fs_colnames, + c("fov", "x_global_px", "y_global_px") + )) { + data.table::setnames(fov_shifts, new = offset_colnames) + } else if (identical(fs_colnames, + c("FOV", "x_global_px", "y_global_px", "x_global_mm", "y_global_mm") + )) { + fov_shifts <- fov_shifts[, c("FOV", "x_global_px", "y_global_px")] + data.table::setnames(fov_shifts, new = offset_colnames) + } else { + return(NULL) # unknown read method + } + fov_shifts +} + .cosmx_transcript <- function( path, fovs = NULL, @@ -526,7 +596,7 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { "cell" ), micron = FALSE, - px2mm = 0.12028, + px2um = 0.12028, cores = determine_cores(), verbose = NULL) { if (missing(path)) { @@ -548,9 +618,8 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { # micron scaling if desired if (micron) { - px2micron <- px2mm / 1000 - tx[, x_global_px := x_global_px * px2micron] - tx[, y_global_px := y_global_px * px2micron] + tx[, x_global_px := x_global_px * px2um] + tx[, y_global_px := y_global_px * px2um] } # giottoPoints ----------------------------------------------------- # @@ -562,6 +631,7 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { gpoints_params$x_colname <- "x_global_px" gpoints_params$y_colname <- "y_global_px" gpoints_params$feat_ID_colname <- "target" + gpoints_params$verbose = FALSE gpoints <- do.call(createGiottoPoints, c(list(x = tx), gpoints_params)) # ensure output is always a list @@ -683,7 +753,7 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { shift_horizontal_step = FALSE, remove_background_polygon = TRUE, micron = FALSE, - px2mm = 0.12028, + px2um = 0.12028, offsets, verbose = NULL) { # NSE params @@ -738,13 +808,12 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { # if micron scale if (micron) { - px2micron <- px2mm / 1000 gpoly <- rescale( gpoly, - fx = px2micron, fy = px2micron, x0 = 0, y0 = 0 + fx = px2um, fy = px2um, x0 = 0, y0 = 0 ) - xshift <- xshift * px2micron - yshift <- yshift * px2micron + xshift <- xshift * px2um + yshift <- yshift * px2um } gpoly <- spatShift(x = gpoly, dx = xshift, dy = yshift) @@ -898,11 +967,11 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { fovs = NULL, img_type = "composite", img_name_fmt = paste(img_type, "_fov%03d"), - negative_y = TRUE, + negative_y = FALSE, flip_vertical = FALSE, flip_horizontal = FALSE, micron = FALSE, - px2mm = 0.12028, + px2um = 0.12028, offsets, verbose = NULL) { if (missing(path)) { @@ -937,13 +1006,12 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { yshift <- offsets[fov == f, y] if (micron) { - px2micron <- px2mm / 1000 gimg <- rescale( gimg, - fx = px2micron, fy = px2micron, x0 = 0, y0 = 0 + fx = px2um, fy = px2um, x0 = 0, y0 = 0 ) - xshift <- xshift * px2micron - yshift <- yshift * px2micron + xshift <- xshift * px2um + yshift <- yshift * px2um } gimg <- spatShift(x = gimg, dx = xshift, dy = yshift) @@ -958,200 +1026,13 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { -#' @title Load CosMx folder subcellular info -#' @name .load_cosmx_folder_subcellular -#' @description loads in the feature detections information. Note that the mask -#' images are still required for a working subcellular object, and those are -#' loaded in \code{\link{.createGiottoCosMxObject_subcellular}} -#' @inheritParams createGiottoCosMxObject -#' @returns list -#' @keywords internal -.load_cosmx_folder_subcellular <- function( - dir_items, - FOVs = NULL, - cores, - verbose = TRUE) { - vmsg(.v = verbose, "Loading subcellular information...") - - # subcellular checks - if (!file.exists(dir_items$`transcript locations file`)) { - stop(wrap_txt("No transcript locations file (.csv) detected")) - } - if (!file.exists(dir_items$`fov positions file`)) { - stop(wrap_txt("No fov positions file (.csv) detected")) - } - - # FOVs to load - vmsg(.v = verbose, "Loading FOV offsets...") - fov_offset_file <- fread( - input = dir_items$`fov positions file`, nThread = cores - ) - if (is.null(FOVs)) FOVs <- fov_offset_file$fov # default to ALL FOVs - FOV_ID <- as.list(sprintf("%03d", FOVs)) - - # TODO Load only relevant portions of file? - - vmsg(.v = verbose, "Loading transcript level info...") - tx_coord_all <- fread( - input = dir_items$`transcript locations file`, nThread = cores - ) - vmsg(.v = verbose, "Subcellular load done") - - data_list <- list( - "FOV_ID" = FOV_ID, - "fov_offset_file" = fov_offset_file, - "tx_coord_all" = tx_coord_all - ) - - return(data_list) -} - - - -#' @title Load CosMx folder aggregate info -#' @name .load_cosmx_folder_aggregate -#' @inheritParams createGiottoCosMxObject -#' @returns list -#' @keywords internal -.load_cosmx_folder_aggregate <- function( - dir_items, - cores, - verbose = TRUE) { - # data.table vars - fov <- cell_ID <- fov_cell_ID <- CenterX_global_px <- - CenterY_global_px <- CenterX_local_px <- - CenterY_local_px <- x_shift <- y_shift <- NULL - - # load aggregate information - vmsg(.v = verbose, "Loading provided aggregated information...") - - # aggregate checks - if (!file.exists(dir_items$`expression matrix file`)) { - stop(wrap_txt("No expression matrix file (.csv) detected")) - } - if (!file.exists(dir_items$`metadata file`)) { - stop(wrap_txt("No metadata file (.csv) detected. Needed for cell - spatial locations.")) - } - - # read in aggregate data - expr_mat <- fread( - input = dir_items$`expression matrix file`, nThread = cores - ) - metadata <- fread(input = dir_items$`metadata file`, nThread = cores) - - # setorder expression and spatlocs - data.table::setorder(metadata, fov, cell_ID) - data.table::setorder(expr_mat, fov, cell_ID) - - - # generate unique cell IDs - expr_mat[, cell_ID := paste0( - "fov", sprintf("%03d", fov), "-", "cell_", cell_ID - )] - expr_mat <- expr_mat[, fov := NULL] - - metadata[, fov_cell_ID := cell_ID] - metadata[, cell_ID := paste0( - "fov", sprintf("%03d", fov), "-", "cell_", cell_ID - )] - # reorder - data.table::setcolorder(x = metadata, c("cell_ID", "fov", "fov_cell_ID")) - - - # extract spatial locations - spatlocs <- metadata[, .(CenterX_global_px, CenterY_global_px, cell_ID)] - spatlocs_fov <- metadata[, .(CenterX_local_px, CenterY_local_px, cell_ID)] - # regenerate FOV shifts - metadata[, x_shift := CenterX_global_px - CenterX_local_px] - metadata[, y_shift := CenterY_global_px - CenterY_local_px] - fov_shifts <- metadata[, .(mean(x_shift), mean(y_shift)), fov] - colnames(fov_shifts) <- c("fov", "x_shift", "y_shift") - - - # rename spatloc column names - spatloc_oldnames <- c("CenterX_global_px", "CenterY_global_px", "cell_ID") - spatloc_oldnames_fov <- c("CenterX_local_px", "CenterY_local_px", "cell_ID") - spatloc_newnames <- c("sdimx", "sdimy", "cell_ID") - data.table::setnames( - spatlocs, - old = spatloc_oldnames, new = spatloc_newnames - ) - data.table::setnames( - spatlocs_fov, - old = spatloc_oldnames_fov, new = spatloc_newnames - ) - - # cleanup metadata and spatlocs - metadata <- metadata[, c( - "CenterX_global_px", "CenterY_global_px", - "CenterX_local_px", "CenterY_local_px" - ) := NULL] - # find unique cell_IDs present in both expression and metadata - giotto_cell_ID <- unique(intersect(expr_mat$cell_ID, metadata$cell_ID)) - - # subset to only unique cell_IDs - expr_mat <- expr_mat[cell_ID %in% giotto_cell_ID, ] - metadata <- metadata[cell_ID %in% giotto_cell_ID, ] - - - # convert protein metadata to expr mat - # take all mean intensity protein information except for - # MembraneStain and DAPI - protein_meta_cols <- colnames(metadata) - protein_meta_cols <- protein_meta_cols[ - grepl(pattern = "Mean.*", x = protein_meta_cols) - ] - protein_meta_cols <- protein_meta_cols[ - !protein_meta_cols %in% c("Mean.MembraneStain", "Mean.DAPI") - ] - protein_meta_cols <- c("cell_ID", protein_meta_cols) - - prot_expr <- metadata[, protein_meta_cols, with = FALSE] - prot_cell_ID <- metadata[, cell_ID] - protM <- Matrix::Matrix(as.matrix(prot_expr[, -1]), - dimnames = list( - prot_expr[[1]], - colnames(prot_expr[, -1]) - ), - sparse = FALSE - ) - protM <- t_flex(protM) - - # convert expression to sparse matrix - spM <- Matrix::Matrix(as.matrix(expr_mat[, -1]), - dimnames = list( - expr_mat[[1]], - colnames(expr_mat[, -1]) - ), - sparse = TRUE - ) - spM <- t_flex(spM) - - ## Ready for downstream aggregate gobject creation or appending into - # existing subcellular Giotto object ## - - data_list <- list( - "spatlocs" = spatlocs, - "spatlocs_fov" = spatlocs_fov, - "metadata" = metadata, - "protM" = protM, - "spM" = spM, - "fov_shifts" = fov_shifts - ) - - return(data_list) -} - - - -# OLD #### +# wrapper #### #' @title Create Nanostring CosMx Giotto Object @@ -1159,19 +1040,40 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { #' @description Given the path to a CosMx experiment directory, creates a Giotto #' object. #' @param cosmx_dir full path to the exported cosmx directory -#' @param data_to_use which type(s) of expression data to build the gobject with -#' Default is \code{'all'} information available. \code{'subcellular'} loads -#' the transcript coordinates only. \code{'aggregate'} loads the provided -#' aggregated expression matrix. +#' @param version character. Version of CosMx output. Current selections are +#' either "default", "v6", and "legacy" (for the NSCLC dataset). #' @param FOVs field of views to load (only affects subcellular data and images) -#' @param remove_background_polygon try to remove background polygon -#' (default: FALSE) -#' @param background_algo algorithm to remove background polygon -#' @param remove_unvalid_polygons remove unvalid polygons (default: TRUE) +#' @param feat_type character. feature type. Provide more than one value if +#' using the `split_keyword` param. For each set of keywords to split by, an +#' additional feat_type should be provided in the same order. Affects how +#' the transcripts information is loaded. Helpful for separating out the +#' QC probes. See details. +#' @param split_keyword list of character vectors of keywords to split the +#' transcripts based on their feat_ID. Keywords will be `grepl()` +#' matched against the feature IDs information. See details. +#' @param load_images named list of filepaths to directories. Loads the +#' composite and overlay images by default. +#' @param load_expression logical. (Default = FALSE) whether to load provided +#' expression matrix +#' @param load_cellmeta logical. (Default = TRUE) whether to load provided +#' cell metadata +#' @param fov_shifts_path Optional. Filepath to fov_positions_file +#' @param transcript_path Optional. Filepath to desired transcripts file to +#' load. +#' @param cell_labels_dir Optional. Path to directory containing CellLabels +#' images to load as polygons. +#' @param expression_path Optional. Filepath to cell feature matrix to load. +#' @param metadata_path Optional. Filepath to metadata file to load. +#' @param cores nthreads to use when loading in cell metadata and transcripts +#' @param data_to_use deprecated. Not used +#' @param remove_background_polygon deprecated. Now always done +#' @param background_algo deprecated. Not used +#' @param remove_unvalid_polygons deprecated. Now always done #' @inheritParams GiottoClass::createGiottoObjectSubcellular +#' @inheritDotParams importCosMx -cosmx_dir -fovs #' @returns a giotto object #' @details -#' [\strong{Expected Directory}] This function generates a giotto object when +#' \[**Expected Directory**\] This function generates a giotto object when #' given a link to a cosmx output directory. It expects the following items #' within the directory where the \strong{bolded} portions are what this #' function matches against: @@ -1186,633 +1088,104 @@ setMethod("$<-", signature("CosmxReader"), function(x, name, value) { #' \item{experimentname_\strong{tx_file}.csv (file)} #' } #' -#' [\strong{Workflows}] Workflow to use is accessed through the data_to_use -#' param -#' \itemize{ -#' \item{'all' - loads and requires subcellular information from tx_file and -#' fov_positions_file -#' and also the existing aggregated information -#' (expression, spatial locations, and metadata) -#' from exprMat_file and metadata_file.} -#' \item{'subcellular' - loads and requires subcellular information from -#' tx_file and -#' fov_positions_file only.} -#' \item{'aggregate' - loads and requires the existing aggregate information -#' (expression, spatial locations, and metadata) from exprMat_file and -#' metadata_file.} -#' } +#' \[**feat_type and split_keyword**\] +#' Additional QC probe information is in the subcellular feature detections +#' information and must be separated from the gene expression information +#' during processing. +#' The QC probes have prefixes that allow them to be selected from the rest of +#' the feature IDs. +#' Giotto uses `feat_type` and `split_keyword` params to select these QC +#' probes out as separate feature types. See examples in +#' `[GiottoClass::createGiottoPoints]` for how this works. +#' +#' The Gene expression subset labeled as `rna` is accepted as the subset of +#' feat_IDs that do not get matched to any of the `split_keywords`. #' -#' [\strong{Images}] Images in the default CellComposite, CellLabels, -#' CompartmentLabels, and CellOverlay -#' folders will be loaded as giotto largeImage objects in all workflows as -#' long as they are available. Additionally, CellComposite images will be -#' converted to giotto image objects, making plotting with -#' these image objects more responsive when accessing them from a server. +#' \[**Images**\] Images in the expected CellComposite and CellOverlay +#' folders will be loaded as giotto largeImage objects by default. #' \code{\link{showGiottoImageNames}} can be used to see the available images. +#' @md #' @export createGiottoCosMxObject <- function( - cosmx_dir = NULL, - data_to_use = c("all", "subcellular", "aggregate"), - remove_background_polygon = TRUE, - background_algo = c("range"), - remove_unvalid_polygons = TRUE, + cosmx_dir, + version = "default", FOVs = NULL, - instructions = NULL, + feat_type = c("rna", "negprobes"), + split_keyword = list("NegPrb"), + load_images = list(composite = "composite", + overlay = "overlay"), + load_expression = FALSE, + load_cellmeta = FALSE, + + # optional filepaths + fov_shifts_path = NULL, + transcript_path = NULL, + cell_labels_dir = NULL, + expression_path = NULL, + metadata_path = NULL, cores = determine_cores(), - verbose = TRUE) { - # 0. setup - cosmx_dir <- path.expand(cosmx_dir) + verbose = NULL, + instructions = NULL, + + # deprecated params + remove_unvalid_polygons = deprecated(), # not used + data_to_use = deprecated(), # not used + remove_background_polygon = deprecated(), # not used + background_algo = deprecated(), # not used + ...) { - # determine data to use - data_to_use <- match.arg( - arg = data_to_use, choices = c("all", "subcellular", "aggregate") - ) - if (data_to_use %in% c("all", "aggregate")) { - stop(wrap_txt('Convenience workflows "all" and "aggregate" are not - available yet')) + # handle deprecations + if (!missing(remove_unvalid_polygons)) { + deprecate_warn( + "4.2.0", "createGiottoCosMxObject(remove_unvalid_polygons)") } - - # Define for data.table - fov <- target <- x_local_px <- y_local_px <- z <- cell_ID <- - CenterX_global_px <- CenterY_global_px <- - CenterX_local_px <- CenterY_local_px <- NULL - - - # 1. test if folder structure exists and is as expected - dir_items <- .read_cosmx_folder( - cosmx_dir = cosmx_dir, - verbose = verbose - ) - - - # 2. load and create giotto object - cosmx_gobject <- switch(data_to_use, - "subcellular" = .createGiottoCosMxObject_subcellular( - dir_items, - FOVs = FOVs, - remove_background_polygon = remove_background_polygon, - background_algo = background_algo, - remove_unvalid_polygons = remove_unvalid_polygons, - cores = cores, - verbose = verbose, - instructions = instructions - ), - "aggregate" = .createGiottoCosMxObject_aggregate( - dir_items, - cores = cores, - verbose = verbose, - instructions = instructions - ), - "all" = .createGiottoCosMxObject_all( - dir_items, - FOVs = FOVs, - remove_background_polygon = remove_background_polygon, - background_algo = background_algo, - remove_unvalid_polygons = remove_unvalid_polygons, - cores = cores, - verbose = verbose, - instructions = instructions - ) - ) - - - # load in subcellular information, subcellular FOV objects, then join - - - # load in pre-generated aggregated expression matrix - if (data_to_use == "aggregate" | data_to_use == "all") { - + if (!missing(remove_background_polygon)) { + deprecate_warn( + "4.2.0", "createGiottoCosMxObject(remove_background_polygon)") } - - vmsg(.v = verbose, "done") - return(cosmx_gobject) -} - - - -#' @title Load and create a CosMx Giotto object from subcellular info -#' @name .createGiottoCosMxObject_subcellular -#' @inheritParams createGiottoCosMxObject -#' @returns giotto object -#' @keywords internal -.createGiottoCosMxObject_subcellular <- function( - dir_items, - FOVs = NULL, - remove_background_polygon = TRUE, - background_algo = c("range"), - remove_unvalid_polygons = TRUE, - cores, - verbose = TRUE, - instructions = NULL) { - target <- fov <- NULL - - # load tx detections and FOV offsets ------------------------------------- # - data_list <- .load_cosmx_folder_subcellular( - dir_items = dir_items, - FOVs = FOVs, - cores = cores, - verbose = verbose - ) - - # unpack data_list - FOV_ID <- data_list$FOV_ID - fov_offset_file <- data_list$fov_offset_file - tx_coord_all <- data_list$tx_coord_all - - # remove global xy values and cell_ID - tx_coord_all[, c("x_global_px", "y_global_px", "cell_ID") := NULL] - - data.table::setcolorder( - tx_coord_all, c("target", "x_local_px", "y_local_px", "z", "fov") - ) - - # feature detection type splitting --------------------------------------- # - - if (isTRUE(verbose)) message("Splitting detections by feature vs neg probe") - all_IDs <- tx_coord_all[, unique(target)] - neg_IDs <- all_IDs[grepl(pattern = "NegPrb", all_IDs)] - feat_IDs <- all_IDs[!all_IDs %in% neg_IDs] - - # split detections DT - feat_coords_all <- tx_coord_all[target %in% feat_IDs] - neg_coords_all <- tx_coord_all[target %in% neg_IDs] - - if (isTRUE(verbose)) { - message(" > Features: ", feat_coords_all[, .N]) - message(" > NegProbes: ", neg_coords_all[, .N]) + if (!missing(data_to_use)) { + deprecate_warn( + "4.2.0", "createGiottoCosMxObject(data_to_use)") } - - # FOV-based processing --------------------------------------------------- # - - fov_gobjects_list <- lapply(FOV_ID, function(x) { - # images --------------------------------------------------- # - # build image paths - if (isTRUE(verbose)) message("Loading image information...") - - composite_dir <- Sys.glob(paths = file.path( - dir_items$`CellComposite folder`, paste0("*", x, "*") - )) - cellLabel_dir <- Sys.glob(paths = file.path( - dir_items$`CellLabels folder`, paste0("*", x, "*") - )) - compartmentLabel_dir <- Sys.glob(paths = file.path( - dir_items$`CompartmentLabels folder`, paste0("*", x, "*") - )) - cellOverlay_dir <- Sys.glob(paths = file.path( - dir_items$`CellOverlay folder`, paste0("*", x, "*") - )) - - # Missing warnings - if (length(composite_dir) == 0) { - warning("[ FOV ", x, " ] No composite images found") - composite_dir <- NULL - } - if (length(cellLabel_dir) == 0) { - stop("[ FOV ", x, " ] No cell mask images found") - } # cell masks are necessary - if (length(compartmentLabel_dir) == 0) { - warning("[ FOV ", x, " ] No compartment label images found") - compartmentLabel_dir <- NULL - } - if (length(cellOverlay_dir) == 0) { - warning("[ FOV ", x, " ] No cell polygon overlay images found") - cellOverlay_dir <- NULL - } - - if (isTRUE(verbose)) message("Image load done") - - if (isTRUE(verbose)) wrap_msg("[ FOV ", x, "]") - - - # transcripts ---------------------------------------------- # - # get FOV specific tx locations - if (isTRUE(verbose)) message("Assigning FOV feature detections...") - - - # feature info - coord_oldnames <- c("target", "x_local_px", "y_local_px") - coord_newnames <- c("feat_ID", "x", "y") - - feat_coord <- feat_coords_all[fov == as.numeric(x)] - data.table::setnames( - feat_coord, - old = coord_oldnames, new = coord_newnames - ) - # neg probe info - neg_coord <- neg_coords_all[fov == as.numeric(x)] - data.table::setnames( - neg_coord, - old = coord_oldnames, new = coord_newnames - ) - - - # build giotto object -------------------------------------- # - if (isTRUE(verbose)) message("Building subcellular giotto object...") - fov_subset <- createGiottoObjectSubcellular( - gpoints = list( - "rna" = feat_coord, - "neg_probe" = neg_coord - ), - gpolygons = list("cell" = cellLabel_dir), - polygon_mask_list_params = list( - mask_method = "guess", - flip_vertical = TRUE, - flip_horizontal = FALSE, - shift_horizontal_step = FALSE, - remove_background_polygon = remove_background_polygon, - background_algo = background_algo, - remove_unvalid_polygons = remove_unvalid_polygons - ), - instructions = instructions, - cores = cores - ) - - - # find centroids as spatial locations ---------------------- # - if (isTRUE(verbose)) { - message("Finding polygon centroids as cell spatial locations...") - } - fov_subset <- addSpatialCentroidLocations( - fov_subset, - poly_info = "cell", - spat_loc_name = "raw" - ) - - - # create and add giotto image objects ---------------------- # - if (isTRUE(verbose)) { - message("Attaching image files...") - print(composite_dir) - print(cellOverlay_dir) - print(compartmentLabel_dir) - } - - gImage_list <- list() - - # load image if files are found - if (!is.null(composite_dir)) { - gImage_list$composite <- createGiottoLargeImage( - raster_object = composite_dir, - negative_y = FALSE, - name = "composite" - ) - } - if (!is.null(cellOverlay_dir)) { - gImage_list$overlay <- createGiottoLargeImage( - raster_object = cellOverlay_dir, - negative_y = FALSE, - name = "overlay" - ) - } - if (!is.null(compartmentLabel_dir)) { - gImage_list$compartment <- createGiottoLargeImage( - raster_object = compartmentLabel_dir, - negative_y = FALSE, - name = "compartment" - ) - } # TODO - - - - if (length(gImage_list) > 0) { - fov_subset <- addGiottoImage( - gobject = fov_subset, - images = gImage_list - ) - - # convert to MG for faster loading (particularly relevant for - # pulling from server) - # TODO remove this - fov_subset <- convertGiottoLargeImageToMG( - giottoLargeImage = gImage_list$composite, - gobject = fov_subset, - return_gobject = TRUE, - verbose = FALSE - ) - } else { - message("No images found for fov") - } - }) # lapply end - - # returning -------------------------------------------------------------- # - - if (length(FOVs) == 1) { - return(fov_gobjects_list[[1]]) - } else { - # join giotto objects according to FOV positions file - if (isTRUE(verbose)) message("Joining FOV gobjects...") - new_gobj_names <- paste0("fov", FOV_ID) - id_match <- match(as.numeric(FOV_ID), fov_offset_file$fov) - x_shifts <- fov_offset_file[id_match]$x_global_px - y_shifts <- fov_offset_file[id_match]$y_global_px - - # Join giotto objects - cosmx_gobject <- joinGiottoObjects( - gobject_list = fov_gobjects_list, - gobject_names = new_gobj_names, - join_method = "shift", - x_shift = x_shifts, - y_shift = y_shifts - ) - return(cosmx_gobject) + if (!missing(background_algo)) { + deprecate_warn( + "4.2.0", "createGiottoCosMxObject(background_algo)") } -} - - - -#' @title Load and create a CosMx Giotto object from aggregate info -#' @name .createGiottoCosMxObject_aggregate -#' @inheritParams createGiottoCosMxObject -#' @returns giotto object -#' @keywords internal -.createGiottoCosMxObject_aggregate <- function( - dir_items, - cores, - verbose = TRUE, - instructions = NULL) { - data_to_use <- fov <- NULL - - data_list <- .load_cosmx_folder_aggregate( - dir_items = dir_items, - cores = cores, - verbose = verbose - ) - - # unpack data_list - spatlocs <- data_list$spatlocs - spatlocs_fov <- data_list$spatlocs_fov - metadata <- data_list$metadata - protM <- data_list$protM - spM <- data_list$spM - fov_shifts <- data_list$fov_shifts - - - # create standard gobject from aggregate matrix - if (data_to_use == "aggregate") { - # Create aggregate gobject - if (isTRUE(verbose)) message("Building giotto object...") - cosmx_gobject <- createGiottoObject( - expression = list("raw" = spM, "protein" = protM), - cell_metadata = list("cell" = list( - "rna" = metadata, - "protein" = metadata - )), - spatial_locs = spatlocs, - instructions = instructions, - cores = cores - ) - - - # load in images - img_ID <- data.table::data.table( - fov = fov_shifts[, fov], - img_name = paste0( - "fov", - sprintf("%03d", fov_shifts[, fov]), "-image" - ) - ) - - if (isTRUE(verbose)) message("Attaching image files...") - composite_dir <- Sys.glob(paths = file.path( - dir_items$`CellComposite folder`, paste0("/*") - )) - cellLabel_dir <- Sys.glob(paths = file.path( - dir_items$`CellLabels folder`, paste0("/*") - )) - compartmentLabel_dir <- Sys.glob(paths = file.path( - dir_items$`CompartmentLabels folder`, paste0("/*") - )) - overlay_dir <- Sys.glob(paths = file.path( - dir_items$`CellOverlay folder`, paste0("/*") - )) - if (length(cellLabel_imgList) > 0) { - cellLabel_imgList <- lapply(cellLabel_dir, function(x) { - createGiottoLargeImage(x, name = "cellLabel", negative_y = TRUE) - }) - } - if (length(composite_imgList) > 0) { - composite_imgList <- lapply(composite_dir, function(x) { - createGiottoLargeImage(x, name = "composite", negative_y = TRUE) - }) - } - if (length(compartmentLabel_dir) > 0) { - compartmentLabel_imgList <- lapply( - compartmentLabel_dir, function(x) { - createGiottoLargeImage(x, - name = "composite", - negative_y = TRUE - ) - } - ) - } - if (length(overlay_dir) > 0) { - overlay_imgList <- lapply(overlay_dir, function(x) { - createGiottoLargeImage(x, - name = "composite", - negative_y = TRUE - ) - }) - } + + # setup importer + x <- importCosMx(cosmx_dir = cosmx_dir, fovs = FOVs, ...) + x$version <- version + if (!is.null(fov_shifts_path)) { + checkmate::assert_file_exists(fov_shifts_path) + x$offsets <- data.table::fread(fov_shifts_path) } -} - - - - -#' @title Load and create a CosMx Giotto object from subcellular and aggregate -#' info -#' @name .createGiottoCosMxObject_all -#' @param dir_items list of full directory paths from \code{.read_cosmx_folder} -#' @inheritParams createGiottoCosMxObject -#' @returns giotto object -#' @details Both \emph{subcellular} -#' (subellular transcript detection information) and -#' \emph{aggregate} (aggregated detection count matrices by cell polygon from -#' NanoString) -#' data will be loaded in. The two will be separated into "cell" and "cell_agg" -#' spatial units in order to denote the difference in origin of the two. -#' @seealso createGiottoCosMxObject .createGiottoCosMxObject_aggregate -#' .createGiottoCosMxObject_subcellular -#' @keywords internal -.createGiottoCosMxObject_all <- function( - dir_items, - FOVs, - remove_background_polygon = TRUE, - background_algo = "range", - remove_unvalid_polygons = TRUE, - cores, - verbose = TRUE, - instructions = NULL, - ...) { - # 1. create subcellular giotto as spat_unit 'cell' - cosmx_gobject <- .createGiottoCosMxObject_subcellular( - dir_items = dir_items, - FOVs = FOVs, - remove_background_polygon = remove_background_polygon, - background_algo = background_algo, - remove_unvalid_polygons = remove_unvalid_polygons, - cores = cores, - verbose = verbose, - instructions = instructions - ) - - # 2. load and append aggregated information in spat_unit 'cell_agg' - agg_data <- .load_cosmx_folder_aggregate( - dir_items = dir_items, + + # gobject creation args setup + load_args <- list( + feat_type = feat_type, + split_keyword = split_keyword, + load_images = load_images, + instructions = instructions, cores = cores, verbose = verbose ) - - # unpack data_list - spatlocs <- agg_data$spatlocs - spatlocs_fov <- agg_data$spatlocs_fov - metadata <- agg_data$metadata - protM <- agg_data$protM - spM <- agg_data$spM - - # add in pre-generated aggregated expression matrix information for 'all' - # workflow - - # Add aggregate expression information - if (isTRUE(verbose)) { - wrap_msg( - 'Appending provided aggregate expression data as... - spat_unit: "cell_agg" - feat_type: "rna" - name: "raw"' - ) + # pass optional paths if provided + if (!is.null(transcript_path)) { + load_args$transcript_path <- transcript_path } - # add expression data to expression slot - s4_expr <- createExprObj( - name = "raw", - expression_data = spM, - spat_unit = "cell_agg", - feat_type = "rna", - provenance = "cell_agg" - ) - - cosmx_gobject <- setGiotto(cosmx_gobject, s4_expr) - - # Add spatial locations - if (isTRUE(verbose)) { - wrap_msg( - 'Appending metadata provided spatial locations data as... - --> spat_unit: "cell_agg" name: "raw" - --> spat_unit: "cell" name: "raw_fov"' - ) + if (!is.null(cell_labels_dir)) { + load_args$cell_labels_dir <- cell_labels_dir } - if (isTRUE(verbose)) { - wrap_msg( - 'Polygon centroid derived spatial locations assigned as... - --> spat_unit: "cell" name: "raw" (default)' - ) + if (!is.null(expression_path)) { + load_args$expression_path <- expression_path } - - locsObj <- create_spat_locs_obj( - name = "raw", - coordinates = spatlocs, - spat_unit = "cell_agg", - provenance = "cell_agg" - ) - locsObj_fov <- create_spat_locs_obj( - name = "raw_fov", - coordinates = spatlocs_fov, - spat_unit = "cell_agg", - provenance = "cell_agg" - ) - - cosmx_gobject <- setGiotto(cosmx_gobject, locsObj, initialize = FALSE) - cosmx_gobject <- setGiotto(cosmx_gobject, locsObj_fov, initialize = FALSE) - - # initialize cell and feat IDs and metadata slots for 'cell_agg' spat_unit - agg_cell_ID <- colnames(s4_expr[]) - agg_feat_ID <- rownames(s4_expr[]) - - sub_feat_ID <- featIDs(cosmx_gobject, feat_type = "rna") - feat_ID_new <- unique(c(agg_feat_ID, sub_feat_ID)) - - # cell metadata - - # Add metadata to both the given and the poly spat_units - if (isTRUE(verbose)) message("Appending provided cell metadata...") - cosmx_gobject <- addCellMetadata(cosmx_gobject, - spat_unit = "cell", - feat_type = "rna", - new_metadata = metadata, - by_column = TRUE, - column_cell_ID = "cell_ID" - ) - cosmx_gobject <- addCellMetadata(cosmx_gobject, - spat_unit = "cell_agg", - feat_type = "rna", - new_metadata = metadata, - by_column = TRUE, - column_cell_ID = "cell_ID" - ) - - initialize(cosmx_gobject) -} - - - -#' @title Read a structured CosMx folder -#' @name .read_cosmx_folder -#' @inheritParams createGiottoCosMxObject -#' @seealso createGiottoCosMxObject load_cosmx_folder -#' @returns path_list a list of cosmx files discovered and their filepaths. NULL -#' values denote missing items -#' @keywords internal -.read_cosmx_folder <- function( - cosmx_dir, - verbose = TRUE) { - ch <- box_chars() - - if (is.null(cosmx_dir) | !dir.exists(cosmx_dir)) { - stop("The full path to a cosmx directory must be given.") - } - vmsg("A structured CosMx directory will be used\n", .v = verbose) - - # find directories (length = 1 if present, length = 0 if missing) - dir_items <- list( - `CellLabels folder` = "*CellLabels", - `CompartmentLabels folder` = "*CompartmentLabels", - `CellComposite folder` = "*CellComposite", - `CellOverlay folder` = "*CellOverlay", - `transcript locations file` = "*tx_file*", - `fov positions file` = "*fov_positions_file*", - `expression matrix file` = "*exprMat_file*", - `metadata file` = "*metadata_file*" - ) - dir_items <- lapply( - dir_items, function(x) Sys.glob(paths = file.path(cosmx_dir, x)) - ) - dir_items_lengths <- lengths(dir_items) - - if (isTRUE(verbose)) { - message("Checking directory contents...") - for (item in names(dir_items)) { - if (dir_items_lengths[[item]] > 0) { - message(ch$s, "> ", item, " found") - } else { - warning(item, " is missing\n") - } - } + if (!is.null(metadata_path)) { + load_args$metadata_path <- metadata_path } + + do.call(x$create_gobject, load_args) +} - # select first directory in list if multiple are detected - if (any(dir_items_lengths > 1)) { - warning("Multiple matches for expected subdirectory item(s).\n - First matching item selected") - multiples <- which(dir_items_lengths > 1) - for (mult_i in multiples) { - message(names(dir_items)[[mult_i]], "multiple matches found:") - print(dir_items[[mult_i]]) - dir_items[[mult_i]] <- dir_items[[mult_i]][[1]] - } - } - vmsg("Directory check done", .v = verbose) - return(dir_items) -} diff --git a/man/createGiottoCosMxObject.Rd b/man/createGiottoCosMxObject.Rd index 324bfc640..c9351e724 100644 --- a/man/createGiottoCosMxObject.Rd +++ b/man/createGiottoCosMxObject.Rd @@ -5,41 +5,92 @@ \title{Create Nanostring CosMx Giotto Object} \usage{ createGiottoCosMxObject( - cosmx_dir = NULL, - data_to_use = c("all", "subcellular", "aggregate"), - remove_background_polygon = TRUE, - background_algo = c("range"), - remove_unvalid_polygons = TRUE, + cosmx_dir, + version = "default", FOVs = NULL, - instructions = NULL, + feat_type = c("rna", "negprobes"), + split_keyword = list("NegPrb"), + load_images = list(composite = "composite", overlay = "overlay"), + load_expression = FALSE, + load_cellmeta = FALSE, + fov_shifts_path = NULL, + transcript_path = NULL, + cell_labels_dir = NULL, + expression_path = NULL, + metadata_path = NULL, cores = determine_cores(), - verbose = TRUE + verbose = NULL, + instructions = NULL, + remove_unvalid_polygons = deprecated(), + data_to_use = deprecated(), + remove_background_polygon = deprecated(), + background_algo = deprecated(), + ... ) } \arguments{ \item{cosmx_dir}{full path to the exported cosmx directory} -\item{data_to_use}{which type(s) of expression data to build the gobject with -Default is \code{'all'} information available. \code{'subcellular'} loads -the transcript coordinates only. \code{'aggregate'} loads the provided -aggregated expression matrix.} +\item{version}{character. Version of CosMx output. Current selections are +either "default", "v6", and "legacy" (for the NSCLC dataset).} + +\item{FOVs}{field of views to load (only affects subcellular data and images)} -\item{remove_background_polygon}{try to remove background polygon -(default: FALSE)} +\item{feat_type}{character. feature type. Provide more than one value if +using the \code{split_keyword} param. For each set of keywords to split by, an +additional feat_type should be provided in the same order. Affects how +the transcripts information is loaded. Helpful for separating out the +QC probes. See details.} -\item{background_algo}{algorithm to remove background polygon} +\item{split_keyword}{list of character vectors of keywords to split the +transcripts based on their feat_ID. Keywords will be \code{grepl()} +matched against the feature IDs information. See details.} -\item{remove_unvalid_polygons}{remove unvalid polygons (default: TRUE)} +\item{load_images}{named list of filepaths to directories. Loads the +composite and overlay images by default.} -\item{FOVs}{field of views to load (only affects subcellular data and images)} +\item{load_expression}{logical. (Default = FALSE) whether to load provided +expression matrix} + +\item{load_cellmeta}{logical. (Default = TRUE) whether to load provided +cell metadata} + +\item{fov_shifts_path}{Optional. Filepath to fov_positions_file} + +\item{transcript_path}{Optional. Filepath to desired transcripts file to +load.} + +\item{cell_labels_dir}{Optional. Path to directory containing CellLabels +images to load as polygons.} + +\item{expression_path}{Optional. Filepath to cell feature matrix to load.} + +\item{metadata_path}{Optional. Filepath to metadata file to load.} + +\item{cores}{nthreads to use when loading in cell metadata and transcripts} + +\item{verbose}{be verbose when building Giotto object} \item{instructions}{list of instructions or output result from \code{\link[GiottoClass]{createGiottoInstructions}}} -\item{cores}{how many cores or threads to use to read data if paths are -provided} +\item{remove_unvalid_polygons}{deprecated. Now always done} -\item{verbose}{be verbose when building Giotto object} +\item{data_to_use}{deprecated. Not used} + +\item{remove_background_polygon}{deprecated. Now always done} + +\item{background_algo}{deprecated. Not used} + +\item{...}{ + Arguments passed on to \code{\link[=importCosMx]{importCosMx}} + \describe{ + \item{\code{slide}}{numeric. Slide number. Defaults to 1} + \item{\code{micron}}{logical. Whether to scale spatial information as micron +instead of the default pixels} + \item{\code{px2um}}{numeric. Scalefactor from pixels to micron. Defaults to 0.12028 +based on `CosMx-ReadMe.html` info} + }} } \value{ a giotto object @@ -54,37 +105,30 @@ given a link to a cosmx output directory. It expects the following items within the directory where the \strong{bolded} portions are what this function matches against: \itemize{ - \item{\strong{CellComposite} (folder of images)} - \item{\strong{CellLabels} (folder of images)} - \item{\strong{CellOverlay} (folder of images)} - \item{\strong{CompartmentLabels} (folder of images)} - \item{experimentname_\strong{exprMat_file}.csv (file)} - \item{experimentname_\strong{fov_positions_file}.csv (file)} - \item{experimentname_\strong{metadata_file}.csv (file)} - \item{experimentname_\strong{tx_file}.csv (file)} +\item{\strong{CellComposite} (folder of images)} +\item{\strong{CellLabels} (folder of images)} +\item{\strong{CellOverlay} (folder of images)} +\item{\strong{CompartmentLabels} (folder of images)} +\item{experimentname_\strong{exprMat_file}.csv (file)} +\item{experimentname_\strong{fov_positions_file}.csv (file)} +\item{experimentname_\strong{metadata_file}.csv (file)} +\item{experimentname_\strong{tx_file}.csv (file)} } -[\strong{Workflows}] Workflow to use is accessed through the data_to_use -param -\itemize{ - \item{'all' - loads and requires subcellular information from tx_file and - fov_positions_file - and also the existing aggregated information - (expression, spatial locations, and metadata) - from exprMat_file and metadata_file.} - \item{'subcellular' - loads and requires subcellular information from - tx_file and - fov_positions_file only.} - \item{'aggregate' - loads and requires the existing aggregate information - (expression, spatial locations, and metadata) from exprMat_file and - metadata_file.} -} +[\strong{feat_type and split_keyword}] +Additional QC probe information is in the subcellular feature detections +information and must be separated from the gene expression information +during processing. +The QC probes have prefixes that allow them to be selected from the rest of +the feature IDs. +Giotto uses \code{feat_type} and \code{split_keyword} params to select these QC +probes out as separate feature types. See examples in +\verb{[GiottoClass::createGiottoPoints]} for how this works. + +The Gene expression subset labeled as \code{rna} is accepted as the subset of +feat_IDs that do not get matched to any of the \code{split_keywords}. -[\strong{Images}] Images in the default CellComposite, CellLabels, -CompartmentLabels, and CellOverlay -folders will be loaded as giotto largeImage objects in all workflows as -long as they are available. Additionally, CellComposite images will be -converted to giotto image objects, making plotting with -these image objects more responsive when accessing them from a server. +[\strong{Images}] Images in the expected CellComposite and CellOverlay +folders will be loaded as giotto largeImage objects by default. \code{\link{showGiottoImageNames}} can be used to see the available images. } diff --git a/man/dot-createGiottoCosMxObject_aggregate.Rd b/man/dot-createGiottoCosMxObject_aggregate.Rd deleted file mode 100644 index 8dcda4a9f..000000000 --- a/man/dot-createGiottoCosMxObject_aggregate.Rd +++ /dev/null @@ -1,29 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.createGiottoCosMxObject_aggregate} -\alias{.createGiottoCosMxObject_aggregate} -\title{Load and create a CosMx Giotto object from aggregate info} -\usage{ -.createGiottoCosMxObject_aggregate( - dir_items, - cores, - verbose = TRUE, - instructions = NULL -) -} -\arguments{ -\item{cores}{how many cores or threads to use to read data if paths are -provided} - -\item{verbose}{be verbose when building Giotto object} - -\item{instructions}{list of instructions or output result -from \code{\link[GiottoClass]{createGiottoInstructions}}} -} -\value{ -giotto object -} -\description{ -Load and create a CosMx Giotto object from aggregate info -} -\keyword{internal} diff --git a/man/dot-createGiottoCosMxObject_all.Rd b/man/dot-createGiottoCosMxObject_all.Rd deleted file mode 100644 index 7f1dc6e12..000000000 --- a/man/dot-createGiottoCosMxObject_all.Rd +++ /dev/null @@ -1,59 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.createGiottoCosMxObject_all} -\alias{.createGiottoCosMxObject_all} -\title{Load and create a CosMx Giotto object from subcellular and aggregate -info} -\usage{ -.createGiottoCosMxObject_all( - dir_items, - FOVs, - remove_background_polygon = TRUE, - background_algo = "range", - remove_unvalid_polygons = TRUE, - cores, - verbose = TRUE, - instructions = NULL, - ... -) -} -\arguments{ -\item{dir_items}{list of full directory paths from \code{.read_cosmx_folder}} - -\item{FOVs}{field of views to load (only affects subcellular data and images)} - -\item{remove_background_polygon}{try to remove background polygon -(default: FALSE)} - -\item{background_algo}{algorithm to remove background polygon} - -\item{remove_unvalid_polygons}{remove unvalid polygons (default: TRUE)} - -\item{cores}{how many cores or threads to use to read data if paths are -provided} - -\item{verbose}{be verbose when building Giotto object} - -\item{instructions}{list of instructions or output result -from \code{\link[GiottoClass]{createGiottoInstructions}}} -} -\value{ -giotto object -} -\description{ -Load and create a CosMx Giotto object from subcellular and aggregate -info -} -\details{ -Both \emph{subcellular} -(subellular transcript detection information) and -\emph{aggregate} (aggregated detection count matrices by cell polygon from -NanoString) -data will be loaded in. The two will be separated into "cell" and "cell_agg" -spatial units in order to denote the difference in origin of the two. -} -\seealso{ -createGiottoCosMxObject .createGiottoCosMxObject_aggregate -.createGiottoCosMxObject_subcellular -} -\keyword{internal} diff --git a/man/dot-createGiottoCosMxObject_subcellular.Rd b/man/dot-createGiottoCosMxObject_subcellular.Rd deleted file mode 100644 index d0c315606..000000000 --- a/man/dot-createGiottoCosMxObject_subcellular.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.createGiottoCosMxObject_subcellular} -\alias{.createGiottoCosMxObject_subcellular} -\title{Load and create a CosMx Giotto object from subcellular info} -\usage{ -.createGiottoCosMxObject_subcellular( - dir_items, - FOVs = NULL, - remove_background_polygon = TRUE, - background_algo = c("range"), - remove_unvalid_polygons = TRUE, - cores, - verbose = TRUE, - instructions = NULL -) -} -\arguments{ -\item{FOVs}{field of views to load (only affects subcellular data and images)} - -\item{remove_background_polygon}{try to remove background polygon -(default: FALSE)} - -\item{background_algo}{algorithm to remove background polygon} - -\item{remove_unvalid_polygons}{remove unvalid polygons (default: TRUE)} - -\item{cores}{how many cores or threads to use to read data if paths are -provided} - -\item{verbose}{be verbose when building Giotto object} - -\item{instructions}{list of instructions or output result -from \code{\link[GiottoClass]{createGiottoInstructions}}} -} -\value{ -giotto object -} -\description{ -Load and create a CosMx Giotto object from subcellular info -} -\keyword{internal} diff --git a/man/dot-load_cosmx_folder_aggregate.Rd b/man/dot-load_cosmx_folder_aggregate.Rd deleted file mode 100644 index 0d837368a..000000000 --- a/man/dot-load_cosmx_folder_aggregate.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.load_cosmx_folder_aggregate} -\alias{.load_cosmx_folder_aggregate} -\title{Load CosMx folder aggregate info} -\usage{ -.load_cosmx_folder_aggregate(dir_items, cores, verbose = TRUE) -} -\arguments{ -\item{cores}{how many cores or threads to use to read data if paths are -provided} - -\item{verbose}{be verbose when building Giotto object} -} -\value{ -list -} -\description{ -Load CosMx folder aggregate info -} -\keyword{internal} diff --git a/man/dot-load_cosmx_folder_subcellular.Rd b/man/dot-load_cosmx_folder_subcellular.Rd deleted file mode 100644 index d218f1045..000000000 --- a/man/dot-load_cosmx_folder_subcellular.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.load_cosmx_folder_subcellular} -\alias{.load_cosmx_folder_subcellular} -\title{Load CosMx folder subcellular info} -\usage{ -.load_cosmx_folder_subcellular(dir_items, FOVs = NULL, cores, verbose = TRUE) -} -\arguments{ -\item{FOVs}{field of views to load (only affects subcellular data and images)} - -\item{cores}{how many cores or threads to use to read data if paths are -provided} - -\item{verbose}{be verbose when building Giotto object} -} -\value{ -list -} -\description{ -loads in the feature detections information. Note that the mask -images are still required for a working subcellular object, and those are -loaded in \code{\link{.createGiottoCosMxObject_subcellular}} -} -\keyword{internal} diff --git a/man/dot-read_cosmx_folder.Rd b/man/dot-read_cosmx_folder.Rd deleted file mode 100644 index a5541c896..000000000 --- a/man/dot-read_cosmx_folder.Rd +++ /dev/null @@ -1,24 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/convenience_cosmx.R -\name{.read_cosmx_folder} -\alias{.read_cosmx_folder} -\title{Read a structured CosMx folder} -\usage{ -.read_cosmx_folder(cosmx_dir, verbose = TRUE) -} -\arguments{ -\item{cosmx_dir}{full path to the exported cosmx directory} - -\item{verbose}{be verbose when building Giotto object} -} -\value{ -path_list a list of cosmx files discovered and their filepaths. NULL -values denote missing items -} -\description{ -Read a structured CosMx folder -} -\seealso{ -createGiottoCosMxObject load_cosmx_folder -} -\keyword{internal} diff --git a/man/importCosMx.Rd b/man/importCosMx.Rd index 11c0c2eb6..fbfd0f8df 100644 --- a/man/importCosMx.Rd +++ b/man/importCosMx.Rd @@ -9,7 +9,7 @@ importCosMx( slide = 1, fovs = NULL, micron = FALSE, - px2mm = 0.12028 + px2um = 0.12028 ) } \arguments{ @@ -23,7 +23,7 @@ Otherwise, all FOVs will be loaded} \item{micron}{logical. Whether to scale spatial information as micron instead of the default pixels} -\item{px2mm}{numeric. Scalefactor from pixels to mm. Defaults to 0.12028 +\item{px2um}{numeric. Scalefactor from pixels to micron. Defaults to 0.12028 based on `CosMx-ReadMe.html` info} } \value{ From c365e6feea894b67782ea3900610f6c955e8124b Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:56:00 -0500 Subject: [PATCH 21/21] Update NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index fea0eb026..eb259f0bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ ## Bug fixes * fix `importCosMx()` fov shifts file detection +* fix micron scaling for `importCosMx()` * `callSpdep()` should also automatically convert *Matrix* classes to `listw` ## Enhancements