From f0593104feab5da43891a50e228f1d0f1ecd7570 Mon Sep 17 00:00:00 2001
From: HelenaLC
Date: Mon, 25 Nov 2024 22:10:23 +0100
Subject: [PATCH 1/9] add 'lim' and 'sat'
---
inst/NEWS | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/inst/NEWS b/inst/NEWS
index 74e32f1..39ed12c 100644
--- a/inst/NEWS
+++ b/inst/NEWS
@@ -1,6 +1,7 @@
changes in version 0.99.2
-- in 'plotImage', added support to visualize channels of choice
+- in 'plotImage', added support to visualize channels of choice, as well as
+ 'lim' and 'sat' to control constrast limits and saturation, repsectively
- updated vignette to include corresponding examples
changes in version 0.99.1
From 92489864cd4bc6fdeb4d225b6066a9d2f92aad52 Mon Sep 17 00:00:00 2001
From: HelenaLC
Date: Mon, 25 Nov 2024 22:10:39 +0100
Subject: [PATCH 2/9] R/plotImage.R
---
man/plotImage.Rd | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/man/plotImage.Rd b/man/plotImage.Rd
index 3f11df9..0721818 100644
--- a/man/plotImage.Rd
+++ b/man/plotImage.Rd
@@ -8,7 +8,16 @@
\usage{
plotSpatialData()
-\S4method{plotImage}{SpatialData}(x, i = 1, j = 1, k = NULL, ch = NULL, c = NULL)
+\S4method{plotImage}{SpatialData}(
+ x,
+ i = 1,
+ j = 1,
+ k = NULL,
+ ch = NULL,
+ c = NULL,
+ lim = NULL,
+ sat = NULL
+)
}
\arguments{
\item{x}{\code{\link{SpatialData}} object.}
@@ -25,7 +34,14 @@ scale in order to minimize memory-usage and blurring for a target size of
the first channel(s) available); use \code{channels()} to see
which channels are available for a given \code{ImageArray}}
-\item{c}{plotting aesthetics; color}
+\item{c}{character vector; colors to use for each channel.}
+
+\item{lim}{list of length-2 (non-negative) numeric vectors;
+contrast limits for each channel - defaults to [0, 1] for all.}
+
+\item{sat}{(non-negative) numeric vector;
+saturation of each channel - defaults to 1 for all
+(note: \code{sat=2} is equivalent to \code{lim=c(0, 0.5)})}
}
\value{
ggplot
From 0959ccfa35fc8c756525d22645f655c0586d3b40 Mon Sep 17 00:00:00 2001
From: HelenaLC
Date: Mon, 25 Nov 2024 22:11:17 +0100
Subject: [PATCH 3/9] +lim/sat to control contrast limits/saturation
---
R/plotImage.R | 46 ++++++++++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/R/plotImage.R b/R/plotImage.R
index 63a1672..8b853ff 100644
--- a/R/plotImage.R
+++ b/R/plotImage.R
@@ -12,9 +12,13 @@
#' 800 x 800px; use Inf to plot the lowest resolution available.
#' @param ch image channel(s) to be used for plotting (defaults to
#' the first channel(s) available); use \code{channels()} to see
-#' which channels are available for a given \code{ImageArray}
-#'
-#' @param c plotting aesthetics; color
+#' which channels are available for a given \code{ImageArray}
+#' @param c character vector; colors to use for each channel.
+#' @param lim list of length-2 (non-negative) numeric vectors;
+#' contrast limits for each channel - defaults to [0, 1] for all.
+#' @param sat (non-negative) numeric vector;
+#' saturation of each channel - defaults to 1 for all
+#' (note: \code{sat=2} is equivalent to \code{lim=c(0, 0.5)})
#'
#' @return ggplot
#'
@@ -40,7 +44,9 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
# if no colors and channels defined, return the first channel
#' @importFrom grDevices col2rgb
#' @noRd
-.manage_channels <- \(a, ch, c=NULL){
+.manage_channels <- \(a, ch, c=NULL, lim=NULL, sat=NULL) {
+ if (is.null(lim)) lim <- replicate(dim(a)[1], c(0, 1), FALSE)
+ if (is.null(sat)) sat <- rep(1, dim(a)[1])
if (length(ch) > (n <- length(.DEFAULT_COLORS)) && is.null(c))
stop("Only ", n, " default colors available, but",
length(ch), " are needed; please specify 'c'")
@@ -49,9 +55,12 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
c <- col2rgb(c)/255
b <- array(0, dim=c(3, dim(a)[-1]))
for (i in seq_len(dim(a)[1])) {
- b[1,,] <- b[1,,,drop=FALSE] + a[i,,,drop=FALSE]*c[1,i]
- b[2,,] <- b[2,,,drop=FALSE] + a[i,,,drop=FALSE]*c[2,i]
- b[3,,] <- b[3,,,drop=FALSE] + a[i,,,drop=FALSE]*c[3,i]
+ b[1,,] <- b[1,,,drop=FALSE] + a[i,,,drop=FALSE]*c[1,i]*(1/lim[[i]][2])*sat[i]
+ b[2,,] <- b[2,,,drop=FALSE] + a[i,,,drop=FALSE]*c[2,i]*(1/lim[[i]][2])*sat[i]
+ b[3,,] <- b[3,,,drop=FALSE] + a[i,,,drop=FALSE]*c[3,i]*(1/lim[[i]][2])*sat[i]
+ b[1,,][b[1,,] < lim[[i]][1]] <- 0
+ b[2,,][b[2,,] < lim[[i]][1]] <- 0
+ b[3,,][b[3,,] < lim[[i]][1]] <- 0
}
a <- pmin(b, 1)
} else {
@@ -72,13 +81,14 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
# normalize the image data given its data type
#' @noRd
-.normalize_image_array <- \(a, dt){
- if (dt %in% names(.DTYPE_MAX_VALUES)) {
- a <- a/.DTYPE_MAX_VALUES[dt]
- } else if (max(a) > 1) {
- for (i in seq_len(dim(a)[1]))
- a[i,,] <- a[i,,]/max(a[i,,])
- }
+.normalize_image_array <- \(a, dt) {
+ d <- dim(a)[1]
+ if (dt %in% names(.DTYPE_MAX_VALUES)) {
+ a <- a / .DTYPE_MAX_VALUES[dt]
+ } else if (max(a) > 1) {
+ for (i in seq_len(d))
+ a[i,,] <- a[i,,] / max(a[i,,])
+ }
return(a)
}
@@ -132,7 +142,7 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
#' @importFrom methods as
#' @importFrom grDevices rgb
#' @importFrom DelayedArray realize
-.df_i <- \(x, k=NULL, ch=NULL, c=NULL) {
+.df_i <- \(x, k=NULL, ch=NULL, c=NULL, lim=NULL, sat=NULL) {
a <- .get_plot_data(x, k)
ch_i <- .ch_idx(x, ch)
if (!.is_rgb(x))
@@ -141,7 +151,7 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
a <- realize(as(a, "DelayedArray"))
a <- .normalize_image_array(a, dt)
if (!.is_rgb(x))
- a <- .manage_channels(a, ch_i, c)
+ a <- .manage_channels(a, ch_i, c, lim, sat)
apply(a, c(2, 3), \(.) do.call(rgb, as.list(.)))
}
@@ -163,13 +173,13 @@ plotSpatialData <- \() ggplot() + scale_y_reverse() + .theme
#' @rdname plotImage
#' @export
-setMethod("plotImage", "SpatialData", \(x, i=1, j=1, k=NULL, ch=NULL, c=NULL) {
+setMethod("plotImage", "SpatialData", \(x, i=1, j=1, k=NULL, ch=NULL, c=NULL, lim=NULL, sat=NULL) {
if (is.numeric(i))
i <- imageNames(x)[i]
y <- image(x, i)
if (is.numeric(j))
j <- CTname(y)[j]
- df <- .df_i(y, k, ch, c)
+ df <- .df_i(y, k, ch, c, lim, sat)
wh <- .get_wh(x, i, j)
.gg_i(df, wh$w, wh$h)
})
\ No newline at end of file
From 683ea4234881f7fd9bf3f489d476b2872ec914cb Mon Sep 17 00:00:00 2001
From: HelenaLC
Date: Mon, 25 Nov 2024 22:11:30 +0100
Subject: [PATCH 4/9] +contrasts/saturation examples
---
vignettes/SpatialData.plot.Rmd | 22 +++++++++++++-
vignettes/SpatialData.plot.html | 54 ++++++++++++++++++++++-----------
2 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/vignettes/SpatialData.plot.Rmd b/vignettes/SpatialData.plot.Rmd
index 1c0f4f4..c06bef3 100644
--- a/vignettes/SpatialData.plot.Rmd
+++ b/vignettes/SpatialData.plot.Rmd
@@ -279,12 +279,32 @@ x <- readSpatialData(pa, anndataR=FALSE)
Plotting with multiple image channels.
```{r steinbock-plot}
-plotSpatialData() + plotImage(x,
+plotSpatialData() + plotImage(x,
i="Patient3_003_image",
ch=c(6, 22, 39),
c=c("blue", "cyan", "yellow"))
```
+### aesthetics
+
+```{r saturation, fig.width=9, fig.height=3}
+p <- plotSpatialData()
+i <- image(x, "Patient3_003_image")
+image(x, "crop") <- i[, 200:400, 200:400]
+lapply(c(1, 0.7, 0.4), \(.) {
+ p + plotImage(x, "crop", sat=c(1.4, 1.2, .),
+ ch=c(6, 22, 39), c=c("blue", "cyan", "yellow"))
+}) |> wrap_plots(nrow=1)
+```
+
+```{r contrasts, fig.width=9, fig.height=3}
+lapply(list(c(0, 1), c(0.2, 1), c(0, 0.8)), \(.) {
+ p + plotImage(x, "crop",
+ lim=list(c(0, 1), c(0, 1), .),
+ ch=c(6, 22, 39), c=c("blue", "cyan", "yellow"))
+}) |> wrap_plots(nrow=1)
+```
+
# Masking
Back to blobs...
diff --git a/vignettes/SpatialData.plot.html b/vignettes/SpatialData.plot.html
index bb22a3b..6c9b7e5 100644
--- a/vignettes/SpatialData.plot.html
+++ b/vignettes/SpatialData.plot.html
@@ -704,7 +704,7 @@ SpatialData.plot
Helena Lucia Crowell, Louise Deconinck, Artür Manukyan, Dario Righelli, Estella Dong and Vince Carey
November 25, 2024
Package
-SpatialData.plot 0.99.1
+SpatialData.plot 0.99.2
@@ -726,7 +726,10 @@ Contents
3.2 VisiumHD
3.3 MibiTOF
3.4 CyCIF (MCMICRO)
-3.5 IMC (Steinbock)
+3.5 IMC (Steinbock)
+
4 Masking
5 Session info
@@ -800,7 +803,7 @@ Images
where k specifies the target scale. This also works for plotting:
wrap_plots(nrow=1, lapply(seq(3), \(.)
plotSpatialData() + plotImage(x, i=2, k=.)))
-
+
Labels
@@ -821,7 +824,7 @@
Labels
(a | b | c) +
plot_layout(guides="collect") &
theme(legend.position="bottom")
-

+

Points
@@ -837,7 +840,7 @@
Points
b <- p + plotPoint(y, i, "letters") # discrete coloring
c <- p + plotPoint(y, i, "numbers") # continuous coloring
a | b | c
-

+

Shapes
@@ -852,7 +855,7 @@
Shapes
ggtitle("circles") +
plotShape(x, "blobs_circles")
wrap_plots(a, b, c)
-

+

Layering
@@ -874,7 +877,7 @@
Layering
p + plotShape(x, 3) + ggtitle("polygons"),
p + plotPoint(x, c="genes") + ggtitle("points"))
wrap_plots(c(list(all), one), nrow=2)
-

+

@@ -918,13 +921,13 @@
MERFISH
new_scale_color() +
plotShape(x, i="anatomical") +
scale_color_manual(values=hcl.colors(6, "Spectral"))
-

+

# bounding-box query
qu <- list(xmin=1800, xmax=2400, ymin=5000, ymax=5400)
bb <- geom_rect(do.call(aes, qu), data.frame(qu), col="yellow", fill=NA)
y <- SpatialData(images=list("."=do.call(query, c(list(x=image(x)), qu))))
plotSpatialData() + plotImage(x) + bb | plotSpatialData() + plotImage(y)
-

+

VisiumHD
@@ -951,7 +954,7 @@
VisiumHD
bb <- geom_rect(do.call(aes, qu), data.frame(qu), col="black", fill=NA)
y <- SpatialData(images=list("."=do.call(query, c(list(x=image(x)), qu))))
plotSpatialData() + plotImage(x) + bb | plotSpatialData() + plotImage(y)
-

+

MibiTOF
@@ -983,7 +986,7 @@
MibiTOF
plotSpatialData() + plotImage(x, .) +
plotLabel(x, ., "Cluster", pal=pal))) +
plot_layout(guides="collect")
-

+

CyCIF (MCMICRO)
@@ -1014,7 +1017,7 @@
CyCIF (MCMICRO)
plotSpatialData() + plotImage(x,
ch=c("DNA_6", "CD45", "CD57"),
c=c("blue", "cyan", "yellow"))
-

+
