diff --git a/DESCRIPTION b/DESCRIPTION index 978a26f..6436d3a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,7 @@ Package: ImageArray Type: Package Title: A framework for on-disk and in-memory image arrays -Version: 1.1.0 -Date: 2026-01-20 +Version: 1.1.2 Authors@R: person("Artür", "Manukyan", role=c("aut", "cre", "fnd"), @@ -13,7 +12,6 @@ Description: ImageArray provides a framework for on-disk and in-memory image Zarr and life sciences image file formats (OME Bio-Formats). License: MIT + file LICENSE Encoding: UTF-8 -RoxygenNote: 7.3.3 biocViews: Software, Visualization Depends: R (>= 4.5.0), @@ -42,3 +40,4 @@ Config/testthat/edition: 3 VignetteBuilder: knitr URL: https://github.com/BIMSBbioinfo/ImageArray BugReports: https://github.com/BIMSBbioinfo/ImageArray/issues +Config/roxygen2/version: 8.0.0 diff --git a/NEWS b/NEWS deleted file mode 100644 index 64718e4..0000000 --- a/NEWS +++ /dev/null @@ -1,14 +0,0 @@ -CHANGES IN VERSION ... -------------------------- - -NEW FEATURES - - o new features - -SIGNIFICANT USER-VISIBLE CHANGES - - o changes - -BUG FIXES - - o bug fixes diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..e485595 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,7 @@ +# ImageArray 1.1.2 + +## NEW FEATURES + +* Images with any combination of XYZCT dimensions are can now be read. + See OME webpage for more information: + https://docs.openmicroscopy.org/ome-model/6.2.2/ome-tiff/specification.html#dimensionorder \ No newline at end of file diff --git a/R/BFArray.R b/R/BFArray.R index 0b740b5..3807201 100644 --- a/R/BFArray.R +++ b/R/BFArray.R @@ -40,6 +40,14 @@ BFArray <- function(image.file, series, resolution) { len_meta <- lengths(meta.data@.Data) meta.data@.Data <- meta.data@.Data[which(len_meta > 0)] + # full image axes from metadata (always lowercase) + if( "coreMetadata" %in% names(meta.data)) { + axes <- meta.data$coreMetadata$dimensionOrder + } else if ("coreMetadata" %in% names(meta.data[[1]])) { + axes <- meta.data@.Data[[1]]$coreMetadata$dimensionOrder + } + axes <- tolower(unlist(strsplit(axes, ""))) + # get shape series_res_meta <- vapply( meta.data@.Data, @@ -58,7 +66,7 @@ BFArray <- function(image.file, series, resolution) { ) if (length(series_index) > 0) { shape <- vapply( - c("sizeX", "sizeY", "sizeC"), + sprintf("size%s", toupper(axes)), function(x) { md <- meta.data@.Data[[series_index]] if (!is.null(cm <- md$coreMetadata)) { @@ -69,6 +77,10 @@ BFArray <- function(image.file, series, resolution) { integer(1), USE.NAMES = FALSE ) + + names(shape) <- toupper(axes) + # remove dimensions with size 1 to mimic RBioFormats::read.image behavior + shape <- shape[shape > 1] seed <- BFArraySeed( filepath = image.file, @@ -100,7 +112,7 @@ BFArraySeed <- function(filepath, series, resolution, shape, type) { ### #' @describeIn BFArray-methods dim function for BFArray objects -setMethod("dim", "BFArraySeed", function(x) x@shape) +setMethod("dim", "BFArraySeed", function(x) unname(x@shape)) ### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ### type() getter @@ -120,11 +132,6 @@ setMethod("type", "BFArraySeed", function(x) x@type) stop("Please install RBioFormats: BiocManager::install('RBioFormats')") } - # check for index length - if (length(index) > 3) { - stop("You cannot get BFArray slices more than 2 dimensions!") - } - # create slices ind <- mapply( function(x, y) { @@ -147,10 +154,7 @@ setMethod("type", "BFArraySeed", function(x) x@type) res <- array(dim = len_ind) type(res) <- x@type } else { - subset_list <- list(X = ind[[1]], Y = ind[[2]]) - if (length(len_ind) == 3) { - subset_list <- c(subset_list, list(C = ind[[3]])) - } + subset_list <- setNames(ind , names(x@shape)) res <- RBioFormats::read.image( file = x@filepath, series = x@series, @@ -158,9 +162,7 @@ setMethod("type", "BFArraySeed", function(x) x@type) subset = subset_list ) res <- EBImage::imageData(res) - if (length(dim(res)) != 3) { - res <- array(res, dim = c(dim(res), 1)) - } + dim(res) <- len_ind } res diff --git a/R/ImageArray.R b/R/ImageArray.R index ae3cb01..ef89f3f 100644 --- a/R/ImageArray.R +++ b/R/ImageArray.R @@ -226,7 +226,7 @@ createBFArray <- function( image_list <- lapply(resolution, function(res) { BFArray(image, series = series, resolution = res) }) - ImageArray(meta = list(axes = c("x", "y", "c")), levels = image_list) + ImageArray(meta = list(axes = tolower(names(image_list[[1]]@seed@shape))), levels = image_list) } #' createMagickArray @@ -279,10 +279,13 @@ createMagickArray <- function( stop("'n.levels' has to be 1 or a larger integer value!") } - # create image levels + # get axes, EBImage accepts XY or XYC + axes <- c("c", "x", "y") if (verbose) { .img_create_msg(dim(image), 1) } + + # create image levels image_data <- magick::image_data(image, channels = "rgb") storage.mode(image_data) <- "integer" image_list <- list(DelayedArray::DelayedArray(as.array(image_data))) @@ -306,7 +309,7 @@ createMagickArray <- function( } # return - ImageArray(meta = list(axes = c("c", "x", "y")), levels = image_list) + ImageArray(meta = list(axes = axes), levels = image_list) } #' createMagickArray @@ -354,7 +357,7 @@ createEBImageArray <- function( # check dim .check_dim(image) - # create image levels + # get axes, EBImage accepts XY or XYC meta <- list(axes = c("x", "y", "c")) if (verbose) { .img_create_msg(dim_image, 1) @@ -363,6 +366,8 @@ createEBImageArray <- function( meta[["axes"]] <- meta[["axes"]][img_perm] img_perm <- stats::setNames(img_perm, meta[["axes"]]) img <- aperm(image, img_perm) + + # create image levels image_list <- list(DelayedArray::DelayedArray(img)) if (n.levels > 1) { cur_image <- image diff --git a/R/utils.R b/R/utils.R index aae545d..36defc0 100644 --- a/R/utils.R +++ b/R/utils.R @@ -224,4 +224,4 @@ is.sequential <- function(x) { #' @keywords internal #' @noRd -.AXES <- c("c", "y", "x") +.AXES <- c("c", "y", "x", "z", "t") diff --git a/inst/extdata/4D-series.ome.tiff b/inst/extdata/4D-series.ome.tiff new file mode 100644 index 0000000..de94451 Binary files /dev/null and b/inst/extdata/4D-series.ome.tiff differ diff --git a/man/ImageArray-methods.Rd b/man/ImageArray-methods.Rd index c8dc121..8fa2fae 100644 --- a/man/ImageArray-methods.Rd +++ b/man/ImageArray-methods.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ImageArray.R, R/conversion.R, R/manipulation.R, -% R/utils.R +% Please edit documentation in R/ImageArray.R, R/conversion.R, +% R/manipulation.R, R/utils.R \name{ImageArray-methods} \alias{ImageArray-methods} \alias{[[,ImageArray,numeric-method} diff --git a/tests/testthat/test-array.R b/tests/testthat/test-array.R index 88c29e2..c268680 100644 --- a/tests/testthat/test-array.R +++ b/tests/testthat/test-array.R @@ -42,7 +42,7 @@ test_that("check indexing (BFArray)", { imgarray <- createImageArray(img.file, series = 1, resolution = 1:2) # crop - imgarray_vis <- crop(imgarray, ind = list(100:200, 100:200, 1)) + imgarray_vis <- crop(imgarray, ind = list(100:200, 100:200)) imgarray_vis <- as.raster(imgarray_vis) plot(imgarray_vis) @@ -55,15 +55,15 @@ test_that("check indexing (BFArray)", { ) # [ method works - imgarray_vis <- imgarray[100:200,,] - expect_equal(dim(imgarray_vis), c(101, dim(imgarray)[2], 1)) - imgarray_vis <- imgarray[,100:200,] - expect_equal(dim(imgarray_vis), c(dim(imgarray)[1], 101, 1)) - imgarray_vis <- imgarray[,,] + imgarray_vis <- imgarray[100:200,] + expect_equal(dim(imgarray_vis), c(101, dim(imgarray)[2])) + imgarray_vis <- imgarray[,100:200] + expect_equal(dim(imgarray_vis), c(dim(imgarray)[1], 101)) + imgarray_vis <- imgarray[,] expect_equal(dim(imgarray_vis), dim(imgarray)) # [ indexing error - expect_error(imgarray[100:200,]) + expect_error(imgarray[,100:200,]) expect_error(imgarray[100:200,,2]) expect_error(imgarray[-100:200,,]) expect_error(imgarray[,-100,]) diff --git a/tests/testthat/test-bfarray.R b/tests/testthat/test-bfarray.R index e7c2595..b5d8cca 100644 --- a/tests/testthat/test-bfarray.R +++ b/tests/testthat/test-bfarray.R @@ -20,13 +20,13 @@ test_that("bfarray object", { # create array bfa <- BFArray(img.file, series = 1, resolution = 2) - expect_equal(dim(bfa), c(256, 256, 1)) + expect_equal(dim(bfa), c(256, 256)) bfa <- BFArray(img.file, series = 1, resolution = 1) - expect_equal(dim(bfa), c(512, 512, 1)) + expect_equal(dim(bfa), c(512, 512)) # methods - bfa2 <- aperm(bfa, c(2, 1, 3)) - expect_equal(bfa2[1, 2, 1], bfa[2, 1, 1]) + bfa2 <- aperm(bfa, c(2, 1)) + expect_equal(bfa2[1, 2], bfa[2, 1]) # construct imagearray img <- createImageArray(img.file, series = 1, resolution = 1:2) @@ -37,9 +37,9 @@ test_that("bfarray object", { # single channel modulate img_modulated <- modulate(img, brightness = 200) - orig <- realize(img[1:10, 1:10,]) * 2 + orig <- realize(img[1:10, 1:10]) * 2 orig[orig > 1] <- 1 - newmat <- realize(img_modulated[1:10, 1:10,]) + newmat <- realize(img_modulated[1:10, 1:10]) expect_equal(orig, newmat) }) diff --git a/tests/testthat/test-imagearray-class.R b/tests/testthat/test-imagearray-class.R index 6e9c7c8..0217289 100644 --- a/tests/testthat/test-imagearray-class.R +++ b/tests/testthat/test-imagearray-class.R @@ -8,7 +8,7 @@ test_that("image array class", { # incorrect axes names expect_error( - imgarray <- ImageArray(meta = list(axes = c("c", "z", "x")), + imgarray <- ImageArray(meta = list(axes = c("b", "a", "d")), levels = list(array(1:75, dim = c(3,5,5)))) ) diff --git a/vignettes/ImageArray.Rmd b/vignettes/ImageArray.Rmd index 210101f..d229a37 100644 --- a/vignettes/ImageArray.Rmd +++ b/vignettes/ImageArray.Rmd @@ -223,6 +223,22 @@ plot(bfa.raster) dim(bfa.raster) ``` +`r Biocpkg("ImageArray")` can also read images with all five dimensions (XYZCT) +available in the OME (Open Microscopy Environment) specifications. +See OME specifications webpage for more information [here](https://docs.openmicroscopy.org/ome-model/6.2.2/ome-tiff/specification.html#dimensionorder). + +```{r 4d, out.width="50%"} +ome.tiff.file <- system.file("extdata", "4D-series.ome.tiff", + package = "ImageArray") + +# read metadata +read.metadata(ome.tiff.file, proprietary.metadata = TRUE, filter.metadata = TRUE) + +# define ImageArray object +imgarray <- createImageArray(ome.tiff.file, series = 1, resolution = 1) +imgarray +``` + # Use cases The delayed pyramid scheme introduced by `ImageArray` objects can also