Skip to content
Draft
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 112 additions & 3 deletions R/preprocessing.R
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ GetResidual <- function(
#' @param filename Name of H5 file containing the feature barcode matrix
#' @param slice Name for the stored image of the tissue slice
#' @param bin.size Specifies the bin sizes to read in - defaults to c(16, 8)
#' @param load.segmentations Should the cell segmentation from space ranger be read in?
#' @param filter.matrix Only keep spots that have been determined to be over
#' tissue
#' @param to.upper Converts all feature names to upper case. Can be useful when
Expand Down Expand Up @@ -527,6 +528,7 @@ Load10X_Spatial <- function (
assay = "Spatial",
slice = "slice1",
bin.size = NULL,
load.segmentations = FALSE,
filter.matrix = TRUE,
to.upper = FALSE,
image = NULL,
Expand Down Expand Up @@ -640,6 +642,22 @@ Load10X_Spatial <- function (
y = object.list[-1]
)

# Possibly make this into a new function
if(load.segmentations) {
segmentation.assay.name <- "Segmentations"
seg.data.dir <- paste0(data.dir, "/segmented_outputs/")
seg.counts.path <- paste0(seg.data.dir, "filtered_feature_cell_matrix.h5")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: filename

segmentation.counts <- Read10X_h5(seg.counts.path)
segmentation.count.cellIDs <- colnames(segmentation.counts)
#segmentation.image <- Read10X_Image(image.dir = paste0(seg.data.dir, "spatial"), assay = "Segmentations", slice = "slice1.segmentations")
segmentation.object <- CreateSeuratObject(segmentation.counts, assay = segmentation.assay.name)
#segmentation.object[["slice1.segmentation.image"]] <- segmentation.image
segmentation.fov <- Read10X_Segmentations(image.dir = paste0(seg.data.dir, "spatial"), data.dir = data.dir, cell.names = segmentation.count.cellIDs)
segmentation.object[["slice1.polygons"]] <- segmentation.fov
object <- merge(x = object, y = segmentation.object)
DefaultAssay(object = object) <- segmentation.assay.name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's intuitive to have this create two assays and merge them together, rather than just one or the other. Will defer to you since you look at this data more, though.

Also, this output won't exist for Visium SD, which is also loaded through this preprocessing function, right? Should we raise a more informative error in those cases?

}

return(object)
}

Expand Down Expand Up @@ -1263,8 +1281,8 @@ Read10X_Image <- function(
image = image
)

# As of v5.1.0 `Radius.VisiumV1` no longer returns the value of the
# `spot.radius` slot and instead calculates the value on the fly, but we
# As of v5.1.0 `Radius.VisiumV1` no longer returns the value of the
# `spot.radius` slot and instead calculates the value on the fly, but we
# can populate the static slot in case it's depended on.
visium.v1@spot.radius <- Radius(visium.v1)

Expand Down Expand Up @@ -1390,6 +1408,97 @@ Read10X_ScaleFactors <- function(filename) {
return (scale.factors)
}

#' Load 10X Genomics Visium Cell Segmentations
#'
#' @param image.dir Path to directory with 10X Genomics visium image data;
#' @param data.dir Directory of the base spaceranger outs
#' @param image.name Name of the tissue image to be plotted. tissue_lowres_image.png or tissue_hires_image.png
#' @param assay Name of assay to associate segmentations to
#' @param slice Name of the slice to associate the segmentations to
#' @param segmentation.type Which segmentations to load, cell or nucleus. If using nucleus the full matrix from cells is still used
#' @param cell.names Names of the cells from the matrix.h5
#'
#'
#' @return A FOV
#'
#' @export
#' @concept preprocessing
#'
Read10X_Segmentations <- function (image.dir,
data.dir,
image.name = "tissue_lowres_image.png",
assay = "Segmentations",
slice = "slice1.segmentations",
segmentation.type = "cell",
cell.names
)
{

image <- png::readPNG(source = file.path(image.dir, image.name))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we need to get the tissue image in there at some point so I left it in.

scale.factors <- Read10X_ScaleFactors(filename = file.path(image.dir, "scalefactors_json.json"))
coordinates <- Read10X_GeoJson(data.dir = data.dir, segmentation.type = segmentation.type)
key <- Key(slice, quiet = TRUE)

st_coords <- merge(
as.data.frame(sf::st_coordinates(coordinates)),
data.frame(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with OP that it would be nice to have a CreateFOV.sfc or similar that allows this construction without having to do two round trips through polygon construction. Not sure if such a method already exists in Seurat

cell_id = coordinates$cell_id,
cell = coordinates$barcodes
),
by.x = 'L2',
by.y = 'cell_id'
)
st_coords <- st_coords[st_coords$cell %in% cell.names, ]
image.scale <- ifelse(grepl("lowres", image.name, ignore.case = TRUE), "lowres", "highres")
fov <- CreateFOV(st_coords,
type = "segmentation",
#radius = scale.factors[[image.scale]],
assay = assay, key = key)
return(fov)
}

#' Format 10X Genomics GeoJson cell IDs
#'
#' A helper function to format cell IDs from the segmentation GeoJson to the same type as in the matrix.h5
#' The GeoJson has cell IDs as integers (eg 1). They need to be in the format cellid_000000001-1
#'
#' @return Vector of formatted cell IDs
Format10X_GeoJson_CellID <- function(ids, prefix = "cellid_", suffix = "-1", digits = 9) {
format_string <- paste0("%0", as.integer(digits), "d")

formatted_ids <- sapply(ids, function(id) {
numeric_part <- sprintf(format_string, as.integer(id))
paste0(prefix, numeric_part, suffix)
})

return(formatted_ids)
}

#' Load 10X Genomics GeoJson
#'
#' @param filename Path to a \code{cell_segmentations.geojson} file
#' @param segmentation.type Which segmentations to load, cell or nucleus. If using nucleus the full matrix from cells is still used
#' @param scale.factor If scaling the segmentations coordinates for the associated tissue image. "lowres" or "highres"
#'
#' @return A FOV
#'
#' @export
#' @concept preprocessing
#'
Read10X_GeoJson <- function(data.dir, segmentation.type = "cell", scale.factor = NULL) {
segmentation_polygons <- read_sf(file.path(data.dir,"segmented_outputs", paste0(segmentation.type, "_segmentations.geojson")))
Comment thread
stephenwilliams22 marked this conversation as resolved.
if (!is.null(scale.factor)) {
scale.factor.path <- file.path(data.dir,"segmented_outputs/spatial/scalefactors_json.json")
scale.factors <- jsonlite::fromJSON(scale.factor.path)
Comment thread
stephenwilliams22 marked this conversation as resolved.
Outdated
segmentation_polygons$geometry <- segmentation_polygons$geometry*scale.factors[[scale.factor]]
}

segmentation_polygons$barcodes <- Format10X_GeoJson_CellID(segmentation_polygons$cell_id)
segmentation_polygons
}



#' Read and Load Akoya CODEX data
#'
#' @param filename Path to matrix generated by upstream processing.
Expand Down Expand Up @@ -4557,7 +4666,7 @@ FindSpatiallyVariableFeatures.Seurat <- function(
verbose = verbose,
...
)

object <- LogSeuratCommand(object)

return(object)
Expand Down
Loading