From 7f27f2005c19d44aec20ff880bcc5eb44fc53b55 Mon Sep 17 00:00:00 2001 From: nilseling Date: Mon, 18 Sep 2023 13:26:00 +0200 Subject: [PATCH 01/13] Start on Zarr reader --- R/ZarrReaders.R | 66 +++++++++++++++++++++++++++++++++++ vignettes/cytomapper_zarr.Rmd | 41 ++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 R/ZarrReaders.R create mode 100644 vignettes/cytomapper_zarr.Rmd diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R new file mode 100644 index 0000000..b8bda7b --- /dev/null +++ b/R/ZarrReaders.R @@ -0,0 +1,66 @@ +#' @title Read data from ZARR files +#' @name readZARR +#' +#' @description +#' Reads in data from a ZARR file +#' +#' @param file +#' @param +#' +#' @return A \linkS4class{CytoImageList} object +#' +#' @examples +#' # TODO +#' +#' @author Nils Eling (\email{nils.eling@@dqbm.uzh.ch}), +#' +#' @export +readZARR <- function(file, + type = c("omengff", "spatialdata"), + what = c("images", "masks"), + resolution = NULL, + z = 1, + t = 1, + fov_name = NULL) { + + # Check if file is spatialData or OME-NGFF + #.valid.readZARR.input(file) + + type <- match.arg(type) + what <- match.arg(what) + + if (type == "spatialdata") { + if (is.null(fov_name)) { + fov_name <- list.files(file.path(file, "images"))[1] + } + + cur_out <- lapply(fov_name, function(x){ + + if (is.null(resolution)) { + cur_res <- list.files(file.path(file, "images", x)) + cur_res <- cur_res[length(cur_res)] + } + + cur_name <- file.path(file, "images", x, cur_res) + + # Create index + cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) + cur_dim <- cur_zarr$dim[[1]] + + if (length(cur_dim) == 3) { + cur_ind <- list(NULL, NULL, NULL) + } else if (length(cur_dim) == 4) { + cur_ind <- list(NULL, NULL, NULL, z) + } else if (length(cur_dim) == 5) { + cur_ind <- list(NULL, NULL, NULL, z, t) + } + + cur_arr <- read_zarr_array(cur_name, index = cur_ind) + cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) + + return(cur_arr) + }) + + } + +} \ No newline at end of file diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd new file mode 100644 index 0000000..3462b59 --- /dev/null +++ b/vignettes/cytomapper_zarr.Rmd @@ -0,0 +1,41 @@ +--- +title: "Zarr support for cytomapper" +date: "`r BiocStyle::doc_date()`" +package: "`r BiocStyle::pkg_ver('cytomapper')`" +author: +- name: Nils Eling + affiliation: + - Department for Quantitative Biomedicine, University of Zurich + - Institute for Molecular Health Sciences, ETH Zurich + email: nils.eling@dqbm.uzh.ch +output: + BiocStyle::html_document: + toc_float: yes +bibliography: library.bib +abstract: | + TODO +vignette: | + %\VignetteIndexEntry{"On disk storage of images"} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, echo=FALSE, results="hide"} +knitr::opts_chunk$set(error=FALSE, warning=FALSE, message=FALSE, + fig.retina = 0.75, crop = NULL) +library(BiocStyle) +``` + +```{r library, echo=FALSE} +library(cytomapper) +``` + +# Zarr + +```{r} +library(Rarr) + +test <- Rarr::read_zarr_array("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/images/blobs_image/0/") + +test <- Rarr:: +``` \ No newline at end of file From 756fa336cf4dcc81d060924db7fdbd623d3e8f91 Mon Sep 17 00:00:00 2001 From: nilseling Date: Mon, 18 Sep 2023 13:33:30 +0200 Subject: [PATCH 02/13] Started on vignette --- vignettes/cytomapper_zarr.Rmd | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index 3462b59..9bba0da 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -33,9 +33,5 @@ library(cytomapper) # Zarr ```{r} -library(Rarr) -test <- Rarr::read_zarr_array("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/images/blobs_image/0/") - -test <- Rarr:: ``` \ No newline at end of file From c094bf39cbda126d5bcc2c4141c8317160989667 Mon Sep 17 00:00:00 2001 From: nilseling Date: Mon, 18 Sep 2023 15:28:07 +0200 Subject: [PATCH 03/13] Reading in images works --- DESCRIPTION | 2 +- NAMESPACE | 1 + NEWS | 3 ++ R/ZarrReaders.R | 71 ++++++++++++++++++++++++++--------- man/readZARR.Rd | 53 ++++++++++++++++++++++++++ vignettes/cytomapper_zarr.Rmd | 3 +- 6 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 man/readZARR.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 754a25a..0eb1858 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: cytomapper -Version: 1.13.0 +Version: 1.13.1 Title: Visualization of highly multiplexed imaging data in R Description: Highly multiplexed imaging acquires the single-cell expression of diff --git a/NAMESPACE b/NAMESPACE index 196bc41..6a338fd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,6 +14,7 @@ export(measureObjects) export(mergeChannels) export(plotCells) export(plotPixels) +export(readZARR) export(scaleImages) exportClasses(CytoImageList) exportMethods("[<-") diff --git a/NEWS b/NEWS index 2f7d040..48f5c89 100644 --- a/NEWS +++ b/NEWS @@ -183,3 +183,6 @@ Changes in version 1.11.2 (2023-02-02): Changes in version 1.11.3 (2023-01-23): + loadImages: added option to read in single-channel images to multi-channel + +Changes in version 1.13.1 (2023-09-18): ++ readZARR function to read in images and masks from ZARR archives diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index b8bda7b..7f12ab0 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -4,8 +4,16 @@ #' @description #' Reads in data from a ZARR file #' -#' @param file -#' @param +#' @param file TODO +#' @param type TODO +#' @param what TODO +#' @param resolution TODO +#' @param x TODO +#' @param y TODO +#' @param c TODO +#' @param z TODO +#' @param t TODO +#' @param fov_names TODO #' #' @return A \linkS4class{CytoImageList} object #' @@ -19,9 +27,12 @@ readZARR <- function(file, type = c("omengff", "spatialdata"), what = c("images", "masks"), resolution = NULL, + x = NULL, + y = NULL, + c = NULL, z = 1, t = 1, - fov_name = NULL) { + fov_names = NULL) { # Check if file is spatialData or OME-NGFF #.valid.readZARR.input(file) @@ -29,30 +40,49 @@ readZARR <- function(file, type <- match.arg(type) what <- match.arg(what) - if (type == "spatialdata") { - if (is.null(fov_name)) { - fov_name <- list.files(file.path(file, "images"))[1] - } - - cur_out <- lapply(fov_name, function(x){ + if (what == "images") { + if (type == "spatialdata") { + if (is.null(fov_names)) { + fov_names <- list.files(file.path(file, "images"))[1] + } + + if (is.null(resolution)) { + if (length(fov_names) > 1) { + resolution <- lapply(file.path(file, "images", fov_names), + function(cur_name) { + cur_res <- list.files(cur_name) + return(cur_res[length(cur_res)]) + }) + resolution <- unlist(resolution) + } else { + resolution <- list.files(file.path(file, "images", fov_names)) + resolution <- resolution[length(resolution)] + } + } + cur_names <- file.path(file, "images", fov_names, resolution) + + } else if (type == "omengff") { if (is.null(resolution)) { - cur_res <- list.files(file.path(file, "images", x)) - cur_res <- cur_res[length(cur_res)] + resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) + resolution <- resolution[length(resolution)] } + + cur_names <- file.path(file, resolution) + } - cur_name <- file.path(file, "images", x, cur_res) + cur_out <- lapply(cur_names, function(cur_name){ # Create index cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) cur_dim <- cur_zarr$dim[[1]] if (length(cur_dim) == 3) { - cur_ind <- list(NULL, NULL, NULL) + cur_ind <- list(c, y, x) } else if (length(cur_dim) == 4) { - cur_ind <- list(NULL, NULL, NULL, z) + cur_ind <- list(z, c, y, x) } else if (length(cur_dim) == 5) { - cur_ind <- list(NULL, NULL, NULL, z, t) + cur_ind <- list(t, z, c, y, x) } cur_arr <- read_zarr_array(cur_name, index = cur_ind) @@ -60,7 +90,14 @@ readZARR <- function(file, return(cur_arr) }) - + + names(cur_out) <- fov_names + + cur_CIL <- CytoImageList(cur_out) + + return(cur_CIL) + } else if (what == "masks") { + } - + } \ No newline at end of file diff --git a/man/readZARR.Rd b/man/readZARR.Rd new file mode 100644 index 0000000..f4f263f --- /dev/null +++ b/man/readZARR.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ZarrReaders.R +\name{readZARR} +\alias{readZARR} +\title{Read data from ZARR files} +\usage{ +readZARR( + file, + type = c("omengff", "spatialdata"), + what = c("images", "masks"), + resolution = NULL, + x = NULL, + y = NULL, + c = NULL, + z = 1, + t = 1, + fov_names = NULL +) +} +\arguments{ +\item{file}{TODO} + +\item{type}{TODO} + +\item{what}{TODO} + +\item{resolution}{TODO} + +\item{x}{TODO} + +\item{y}{TODO} + +\item{c}{TODO} + +\item{z}{TODO} + +\item{t}{TODO} + +\item{fov_name}{TODO} +} +\value{ +A \linkS4class{CytoImageList} object +} +\description{ +Reads in data from a ZARR file +} +\examples{ +# TODO + +} +\author{ +Nils Eling (\email{nils.eling@dqbm.uzh.ch}), +} diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index 9bba0da..4e34c18 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -33,5 +33,6 @@ library(cytomapper) # Zarr ```{r} - +images <- readZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", + type = "spatialdata", what = "images") ``` \ No newline at end of file From a9ac4d04a13ed4e833b972b75f0d6dd20480d454 Mon Sep 17 00:00:00 2001 From: nilseling Date: Mon, 18 Sep 2023 16:56:13 +0200 Subject: [PATCH 04/13] Read in channel names --- NAMESPACE | 1 + R/ZarrReaders.R | 37 +++++++++++++++++++++++++++++++++++ man/readZARR.Rd | 2 +- vignettes/cytomapper_zarr.Rmd | 3 +++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index 6a338fd..5dbff38 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -98,6 +98,7 @@ importFrom(graphics,rect) importFrom(graphics,strheight) importFrom(graphics,strwidth) importFrom(graphics,text) +importFrom(jsonlite,fromJSON) importFrom(matrixStats,rowRanges) importFrom(methods,as) importFrom(methods,callNextMethod) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 7f12ab0..e448a51 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -23,6 +23,8 @@ #' @author Nils Eling (\email{nils.eling@@dqbm.uzh.ch}), #' #' @export +#' @importFrom jsonlite fromJSON +#' @importFrom Rarr read_zarr_array zarr_overview readZARR <- function(file, type = c("omengff", "spatialdata"), what = c("images", "masks"), @@ -62,13 +64,41 @@ readZARR <- function(file, cur_names <- file.path(file, "images", fov_names, resolution) + # Read in metadata + cur_meta <- lapply(file.path(file, "images", fov_names), + function(cur_m) { + return(fromJSON(file.path(cur_m, ".zattrs"))) + }) + + cur_channels <- lapply(cur_meta, function(cur_m){ + if ("omero" %in% names(cur_meta)) { + return(cur_meta$omero$channels$label) + } else if ("channels_metadata" %in% names(cur_meta)) { + return(cur_meta$channels_metadata$channels$label) + } + }) + + if (length(unique(cur_channels)) > 1) { + stop("Channel names need to match across images.") + } + } else if (type == "omengff") { if (is.null(resolution)) { resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) resolution <- resolution[length(resolution)] } + fov_names <- sub("\\.[^.]*$", "", basename(file)) + cur_names <- file.path(file, resolution) + + cur_meta <- fromJSON(file.path(file, ".zattrs")) + + if ("omero" %in% names(cur_meta)) { + cur_channels <- cur_meta$omero$channels$label + } else if ("channels_metadata" %in% names(cur_meta)) { + cur_channels <- cur_meta$channels_metadata$channels$label + } } cur_out <- lapply(cur_names, function(cur_name){ @@ -95,6 +125,13 @@ readZARR <- function(file, cur_CIL <- CytoImageList(cur_out) + # Set channelNames + if (exists("cur_channels")) { + channelNames(cur_CIL) <- cur_channels + } else { + channelNames(cur_CIL) <- as.character(seq_len(dim(cur_CIL[[1]])[3])) + } + return(cur_CIL) } else if (what == "masks") { diff --git a/man/readZARR.Rd b/man/readZARR.Rd index f4f263f..bc13b82 100644 --- a/man/readZARR.Rd +++ b/man/readZARR.Rd @@ -36,7 +36,7 @@ readZARR( \item{t}{TODO} -\item{fov_name}{TODO} +\item{fov_names}{TODO} } \value{ A \linkS4class{CytoImageList} object diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index 4e34c18..20fb96c 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -35,4 +35,7 @@ library(cytomapper) ```{r} images <- readZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", type = "spatialdata", what = "images") + +#images <- readZARR("~/Desktop/5514375.zarr/", +# type = "omengff", what = "images") ``` \ No newline at end of file From 9c8e8458f466eb9e73ab2f7dbe48cf934ad4bcda Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 09:34:37 +0200 Subject: [PATCH 05/13] Split into two functions --- R/ZarrReaders.R | 160 +++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 83 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index e448a51..df1e338 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -25,9 +25,8 @@ #' @export #' @importFrom jsonlite fromJSON #' @importFrom Rarr read_zarr_array zarr_overview -readZARR <- function(file, +readImagesFromZARR <- function(file, type = c("omengff", "spatialdata"), - what = c("images", "masks"), resolution = NULL, x = NULL, y = NULL, @@ -42,99 +41,94 @@ readZARR <- function(file, type <- match.arg(type) what <- match.arg(what) - if (what == "images") { - if (type == "spatialdata") { - if (is.null(fov_names)) { - fov_names <- list.files(file.path(file, "images"))[1] - } - - if (is.null(resolution)) { - if (length(fov_names) > 1) { - resolution <- lapply(file.path(file, "images", fov_names), - function(cur_name) { - cur_res <- list.files(cur_name) - return(cur_res[length(cur_res)]) - }) - resolution <- unlist(resolution) - } else { - resolution <- list.files(file.path(file, "images", fov_names)) - resolution <- resolution[length(resolution)] - } - } - - cur_names <- file.path(file, "images", fov_names, resolution) - - # Read in metadata - cur_meta <- lapply(file.path(file, "images", fov_names), - function(cur_m) { - return(fromJSON(file.path(cur_m, ".zattrs"))) - }) - - cur_channels <- lapply(cur_meta, function(cur_m){ - if ("omero" %in% names(cur_meta)) { - return(cur_meta$omero$channels$label) - } else if ("channels_metadata" %in% names(cur_meta)) { - return(cur_meta$channels_metadata$channels$label) - } - }) - - if (length(unique(cur_channels)) > 1) { - stop("Channel names need to match across images.") - } - - } else if (type == "omengff") { - if (is.null(resolution)) { - resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) + if (type == "spatialdata") { + if (is.null(fov_names)) { + fov_names <- list.files(file.path(file, "images"))[1] + } + + if (is.null(resolution)) { + if (length(fov_names) > 1) { + resolution <- lapply(file.path(file, "images", fov_names), + function(cur_name) { + cur_res <- list.files(cur_name) + return(cur_res[length(cur_res)]) + }) + resolution <- unlist(resolution) + } else { + resolution <- list.files(file.path(file, "images", fov_names)) resolution <- resolution[length(resolution)] } - - fov_names <- sub("\\.[^.]*$", "", basename(file)) - - cur_names <- file.path(file, resolution) - - cur_meta <- fromJSON(file.path(file, ".zattrs")) - + } + + cur_names <- file.path(file, "images", fov_names, resolution) + + # Read in metadata + cur_meta <- lapply(file.path(file, "images", fov_names), + function(cur_m) { + return(fromJSON(file.path(cur_m, ".zattrs"))) + }) + + cur_channels <- lapply(cur_meta, function(cur_m){ if ("omero" %in% names(cur_meta)) { - cur_channels <- cur_meta$omero$channels$label + return(cur_meta$omero$channels$label) } else if ("channels_metadata" %in% names(cur_meta)) { - cur_channels <- cur_meta$channels_metadata$channels$label + return(cur_meta$channels_metadata$channels$label) } + }) + + if (length(unique(cur_channels)) > 1) { + stop("Channel names need to match across images.") } - cur_out <- lapply(cur_names, function(cur_name){ - - # Create index - cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) - cur_dim <- cur_zarr$dim[[1]] - - if (length(cur_dim) == 3) { - cur_ind <- list(c, y, x) - } else if (length(cur_dim) == 4) { - cur_ind <- list(z, c, y, x) - } else if (length(cur_dim) == 5) { - cur_ind <- list(t, z, c, y, x) - } - - cur_arr <- read_zarr_array(cur_name, index = cur_ind) - cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) - - return(cur_arr) - }) + } else if (type == "omengff") { + if (is.null(resolution)) { + resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) + resolution <- resolution[length(resolution)] + } + + fov_names <- sub("\\.[^.]*$", "", basename(file)) - names(cur_out) <- fov_names + cur_names <- file.path(file, resolution) - cur_CIL <- CytoImageList(cur_out) + cur_meta <- fromJSON(file.path(file, ".zattrs")) + + if ("omero" %in% names(cur_meta)) { + cur_channels <- cur_meta$omero$channels$label + } else if ("channels_metadata" %in% names(cur_meta)) { + cur_channels <- cur_meta$channels_metadata$channels$label + } + } + + cur_out <- lapply(cur_names, function(cur_name){ - # Set channelNames - if (exists("cur_channels")) { - channelNames(cur_CIL) <- cur_channels - } else { - channelNames(cur_CIL) <- as.character(seq_len(dim(cur_CIL[[1]])[3])) + # Create index + cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) + cur_dim <- cur_zarr$dim[[1]] + + if (length(cur_dim) == 3) { + cur_ind <- list(c, y, x) + } else if (length(cur_dim) == 4) { + cur_ind <- list(z, c, y, x) + } else if (length(cur_dim) == 5) { + cur_ind <- list(t, z, c, y, x) } - return(cur_CIL) - } else if (what == "masks") { + cur_arr <- read_zarr_array(cur_name, index = cur_ind) + cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) + return(cur_arr) + }) + + names(cur_out) <- fov_names + + cur_CIL <- CytoImageList(cur_out) + + # Set channelNames + if (exists("cur_channels")) { + channelNames(cur_CIL) <- cur_channels + } else { + channelNames(cur_CIL) <- as.character(seq_len(dim(cur_CIL[[1]])[3])) } - + + return(cur_CIL) } \ No newline at end of file From d46ac7d93a68b462ac0a4f6bd856f190d0d7cc16 Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 10:35:49 +0200 Subject: [PATCH 06/13] Read in axes from metadata --- R/ZarrReaders.R | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index df1e338..b3b2a9c 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -69,10 +69,10 @@ readImagesFromZARR <- function(file, }) cur_channels <- lapply(cur_meta, function(cur_m){ - if ("omero" %in% names(cur_meta)) { - return(cur_meta$omero$channels$label) - } else if ("channels_metadata" %in% names(cur_meta)) { - return(cur_meta$channels_metadata$channels$label) + if ("omero" %in% names(cur_m)) { + return(cur_m$omero$channels$label) + } else if ("channels_metadata" %in% names(cur_m)) { + return(cur_m$channels_metadata$channels$label) } }) @@ -80,6 +80,22 @@ readImagesFromZARR <- function(file, stop("Channel names need to match across images.") } + cur_channels <- unlist(unique(cur_channels)) + + cur_index <- lapply(cur_meta, function(cur_m){ + if ("coordinateTransformations" %in% names(cur_m$multiscales)) { + return(cur_m$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name) + } else { + return(cur_m$multiscales$axes[[1]]$name) + } + }) + + if (length(unique(cur_index)) > 1) { + stop("Dimensions need to match across images.") + } + + cur_index <- unlist(unique(cur_index)) + } else if (type == "omengff") { if (is.null(resolution)) { resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) @@ -97,6 +113,12 @@ readImagesFromZARR <- function(file, } else if ("channels_metadata" %in% names(cur_meta)) { cur_channels <- cur_meta$channels_metadata$channels$label } + + if ("coordinateTransformations" %in% names(cur_meta$multiscales)) { + return(cur_meta$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name) + } else { + return(cur_meta$multiscales$axes[[1]]$name) + } } cur_out <- lapply(cur_names, function(cur_name){ @@ -105,13 +127,9 @@ readImagesFromZARR <- function(file, cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) cur_dim <- cur_zarr$dim[[1]] - if (length(cur_dim) == 3) { - cur_ind <- list(c, y, x) - } else if (length(cur_dim) == 4) { - cur_ind <- list(z, c, y, x) - } else if (length(cur_dim) == 5) { - cur_ind <- list(t, z, c, y, x) - } + cur_ind <- list(t, c, z, y, x) + names(cur_ind) <- c("t", "c", "z", "y", "x") + cur_ind <- cur_ind[cur_index] cur_arr <- read_zarr_array(cur_name, index = cur_ind) cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) From e06ca448820883f8a9e24a8df8b829767212d15c Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 10:41:58 +0200 Subject: [PATCH 07/13] Removed unneded code --- R/ZarrReaders.R | 4 ---- 1 file changed, 4 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index b3b2a9c..8ebbd5b 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -123,10 +123,6 @@ readImagesFromZARR <- function(file, cur_out <- lapply(cur_names, function(cur_name){ - # Create index - cur_zarr <- zarr_overview(cur_name, as_data_frame = TRUE) - cur_dim <- cur_zarr$dim[[1]] - cur_ind <- list(t, c, z, y, x) names(cur_ind) <- c("t", "c", "z", "y", "x") cur_ind <- cur_ind[cur_index] From 515922c438c938d619ce8f3b042242717b8dce70 Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 11:53:00 +0200 Subject: [PATCH 08/13] Fixed bugs in readImagesFromZARR for spatialData --- NAMESPACE | 4 +++- R/ZarrReaders.R | 7 ++++--- man/{readZARR.Rd => readImagesFromZARR.Rd} | 9 +++------ vignettes/cytomapper_zarr.Rmd | 17 +++++++++++------ 4 files changed, 21 insertions(+), 16 deletions(-) rename man/{readZARR.Rd => readImagesFromZARR.Rd} (87%) diff --git a/NAMESPACE b/NAMESPACE index 5dbff38..c9d507d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,7 +14,7 @@ export(measureObjects) export(mergeChannels) export(plotCells) export(plotPixels) -export(readZARR) +export(readImagesFromZARR) export(scaleImages) exportClasses(CytoImageList) exportMethods("[<-") @@ -64,6 +64,8 @@ importFrom(EBImage,readImage) importFrom(HDF5Array,HDF5Array) importFrom(HDF5Array,writeHDF5Array) importFrom(RColorBrewer,brewer.pal) +importFrom(Rarr,read_zarr_array) +importFrom(Rarr,zarr_overview) importFrom(S4Vectors,"mcols<-") importFrom(S4Vectors,DataFrame) importFrom(S4Vectors,SimpleList) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 8ebbd5b..6821eb6 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -1,12 +1,11 @@ #' @title Read data from ZARR files -#' @name readZARR +#' @name readImagesFromZARR #' #' @description #' Reads in data from a ZARR file #' #' @param file TODO #' @param type TODO -#' @param what TODO #' @param resolution TODO #' @param x TODO #' @param y TODO @@ -39,7 +38,6 @@ readImagesFromZARR <- function(file, #.valid.readZARR.input(file) type <- match.arg(type) - what <- match.arg(what) if (type == "spatialdata") { if (is.null(fov_names)) { @@ -121,6 +119,9 @@ readImagesFromZARR <- function(file, } } + message("Resolution of the image: ", resolution) + message("Channels of the image: ", cur_channels) + cur_out <- lapply(cur_names, function(cur_name){ cur_ind <- list(t, c, z, y, x) diff --git a/man/readZARR.Rd b/man/readImagesFromZARR.Rd similarity index 87% rename from man/readZARR.Rd rename to man/readImagesFromZARR.Rd index bc13b82..de665be 100644 --- a/man/readZARR.Rd +++ b/man/readImagesFromZARR.Rd @@ -1,13 +1,12 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/ZarrReaders.R -\name{readZARR} -\alias{readZARR} +\name{readImagesFromZARR} +\alias{readImagesFromZARR} \title{Read data from ZARR files} \usage{ -readZARR( +readImagesFromZARR( file, type = c("omengff", "spatialdata"), - what = c("images", "masks"), resolution = NULL, x = NULL, y = NULL, @@ -22,8 +21,6 @@ readZARR( \item{type}{TODO} -\item{what}{TODO} - \item{resolution}{TODO} \item{x}{TODO} diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index 20fb96c..c215755 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -28,14 +28,19 @@ library(BiocStyle) ```{r library, echo=FALSE} library(cytomapper) +library(zellkonverter) ``` -# Zarr +# SpatialData ```{r} -images <- readZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", - type = "spatialdata", what = "images") +images <- readImagesFromZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", + type = "spatialdata") -#images <- readZARR("~/Desktop/5514375.zarr/", -# type = "omengff", what = "images") -``` \ No newline at end of file +images <- readImagesFromZARR("https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr", + type = "omengff") + +``` + + +# OME NGFF \ No newline at end of file From deada1587538222b3078d8670fbdc46100bb553e Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 13:24:27 +0200 Subject: [PATCH 09/13] Read metadata from zmetadata --- R/ZarrReaders.R | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 6821eb6..52037cc 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -40,16 +40,23 @@ readImagesFromZARR <- function(file, type <- match.arg(type) if (type == "spatialdata") { + + cur_meta <- fromJSON(file.path(file, "zmetadata")) + cur_img_meta <- str_split(names(cur_meta$metadata), "/", simplify = TRUE) + cur_img_meta <- cur_img_meta[cur_img_meta[,1] == "images" & !grepl("^\\.", cur_img_meta[,2]),] + cur_fov_names <- unique(cur_img_meta[,2]) + if (is.null(fov_names)) { - fov_names <- list.files(file.path(file, "images"))[1] + fov_names <- cur_fov_names[1] } if (is.null(resolution)) { if (length(fov_names) > 1) { resolution <- lapply(file.path(file, "images", fov_names), function(cur_name) { - cur_res <- list.files(cur_name) - return(cur_res[length(cur_res)]) + cur_res <- fromJSON(file.path(cur_name, ".zattrs")) + cur_res <- cur_res$multiscales$datasets[[1]]$path + return(sort(cur_res)[length(cur_res)]) }) resolution <- unlist(resolution) } else { From 007c0cb13e346186763512df1eb144b2afac7144 Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 13:48:23 +0200 Subject: [PATCH 10/13] Get right resolution --- R/ZarrReaders.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 52037cc..5302c7a 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -60,8 +60,9 @@ readImagesFromZARR <- function(file, }) resolution <- unlist(resolution) } else { - resolution <- list.files(file.path(file, "images", fov_names)) - resolution <- resolution[length(resolution)] + resolution <- fromJSON(file.path(file, "images", fov_names, ".zattrs")) + resolution <- cur_res$multiscales$datasets[[1]]$path + resolution <- sort(resolution)[length(resolution)] } } From 9851b77fad7d27966a7f572fc0e192f1ab14e916 Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 13:51:44 +0200 Subject: [PATCH 11/13] OMENGFF get resolution from file --- R/ZarrReaders.R | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 5302c7a..e417a09 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -61,7 +61,7 @@ readImagesFromZARR <- function(file, resolution <- unlist(resolution) } else { resolution <- fromJSON(file.path(file, "images", fov_names, ".zattrs")) - resolution <- cur_res$multiscales$datasets[[1]]$path + resolution <- resolution$multiscales$datasets[[1]]$path resolution <- sort(resolution)[length(resolution)] } } @@ -103,9 +103,12 @@ readImagesFromZARR <- function(file, cur_index <- unlist(unique(cur_index)) } else if (type == "omengff") { + + cur_meta <- fromJSON(file.path(file, ".zattrs")) + if (is.null(resolution)) { - resolution <- list.files(file, pattern = paste(seq(0, 10), collapse = "|")) - resolution <- resolution[length(resolution)] + resolution <- cur_meta$multiscales$datasets[[1]]$path + resolution <- sort(resolution)[length(resolution)] } fov_names <- sub("\\.[^.]*$", "", basename(file)) From 6b560b90590d24216ea86771f35b1dfead4fc3cd Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 15:31:55 +0200 Subject: [PATCH 12/13] Finalized reader function --- NAMESPACE | 1 + R/ZarrReaders.R | 14 +++++++++----- vignettes/cytomapper_zarr.Rmd | 16 +++++++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index c9d507d..dfbe136 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -111,6 +111,7 @@ importFrom(raster,as.raster) importFrom(rhdf5,h5delete) importFrom(rhdf5,h5ls) importFrom(stats,quantile) +importFrom(stringr,str_split) importFrom(svgPanZoom,renderSvgPanZoom) importFrom(svgPanZoom,svgPanZoom) importFrom(svgPanZoom,svgPanZoomOutput) diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index e417a09..5584b6c 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -24,6 +24,7 @@ #' @export #' @importFrom jsonlite fromJSON #' @importFrom Rarr read_zarr_array zarr_overview +#' @importFrom stringr str_split readImagesFromZARR <- function(file, type = c("omengff", "spatialdata"), resolution = NULL, @@ -115,8 +116,6 @@ readImagesFromZARR <- function(file, cur_names <- file.path(file, resolution) - cur_meta <- fromJSON(file.path(file, ".zattrs")) - if ("omero" %in% names(cur_meta)) { cur_channels <- cur_meta$omero$channels$label } else if ("channels_metadata" %in% names(cur_meta)) { @@ -124,9 +123,9 @@ readImagesFromZARR <- function(file, } if ("coordinateTransformations" %in% names(cur_meta$multiscales)) { - return(cur_meta$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name) + cur_index <- cur_meta$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name } else { - return(cur_meta$multiscales$axes[[1]]$name) + cur_index <- cur_meta$multiscales$axes[[1]]$name } } @@ -139,8 +138,13 @@ readImagesFromZARR <- function(file, names(cur_ind) <- c("t", "c", "z", "y", "x") cur_ind <- cur_ind[cur_index] + cur_perm <- c("x", "y", "c", "z", "t") + cur_perm <- match(cur_perm, cur_index) + cur_perm <- cur_perm[!is.na(cur_perm)] + cur_arr <- read_zarr_array(cur_name, index = cur_ind) - cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) + cur_arr <- aperm(cur_arr, cur_perm) + cur_arr <- Image(array(cur_arr, dim = dim(cur_arr)[seq_len(3)])) return(cur_arr) }) diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index c215755..9ac33b7 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -33,14 +33,24 @@ library(zellkonverter) # SpatialData -```{r} +```{r, eval=FALSE} images <- readImagesFromZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", type = "spatialdata") +images <- readImagesFromZARR("https://dl01.irc.ugent.be/spatial/mibitof/data.zarr/", + type = "spatialdata") + +``` + +# OME NGFF + +```{r,eval=FALSE} images <- readImagesFromZARR("https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr", type = "omengff") -``` +images <- readImagesFromZARR("~/Desktop/5514375.zarr/", + type = "omengff", z = 10, t = 10) +plotPixels(images, colour_by = c("NCAPD2", "DNA", "NEG_Dextran")) +``` -# OME NGFF \ No newline at end of file From ec4be86ed37bfa4172ed2b6a3754b32995e30ba6 Mon Sep 17 00:00:00 2001 From: nilseling Date: Tue, 19 Sep 2023 16:35:49 +0200 Subject: [PATCH 13/13] First working version --- NAMESPACE | 1 + R/ZarrReaders.R | 136 ++++++++++++++++++++++++++++++++++ man/readMasksFromZARR.Rd | 50 +++++++++++++ vignettes/cytomapper_zarr.Rmd | 34 ++++++++- 4 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 man/readMasksFromZARR.Rd diff --git a/NAMESPACE b/NAMESPACE index dfbe136..112c8ad 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,6 +15,7 @@ export(mergeChannels) export(plotCells) export(plotPixels) export(readImagesFromZARR) +export(readMasksFromZARR) export(scaleImages) exportClasses(CytoImageList) exportMethods("[<-") diff --git a/R/ZarrReaders.R b/R/ZarrReaders.R index 5584b6c..f9d18b5 100644 --- a/R/ZarrReaders.R +++ b/R/ZarrReaders.R @@ -160,5 +160,141 @@ readImagesFromZARR <- function(file, channelNames(cur_CIL) <- as.character(seq_len(dim(cur_CIL[[1]])[3])) } + return(cur_CIL) +} + +#' @title Read data from ZARR files +#' @name readMasksFromZARR +#' +#' @description +#' Reads in data from a ZARR file +#' +#' @param file TODO +#' @param type TODO +#' @param resolution TODO +#' @param x TODO +#' @param y TODO +#' @param c TODO +#' @param z TODO +#' @param t TODO +#' @param fov_names TODO +#' +#' @return A \linkS4class{CytoImageList} object +#' +#' @examples +#' # TODO +#' +#' @author Nils Eling (\email{nils.eling@@dqbm.uzh.ch}), +#' +#' @export +#' @importFrom jsonlite fromJSON +#' @importFrom Rarr read_zarr_array zarr_overview +#' @importFrom stringr str_split +readMasksFromZARR <- function(file, + type = c("omengff", "spatialdata"), + resolution = NULL, + x = NULL, + y = NULL, + c = NULL, + z = 1, + t = 1, + fov_names = NULL) { + + # Check if file is spatialData or OME-NGFF + #.valid.readZARR.input(file) + + type <- match.arg(type) + + if (type == "spatialdata") { + + cur_meta <- fromJSON(file.path(file, "zmetadata")) + cur_points_meta <- str_split(names(cur_meta$metadata), "/", simplify = TRUE) + cur_points_meta <- cur_points_meta[cur_points_meta[,1] == "labels" & !grepl("^\\.", cur_points_meta[,2]),] + + cur_fov_names <- unique(cur_points_meta[,2]) + + if (is.null(fov_names)) { + fov_names <- cur_fov_names[1] + } + + if (is.null(resolution)) { + if (length(fov_names) > 1) { + resolution <- lapply(file.path(file, "labels", fov_names), + function(cur_name) { + cur_res <- fromJSON(file.path(cur_name, ".zattrs")) + cur_res <- cur_res$multiscales$datasets[[1]]$path + return(sort(cur_res)[length(cur_res)]) + }) + + resolution <- unlist(resolution) + } else { + resolution <- fromJSON(file.path(file, "labels", fov_names, ".zattrs")) + resolution <- resolution$multiscales$datasets[[1]]$path + resolution <- sort(resolution)[length(resolution)] + } + } + + cur_names <- file.path(file, "labels", fov_names, resolution) + + # Read in metadata + cur_meta <- lapply(file.path(file, "labels", fov_names), + function(cur_m) { + return(fromJSON(file.path(cur_m, ".zattrs"))) + }) + + cur_index <- lapply(cur_meta, function(cur_m){ + if ("coordinateTransformations" %in% names(cur_m$multiscales)) { + return(cur_m$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name) + } else { + return(cur_m$multiscales$axes[[1]]$name) + } + }) + + if (length(unique(cur_index)) > 1) { + stop("Dimensions need to match across images.") + } + + cur_index <- unlist(unique(cur_index)) + + } else if (type == "omengff") { + + if (is.null(resolution)) { + cur_res <- fromJSON(file.path(file, "labels/Cell/.zattrs")) + cur_res <- cur_res$multiscales$datasets[[1]]$path + resolution <- cur_res[length(cur_res)] + resolution <- unlist(resolution) + } + + fov_names <- sub("\\.[^.]*$", "", basename(file)) + + cur_names <- file.path(file, "labels/Cell", resolution) + + cur_meta <- fromJSON(file.path(file, "labels/Cell/.zattrs")) + + cur_index <- if ("coordinateTransformations" %in% names(cur_meta$multiscales)) { + cur_meta$multiscales$coordinateTransformations[[1]]$input$axes[[1]]$name + } else { + cur_meta$multiscales$axes[[1]]$name + } + } + + message("Resolution of the image: ", resolution) + + cur_out <- lapply(cur_names, function(cur_name){ + cur_ind <- list(t, c, z, y, x) + names(cur_ind) <- c("t", "c", "z", "y", "x") + cur_ind <- cur_ind[cur_index] + + cur_arr <- read_zarr_array(cur_name, index = cur_ind) + + cur_arr <- Image(aperm(cur_arr, rev(seq_along(dim(cur_arr))))) + + return(cur_arr) + }) + + names(cur_out) <- fov_names + + cur_CIL <- CytoImageList(cur_out) + return(cur_CIL) } \ No newline at end of file diff --git a/man/readMasksFromZARR.Rd b/man/readMasksFromZARR.Rd new file mode 100644 index 0000000..33a378e --- /dev/null +++ b/man/readMasksFromZARR.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ZarrReaders.R +\name{readMasksFromZARR} +\alias{readMasksFromZARR} +\title{Read data from ZARR files} +\usage{ +readMasksFromZARR( + file, + type = c("omengff", "spatialdata"), + resolution = NULL, + x = NULL, + y = NULL, + c = NULL, + z = 1, + t = 1, + fov_names = NULL +) +} +\arguments{ +\item{file}{TODO} + +\item{type}{TODO} + +\item{resolution}{TODO} + +\item{x}{TODO} + +\item{y}{TODO} + +\item{c}{TODO} + +\item{z}{TODO} + +\item{t}{TODO} + +\item{fov_names}{TODO} +} +\value{ +A \linkS4class{CytoImageList} object +} +\description{ +Reads in data from a ZARR file +} +\examples{ +# TODO + +} +\author{ +Nils Eling (\email{nils.eling@dqbm.uzh.ch}), +} diff --git a/vignettes/cytomapper_zarr.Rmd b/vignettes/cytomapper_zarr.Rmd index 9ac33b7..cfcb472 100644 --- a/vignettes/cytomapper_zarr.Rmd +++ b/vignettes/cytomapper_zarr.Rmd @@ -28,6 +28,7 @@ library(BiocStyle) ```{r library, echo=FALSE} library(cytomapper) +library(basilisk) library(zellkonverter) ``` @@ -37,20 +38,45 @@ library(zellkonverter) images <- readImagesFromZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", type = "spatialdata") -images <- readImagesFromZARR("https://dl01.irc.ugent.be/spatial/mibitof/data.zarr/", +masks <- readMasksFromZARR("~/Github/spatialdata-notebooks/notebooks/developers_resources/storage_format/multiple_elements.zarr/", type = "spatialdata") +mcols(images) <- mcols(masks) <- DataFrame(image = names(images)) + +plotPixels(images, mask = masks, colour_by = c("0", "1", "2"), img_id = "image") + +images <- readImagesFromZARR("https://dl01.irc.ugent.be/spatial/mibitof/data.zarr/", + type = "spatialdata", resolution = "0") + +masks <- readMasksFromZARR("https://dl01.irc.ugent.be/spatial/mibitof/data.zarr/", + type = "spatialdata", resolution = "0") + +mcols(images) <- mcols(masks) <- DataFrame(image = names(images)) + +plotPixels(images, mask = masks, colour_by = c("0", "1", "2"), img_id = "image") ``` # OME NGFF ```{r,eval=FALSE} images <- readImagesFromZARR("https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr", - type = "omengff") + type = "omengff", resolution = "0", z = 2, t = 1) + +masks <- readMasksFromZARR("https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr", + type = "omengff", resolution = "0", z = 2, t = 1) + +mcols(images) <- mcols(masks) <- DataFrame(image = names(images)) + +plotPixels(images, mask = masks, img_id = "image", colour_by = c("NCAPD2", "DNA", "NEG_Dextran")) images <- readImagesFromZARR("~/Desktop/5514375.zarr/", - type = "omengff", z = 10, t = 10) + type = "omengff", resolution = "0", z = 3, t = 1) + +masks <- readMasksFromZARR("~/Desktop/5514375.zarr/", + type = "omengff", resolution = "0", z = 3, t = 1) + +mcols(images) <- mcols(masks) <- DataFrame(image = names(images)) -plotPixels(images, colour_by = c("NCAPD2", "DNA", "NEG_Dextran")) +plotPixels(images, mask = masks, img_id = "image", colour_by = c("NCAPD2", "DNA", "NEG_Dextran")) ```