#' Augment all features involving locations
#'
#' All features containing the strings "Location_Center_X" or
#' "Location_Center_Y" are extended to include their membership to an ellipse (
#' concentric w.r.t. image center/well center), their membership to a tile (at
#' well and image level), the number of nonempty neighbor tiles, the type of
#' tile (inwards/outwards facing image border/edge) the distance to the image/
#' well center, the local object density to the image center. If features
#' labelled with "Location_Shifted_X" or "Location_Shifted_Y" are present
#' (augmentImageLocation), Those are extended too.
#'
#' @param x The PlateData/WellData/ImageData object of interest.
#' @param ellipse An integer corresponding to the number of ellipses.
#' @param facet A vector of length 2 e.g. c(14, 10) corresponding to the
#' number of bins in x-direction (14) and y-direction (10).
#' @param center.dist Logical, whether to include distances to image/well
#' centers.
#' @param density Logical, whether to estimate densities.
#'
#' @return The augmented version of the original object.
#'
#' @examples
#' data <- PlateData(PlateLocation("J101-2C"))
#' aug <- augmentCordinateFeatures(data, 5, c(14, 10), TRUE, TRUE)
#'
#' @export
augmentCordinateFeatures <- function(x, ...) {
UseMethod("augmentCordinateFeatures", x)
}
#' @export
augmentCordinateFeatures.PlateData <- function(x, ellipse=NULL, facet=NULL,
center.dist=FALSE,
density=FALSE) {
output <- augmentCordinateFeatures(x$data[[1]], ellipse, facet, center.dist,
density)
x$data <- llply(x$data, function(dat, ell, fac, cen, den) {
suppressMessages(res <- augmentCordinateFeatures(dat, ell, fac, cen, den))
return(res)
}, ellipse, facet, center.dist, density,
.progress=getOption("singleCellFeatures.progressBars"))
return(x)
}
#' @export
augmentCordinateFeatures.WellData <- function(x, ellipse=NULL, facet=NULL,
center.dist=FALSE,
density=FALSE) {
output <- augmentCordinateFeatures(x$data[[1]], ellipse, facet, center.dist,
density)
suppressMessages(x$data <- lapply(x$data, augmentCordinateFeatures, ellipse,
facet, center.dist, density))
return(x)
}
#' @export
augmentCordinateFeatures.ImageData <- function(x, ellipse=NULL, facet=NULL,
center.dist=FALSE,
density=FALSE) {
singleEllipse <- function(ind, x, y, cent, elli) {
sum <- (x[,ind] - cent[1])^2 / (elli[1])^2 +
(y[,ind] - cent[2])^2 / (elli[2])^2
return(1 - (sum <= 1))
}
multipleEllipses <- function(ind, x, y, cent, n.ell, elli) {
sum <- apply(elli, 2, function(e, x, y, cent) {
return((x[,ind] - cent[1])^2 / (e[1])^2 +
(y[,ind] - cent[2])^2 / (e[2])^2)
}, x, y, cent)
if(is.null(nrow(sum))) {
subtr <- sum(sapply(sum, function(row) row <= 1))
} else {
subtr <- rowSums(apply(sum, 2, function(row) row <= 1))
}
res <- n.ell - subtr
return(res)
}
facetCalc <- function(ind, x, fac) {
return(ceiling(x[,ind] / fac))
}
facetType <- function(ind, x, y, facet, n.img, img) {
res <- rep(0, length(x[,ind]))
if(img == 1) {
res <- res + (x[,ind] == facet[1]) # inward boundary
res <- res + (y[,ind] == 1) # inward boundary
res <- res + 3 * (x[,ind] == 1) # outward boundary
res <- res + 3 * (y[,ind] == facet[2]) # outward boundary
res <- res - (x[,ind] == 1 & y[,ind] == facet[2]) # outward corner
} else if(img == 2) {
res <- res + (y[,ind] == 1) # inward boundary
res <- res + (x[,ind] == facet[1]) # inward boundary
res <- res + (x[,ind] == 1) # inward boundary
res <- res + 3 * (y[,ind] == facet[2]) # outward boundary
} else if(img == 3) {
res <- res + (x[,ind] == 1) # inward boundary
res <- res + (y[,ind] == 1) # inward boundary
res <- res + 3 * (x[,ind] == facet[1]) # outward boundary
res <- res + 3 * (y[,ind] == facet[2]) # outward boundary
res <- res - (x[,ind] == facet[1] & y[,ind] == facet[2])# outward corner
} else if(img == 4 & n.img == 9) {
res <- res + (y[,ind] == facet[2]) # inward boundary
res <- res + (x[,ind] == facet[1]) # inward boundary
res <- res + (y[,ind] == 1) # inward boundary
res <- res + 3 * (x[,ind] == 1) # outward boundary
} else if(img == 5 & n.img == 9) {
res <- res + (y[,ind] == 1 | y[,ind] == facet[2]) # inward boundary
res <- res + (x[,ind] == 1 | x[,ind] == facet[1]) # inward boundary
} else if(img == 6 & n.img == 9) {
res <- res + (y[,ind] == facet[2]) # inward boundary
res <- res + (x[,ind] == 1) # inward boundary
res <- res + (y[,ind] == 1) # inward boundary
res <- res + 3 * (x[,ind] == facet[1]) # outward boundary
} else if(img == (n.img - 2)) {
res <- res + (x[,ind] == facet[1]) # inward boundary
res <- res + (y[,ind] == facet[2]) # inward boundary
res <- res + 3 * (x[,ind] == 1) # outward boundary
res <- res + 3 * (y[,ind] == 1) # outward boundary
res <- res - (x[,ind] == 1 & y[,ind] == 1) # outward corner
} else if(img == (n.img - 1)) {
res <- res + (y[,ind] == facet[2]) # inward boundary
res <- res + (x[,ind] == facet[1]) # inward boundary
res <- res + (x[,ind] == 1) # inward boundary
res <- res + 3 * (y[,ind] == 1) # outward boundary
} else if(img == n.img) {
res <- res + (x[,ind] == 1) # inward boundary
res <- res + (y[,ind] == facet[2]) # inward boundary
res <- res + 3 * (x[,ind] == facet[1]) # outward boundary
res <- res + 3 * (y[,ind] == 1) # outward boundary
res <- res - (x[,ind] == facet[1] & y[,ind] == 1) # outward corner
} else stop("can only deal with 6/9 image wells.")
return(res)
}
facetBorder <- function(ind, x, y, facet) {
# initialize empty grid/border matrices
grid <- matrix(0, facet[2], facet[1])
# calculate col-major grid index for each object
index <- y[,ind] + (x[,ind] - 1) * facet[2]
# summarize as counts
counts <- table(index)
# fill grid with counts
grid[as.numeric(names(counts))] <- counts
grid <- grid > 0
# extend grid with a frame of zeros
grid.ext <- rbind(rep(1, (facet[1] + 2)),
cbind(rep(1, facet[2]), grid, rep(1, facet[2])),
rep(1, (facet[1] + 2)))
# set up stencil
row <- rep(rep(1:facet[2], facet[1]))
col <- rep(1:facet[1], each=facet[2])
colP1 <- col + 1
colM1 <- col - 1
rowP1 <- row + 1
rowP2 <- row + 2
nrowP <- facet[2] + 2
stencil <- cbind(row + colM1 * nrowP, # northwest neighbor
row + col * nrowP, # north neighbor
row + colP1 * nrowP, # northeast neighbor
rowP1 + colP1 * nrowP, # west neighbor
rowP2 + colP1 * nrowP, # east neighbor
rowP2 + col * nrowP, # southwest neighbor
rowP2 + colM1 * nrowP, # south neighbor
rowP1 + colM1 * nrowP) # southeast neighbor
# apply stencil row-wise to grid
border <- apply(stencil, 1, function(ind, mat) {
return(sum(mat[ind]))
}, as.numeric(grid.ext))
# map col-major object index to border array
return(border[index])
#border <- matrix(border, facet[2], facet[1])
#matrix((as.numeric(border) * as.numeric(grid)), facet[2], facet[1])
}
distCalc <- function(ind, x, y, cent) {
return(sqrt((x[,ind] - cent[1])^2 + (y[,ind] - cent[2])^2))
}
densCalc <- function(ind, x, y) {
data <- cbind(x[,ind], y[,ind])
if(nrow(data) < 2) {
warning("not estimating densities with less than 2 cells.")
return(rep(NA, nrow(data)))
}
capture.output(dens <- sm::sm.density(data, display="none"))
surf <- list(x=dens$eval.points[, 1],
y=dens$eval.points[, 2],
z=dens$estimate)
res <- fields::interp.surface(surf, data)
return(res)
}
if(is.null(ellipse) & is.null(facet) & !center.dist & !density) {
warning("nothing to do.")
return(x)
}
# check if shifted features are present
shifted <- any(grepl("Shifted_X" ,getFeatureNames(x)))
image.dimensions1 <- c(1392, 1040)
image.center1 <- image.dimensions1 / 2
if(x$image.total == 9) {
image.dimensions2 <- c((1392 * 3 + 20), (1040 * 3 + 20))
} else if(x$image.total == 6) {
image.dimensions2 <- c((1392 * 3 + 20), (1040 * 2 + 10))
} else stop("can only deal with 6/9 image wells.")
image.center2 <- image.dimensions2 / 2
if(!is.null(ellipse)) {
ellipse <- as.integer(ellipse)
if(ellipse < 1 | ellipse > 20) stop("expecting ellipse to be in 1:20")
if(ellipse != 1) {
rect1 <- image.dimensions1[1] * image.dimensions1[2]
step1 <- 1:(ellipse - 1) * rect1 / ellipse
rect2 <- image.dimensions2[1] * image.dimensions2[2]
step2 <- 1:(ellipse - 1) * rect2 / ellipse
message("ellipse sizes: ", paste(step1, collapse=", "),
" (within images)")
if(shifted) {
message(" and: ", paste(step2, collapse=", "), " (within wells)")
}
elli1 <- sapply(step1, function(x) {
b <- sqrt(x / pi * image.dimensions1[2] / image.dimensions1[1])
a <- image.dimensions1[1] * b / image.dimensions1[2]
return(c(a, b))
})
elli2 <- sapply(step2, function(x) {
b <- sqrt(x / pi * image.dimensions2[2] / image.dimensions2[1])
a <- image.dimensions2[1] * b / image.dimensions2[2]
return(c(a, b))
})
} else {
elli1 <- image.center1 - 100
elli2 <- image.center2 - 200
if(shifted) {
message("using a single ellipse, 100px (within images) and \n200px ",
"(within wells) dist from borders.")
} else {
message("using a single ellipse, 100px dist from borders.")
}
}
x$data.mat <- lapply(x$data.mat, function(group) {
if(!is.null(group)) {
index1.x <- grep("Location_Center_X", colnames(group))
index1.y <- grep("Location_Center_Y", colnames(group))
index2.x <- grep("Location_Shifted_X", colnames(group))
index2.y <- grep("Location_Shifted_Y", colnames(group))
if(length(index1.x) != length(index1.y)) {
stop("expecting equal number of center features for x & y")
}
if(length(index2.x) != length(index2.y)) {
stop("expecting equal number of shifted features for x & y")
}
length.original <- ncol(group)
length.center <- length(index1.x)
length.shifted <- length(index2.x)
if((length.center > 0 | length.shifted > 0) & nrow(group) == 0) {
# in case no rows are present, add features to colnames anyway
if(length.center > 0 & length.shifted > 0) {
nmes <- c(
colnames(group),
gsub("Center_X$", "In_Ellipse_Image", colnames(group)[index1.x]),
gsub("Shifted_X$", "In_Ellipse_Well", colnames(group)[index2.x]))
dims <- c(0, length.original + length.center + length.shifted)
} else if(length.center > 0) {
nmes <- c(colnames(group), gsub("Center_X$", "In_Ellipse_Image",
colnames(group)[index1.x]))
dims <- c(0, length.original + length.center)
} else if(length.shifted > 0) {
nmes <- c(colnames(group), gsub("Shifted_X$", "In_Ellipse_Well",
colnames(group)[index2.x]))
dims <- c(0, length.original + length.shifted)
}
dim(group) <- dims
colnames(group) <- nmes
return(group)
} else if((length.center > 0 | length.shifted > 0) & nrow(group) > 0) {
if(length.center > 0) {
# center features
feat1.x <- group[,index1.x, drop=FALSE]
feat1.y <- group[,index1.y, drop=FALSE]
if (ellipse == 1) {
# only one ellipse
res1.ell <- sapply(1:ncol(feat1.x), singleEllipse, feat1.x,
feat1.y, image.center1, elli1)
} else {
# multiple ellipses
res1.ell <- sapply(1:ncol(feat1.x), multipleEllipses, feat1.x,
feat1.y, image.center1, ellipse, elli1)
}
dim(res1.ell) <- dim(feat1.x)
} else res1.ell <- NULL
if (length.shifted > 0) {
# shifted features
feat2.x <- group[,index2.x, drop=FALSE]
feat2.y <- group[,index2.y, drop=FALSE]
if (ellipse == 1) {
# only one ellipse
res2.ell <- sapply(1:ncol(feat2.x), singleEllipse, feat2.x,
feat2.y, image.center2, elli2)
} else {
# multiple ellipses
res2.ell <- sapply(1:ncol(feat2.x), multipleEllipses, feat2.x,
feat2.y, image.center2, ellipse, elli2)
}
dim(res2.ell) <- dim(feat2.x)
} else res2.ell <- NULL
if(!is.null(res1.ell) & !is.null(res2.ell)) {
res <- cbind(group, res1.ell, res2.ell)
range1 <- (length.original + 1):(length.original + length.center)
range2 <- (length.original + length.center + 1):(length.original +
length.center + length.shifted)
colnames(res)[range1] <- gsub("Center_X$", "In_Ellipse_Image",
colnames(group)[index1.x])
colnames(res)[range2] <- gsub("Shifted_X$", "In_Ellipse_Well",
colnames(group)[index2.x])
} else if (!is.null(res1.ell)) {
res <- cbind(group, res1.ell)
range1 <- (length.original + 1):(length.original + length.center)
colnames(res)[range1] <- gsub("Center_X$", "In_Ellipse_Image",
colnames(group)[index1.x])
} else if (!is.null(res2.ell)) {
res <- cbind(group, res2.ell)
range2 <- (length.original + length.center + 1):(length.original +
length.center + length.shifted)
colnames(res)[range2] <- gsub("Shifted_X$", "In_Ellipse_Well",
colnames(group)[index2.x])
} else stop("?!?")
return(res)
} else {
return(group)
}
} else return(NULL)
})
}
if(!is.null(facet)) {
facet <- as.integer(facet)
if(!(is.vector(facet, mode="integer") & length(facet) == 2)) {
stop("expecting an integer vector of length 2 for facet.")
}
facet.size <- image.dimensions1 / facet
message("facet size: ", round(facet.size[1]), " (x), ",
round(facet.size[2]), " (y)")
x$data.mat <- lapply(x$data.mat, function(group) {
if(!is.null(group)) {
index.x <- grep("Location_Center_X", colnames(group))
index.y <- grep("Location_Center_Y", colnames(group))
if(length(index.x) != length(index.y)) {
stop("expecting equal number of center features for x & y")
}
length.original <- ncol(group)
length.new <- length(index.x)
if(length.new > 0 & nrow(group) == 0) {
# in case no rows are present, add features to colnames anyway
if(shifted) {
nmes <- c(
colnames(group),
gsub("Center_X$", "Facet_X_Image", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Image", colnames(group)[index.y]),
gsub("Center_X$", "Facet_X_Well", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Well", colnames(group)[index.y]),
gsub("Center_X$", "Facet_Type", colnames(group)[index.x]),
gsub("Center_X$", "Facet_Border", colnames(group)[index.x]))
dims <- c(0, length.original + 6 * length.new)
} else {
nmes <- c(
colnames(group),
gsub("Center_X$", "Facet_X_Image", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Image", colnames(group)[index.y]),
gsub("Center_X$", "Facet_Type", colnames(group)[index.x]),
gsub("Center_X$", "Facet_Border", colnames(group)[index.x]))
dims <- c(0, length.original + 4 * length.new)
}
dim(group) <- dims
colnames(group) <- nmes
return(group)
} else if(length.new > 0 & nrow(group) > 0) {
feat.x <- group[,index.x, drop=FALSE]
feat.y <- group[,index.y, drop=FALSE]
facet.x <- sapply(1:ncol(feat.x), facetCalc, feat.x, facet.size[1])
facet.y <- sapply(1:ncol(feat.y), facetCalc, feat.y, facet.size[2])
dim(facet.x) <- dim(feat.x)
dim(facet.y) <- dim(feat.y)
facet.type <- sapply(1:ncol(feat.x), facetType, facet.x, facet.y,
facet, x$image.total, x$image.index)
dim(facet.type) <- dim(feat.x)
facet.border <- sapply(1:ncol(feat.x), facetBorder, facet.x, facet.y,
facet)
dim(facet.border) <- dim(feat.x)
res.fac <- cbind(facet.x, facet.y, facet.type, facet.border)
colnames(res.fac) <- c(
gsub("Center_X$", "Facet_X_Image", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Image", colnames(group)[index.y]),
gsub("Center_X$", "Facet_Type", colnames(group)[index.x]),
gsub("Center_X$", "Facet_Border", colnames(group)[index.x]))
if(shifted) {
shifted.x <- facet.x + (x$image.col - 1) * facet[1]
shifted.y <- facet.y + (x$image.total / 3 - x$image.row) * facet[2]
res.fac <- cbind(facet.x, facet.y, shifted.x, shifted.y,
facet.type, facet.border)
colnames(res.fac) <- c(
gsub("Center_X$", "Facet_X_Image", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Image", colnames(group)[index.y]),
gsub("Center_X$", "Facet_X_Well", colnames(group)[index.x]),
gsub("Center_Y$", "Facet_Y_Well", colnames(group)[index.y]),
gsub("Center_X$", "Facet_Type", colnames(group)[index.x]),
gsub("Center_X$", "Facet_Border", colnames(group)[index.x]))
}
return(cbind(group, res.fac))
} else {
return(group)
}
} else return(NULL)
})
}
if(center.dist) {
message("including distance to image center.")
x$data.mat <- lapply(x$data.mat, function(group) {
if(!is.null(group)) {
index1.x <- grep("Location_Center_X", colnames(group))
index1.y <- grep("Location_Center_Y", colnames(group))
index2.x <- grep("Location_Shifted_X", colnames(group))
index2.y <- grep("Location_Shifted_Y", colnames(group))
if(length(index1.x) != length(index1.y)) {
stop("expecting equal number of center features for x & y")
}
if(length(index2.x) != length(index2.y)) {
stop("expecting equal number of shifted features for x & y")
}
length.original <- ncol(group)
length.center <- length(index1.x)
length.shifted <- length(index2.x)
if((length.center > 0 | length.shifted > 0) & nrow(group) == 0) {
# in case no rows are present, add features to colnames anyway
if(length.center > 0 & length.shifted > 0) {
nmes <- c(colnames(group),
gsub("Center_X$", "Dist_Center_Image",
colnames(group)[index1.x]),
gsub("Shifted_X$", "Dist_Center_Well",
colnames(group)[index2.x]))
dims <- c(0, length.original + length.center + length.shifted)
} else if(length.center > 0) {
nmes <- c(colnames(group),
gsub("Center_X$", "Dist_Center_Image",
colnames(group)[index1.x]))
dims <- c(0, length.original + length.center)
} else if(length.shifted > 0) {
nmes <- c(colnames(group),
gsub("Shifted_X$", "Dist_Center_Well",
colnames(group)[index2.x]))
dims <- c(0, length.original + length.shifted)
}
dim(group) <- dims
colnames(group) <- nmes
return(group)
} else if((length.center > 0 | length.shifted > 0) & nrow(group) > 0) {
if(length.center > 0) {
feat1.x <- group[,index1.x, drop=FALSE]
feat1.y <- group[,index1.y, drop=FALSE]
dist1 <- sapply(1:ncol(feat1.x), distCalc, feat1.x, feat1.y,
image.center1)
dim(dist1) <- dim(feat1.x)
} else dist1 <- NULL
if (length.shifted > 0) {
feat2.x <- group[,index2.x, drop=FALSE]
feat2.y <- group[,index2.y, drop=FALSE]
dist2 <- sapply(1:ncol(feat2.x), distCalc, feat2.x, feat2.y,
image.center2)
dim(dist2) <- dim(feat2.x)
} else dist2 <- NULL
if(!is.null(dist1) & !is.null(dist2)) {
res <- cbind(group, dist1, dist2)
range1 <- (length.original + 1):(length.original + length.center)
range2 <- (length.original + length.center + 1):(length.original
+ length.center + length.shifted)
colnames(res)[range1] <- gsub("Center_X$", "Dist_Center_Image",
colnames(group)[index1.x])
colnames(res)[range2] <- gsub("Shifted_X$", "Dist_Center_Well",
colnames(group)[index2.x])
} else if(!is.null(dist1)) {
res <- cbind(group, dist1)
range1 <- (length.original + 1):(length.original + length.center)
colnames(res)[range1] <- gsub("Center_X$", "Dist_Center_Image",
colnames(group)[index1.x])
} else if(!is.null(dist2)) {
res <- cbind(group, dist2)
range2 <- (length.original + length.center + 1):(length.original
+ length.center + length.shifted)
colnames(res)[range2] <- gsub("Shifted_X$", "Dist_Center_Well",
colnames(group)[index2.x])
} else stop("?!?")
return(res)
} else {
return(group)
}
} else return(NULL)
})
}
if(density) {
message("calculating densities.")
x$data.mat <- lapply(x$data.mat, function(group) {
if(!is.null(group)) {
index.x <- grep("Location_Center_X", colnames(group))
index.y <- grep("Location_Center_Y", colnames(group))
if(length(index.x) != length(index.y)) {
stop("expecting equal number of center features for x & y")
}
length.original <- ncol(group)
length.center <- length(index.x)
if(length.center > 0 & nrow(group) == 0) {
# in case no rows are present, add features to colnames anyway
nmes <- c(colnames(group),
gsub("Center_X$", "Kern_Dens_Image",
colnames(group)[index.x]))
dims <- c(0, length.original + length.center)
dim(group) <- dims
colnames(group) <- nmes
return(group)
} else if(length.center > 0 & nrow(group) > 0) {
feat.x <- group[,index.x, drop=FALSE]
feat.y <- group[,index.y, drop=FALSE]
dens <- sapply(1:ncol(feat.x), densCalc, feat.x, feat.y)
dim(dens) <- dim(feat.x)
res <- cbind(group, dens)
range <- (length.original + 1):(length.original + length.center)
colnames(res)[range] <- gsub("Center_X$", "Kern_Dens_Image",
colnames(group)[index.x])
return(res)
} else {
return(group)
}
} else return(NULL)
})
}
return(x)
}
#' @export
augmentCordinateFeatures.default <- function(x, ...) {
stop("can only deal with Data (ImageData/WellData/PlateData) objects.")
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.