## maskTracks-methods
##
##
###############################################################################
##' @name maskTracks
##' @aliases maskTracks indexCell filterOnCell sampleTracks
##' @title maskTracks
##' @rdname maskTracks-methods
##' @docType methods
##' @description apply binary image masks to lists of track lists
#
##' @usage
##' maskTracks(folder, trackll)
##'
##' indexCell(folder, trackll, areaFilter = c(0, Inf),
##' intensityFilter = c(0, Inf), export = F, max.pixel = 128)
##'
##' filterOnCell(trackll, numTracks = 0)
##'
##' sampleTracks(trackll, num = 0)
##' @param folder Full path to the output files.
##' @param trackll A list of track lists.
##' @param export Logical indicate if .csv output file should be generated
##' @param max.pixel Pixel dimension of image
##' @param areaFilter Range of cell areas (pixel sq) to keep in filtering
##' @param intensityFilter Range of avg cell intensities (grayscale) to keep in filtering
##' @param numTracks Minimum number of required tracks in the trackll
##' @param num Number of tracks to randomly sample per trackl in trackll
##' @examples
##' #Basic masking with folder path with image masks
##' folder = system.file("extdata","ImageJ",package="sojourner")
##' trackll = createTrackll(folder, input = 3)
##' trackll.masked <- maskTracks(folder = folder, trackll = trackll)
##'
##' #Compare the masking effect
##' plotTrackOverlay(trackll)
##' plotTrackOverlay(trackll.masked)
##'
##' #Plot mask
##' mask.list=list.files(path=folder,pattern="_MASK.tif",full.names=TRUE)
##' plotMask(folder)
##'
##' #If Nuclear image is available
##' plotNucTrackOverlay(folder=folder,trackll=trackll)
##' plotNucTrackOverlay(folder=folder,trackll=trackll.masked)
##'
##' #Plot mask
##' plotMask(folder=folder)
##' @details
##' IMPORTANT: It will take an extremely long time to mask unfiltered data. Filter first using filterTrack(trackll,filter=c(min=7,max=Inf)), then mask using maskTracks(folder, trackll)!
##' Note the mask file should have the same name as the output files with a "_MASK.tif" ending.
##' If there are more mask files than trackll, masking will fail. If there are less mask files, trackls without masks will be deleted.
##' Users can use plotMask() and plotTrackOverlay() to see the mask and its effect on screening tracks.
##'
##' indexCell() will mask a trackll, separate each cell into a trackl, display all cell areas and mean intensities, and then apply any area and intensity filters.
##' There is also the capability to export the final areas/intensities as "indexCell.csv" to the home directory and the pixel dimensions can be changed.
##'
##' filterOnCell() eliminates all trackl in trackll that has less than numTracks tracks.
##'
##' sampleTracks() randomly samples num number of tracks for each trackl in trackll.
##' @importFrom pixmap getChannels
##' @importFrom SDMTools ConnCompLabel
##' @export maskTracks
##' @export indexCell
##' @export filterOnCell
##' @export sampleTracks
##------------------------------------------------------------------------------
##
# read in mask and derive positive pix form a mask
maskPoint=function(mask.file,plot=F){
mask.file.name=basename(mask.file)
# read in tiff mask
# library(rtiff)
cat("Reading mask file ",mask.file.name,"\n")
mask=rtiff::readTiff(fn=mask.file)
# plot(mask)
pospt=which(mask@red!=0,arr.ind=T)
pos.point=with(data.frame(pospt),data.frame(x=col,y=row))
# horizontal is the same vertical is fliped as the pixels is counted from
# upper left in the image, but is counted from lower left in the plot.
if (plot) {
plot(mask)
plot(x=pos.point$x,y=pos.point$y)
#ggplot(pos.point,aes(x=x,y=y))+geom_point()
}
return(pos.point)
}
##------------------------------------------------------------------------------
##
# Use each trajectory's geometric center as unit for clusterization.
# Each data point is an averaged trajectory.
trackCenter=function(trackll){
# arithmetic mean for geometric center (centroid)
track.center=list()
length(track.center)=length(trackll)
names(track.center)=names(trackll)
for (i in 1:length(trackll)){
track.center[[i]]=lapply(trackll[[i]],function(x){
# round coords
apply(x,2,function(coord){round(mean(coord))})})
}
return(track.center)
}
##------------------------------------------------------------------------------
##
# track.center and pos.point should be one to one cooresponding
posTracks=function(track.center,pos.point){
# convert list to data.frame
track.center.df=do.call(rbind.data.frame,track.center)
names(track.center.df)=c("x","y","z", "Frame")
# change rownames to number to avoid PC mac difference in rowname when do.call rbind
rownames(track.center.df)=seq(1,dim(track.center.df)[1])
pos.tracks=plyr::match_df(track.center.df,pos.point,on=c("x","y"))
return(pos.tracks)
}
##------------------------------------------------------------------------------
##
maskTracks=function(folder, trackll){
# read in mask
maskl=list.files(path=folder,pattern="_MASK.tif",full.names=T)
if (length(maskl) == 0){
cat("No image mask file ending '_MASK.tif' found.\n")
}
if (length(maskl) > length(trackll)){
stop("More masks than trackl.\n")
}
# make mask list and trackll one-to-one, delete extra trackl
maskl.check = list()
maskl.names = gsub("_MASK.tif","",basename(maskl))
trackll.names = gsub("[.].*","",names(trackll))
for (i in 1:length(trackll.names)) {
found = FALSE
for (j in 1:length(maskl.names)) {
if (trackll.names[[i]] == maskl.names[[j]]) {
maskl.check[[length(maskl.check)+1]] = maskl[j]
found = TRUE
break
}
}
if (!found) {
cat(paste(names(trackll)[[i]], "mask not found. Trackl deleted from masked output.\n", sep = " "))
trackll[[i]] <- NULL
}
}
maskl = maskl.check
mask.track.index=list()
length(mask.track.index)=length(trackll)
names(mask.track.index)=names(trackll)
masked.tracks=list()
length(masked.tracks)=length(trackll)
names(masked.tracks)=names(trackll)
for (i in 1:length(trackll)){
## Finds all track centers
track.center=trackCenter(trackll)[[i]]
## Returns all positive mask pixel locations
pos.point=maskPoint(maskl[[i]],plot=F)
## Filters all positive track centers
mask.track.index[[i]]=posTracks(track.center,pos.point)
## Collects track indexes to keep
index=rownames(mask.track.index[[i]])
## Filters only such indexes in the raw trackll[i]
masked.tracks[[i]]=lapply(trackll[i],function(x){x[as.numeric(index)]})[[1]]
}
cat("\nAll files masked.\n")
return(masked.tracks)
}
# Function masks, separates by cell, displays cell areas and mean intensities, and filters.
indexCell=function(folder, trackll, areaFilter = c(0, Inf), intensityFilter = c(0, Inf), export = F, max.pixel = 128){
# Read in mask
maskl=list.files(path=folder,pattern="_MASK.tif",full.names=T)
# Read in nuclear image
glowl=list.files(path=folder,pattern="_Nuclei.tif",full.names=T)
if (length(maskl) == 0){
cat("No image mask file ending '_MASK.tif' found.\n")
}
if (length(maskl) > length(trackll)){
stop("More masks than trackl.\n")
}
# Make mask list and trackll one-to-one and onto, delete extra trackl
maskl.check = list()
maskl.names = gsub("_MASK.tif","",basename(maskl))
trackll.names = gsub("[.].*","",names(trackll))
for (i in 1:length(trackll.names)) {
found = FALSE
for (j in 1:length(maskl.names)) {
if (trackll.names[[i]] == maskl.names[[j]]) {
maskl.check[[length(maskl.check)+1]] = maskl[j]
found = TRUE
break
}
}
if (!found) {
cat(paste(names(trackll)[[i]], "mask not found. Trackl deleted from masked output.\n", sep = " "))
trackll[[i]] <- NULL
}
}
maskl = maskl.check
# Instantiate new variables
masked.trackll=list()
raw.names=names(trackll)
cell.count = list()
length(cell.count)=length(trackll)
areas=list()
intensities=list()
## Find all track centers
track.center=trackCenter(trackll)
# Loop through each trackl
for (i in 1:length(trackll)){
## Returns all positive mask pixel locations
invisible(capture.output(pos.point <- maskPoint(maskl[[i]],plot=F)))
#Instantiate empty
binary.mat = matrix( rep( 0, len=max.pixel*max.pixel), nrow = max.pixel)
#Fill with binary pospoints
for (m in 1:nrow(pos.point)){
binary.mat[pos.point[[1]][[m]], pos.point[[2]][[m]]] = 1
}
# Calculate connected components and label accordingly
labeled.mat <- SDMTools::ConnCompLabel(binary.mat)
# FOR VISUALIZING THE LABELED MASK
# image(t(labeled.mat[128:1,]),col=c('grey',rainbow(length(unique(labeled.mat))-1)))
pos.points.indexed = list()
length(pos.points.indexed) = max(labeled.mat)
# Convert labeled.mat to lists of pos.points per cell
for (j in 1:nrow(pos.point)) {
pos.points.indexed[[labeled.mat[pos.point[j,]$x, pos.point[j,]$y]]] <- rbind( pos.points.indexed[[labeled.mat[pos.point[j,]$x, pos.point[j,]$y]]], pos.point[j,])
}
## Display Area ##
cat(maskl.names[[i]], "\n", "Cell Areas (pixels squared): ", "\n", sep = "")
areal <- sapply(pos.points.indexed, nrow)
cat(cat(areal, sep = "\n"))
areas <- append(areas, areal)
## Display Intensity ##
glow = t(matrix(pixmap::getChannels(rtiff::readTiff(glowl[[i]])), nrow = max.pixel, ncol = max.pixel))
raw.intensities = rep(list(list()), max(labeled.mat))
for(row in 1:nrow(labeled.mat)) {
for(col in 1:ncol(labeled.mat)) {
if (labeled.mat[row, col] != 0){
raw.intensities[[labeled.mat[row, col]]][[length(raw.intensities[[labeled.mat[row, col]]])+1]] = glow[row, col]
}
}
}
cat("Cell Intensities (grayscale): ", "\n", sep = "")
intensityl <- list()
for (cell in 1:length(raw.intensities)){
intensityl[[cell]] <- mean(unlist(raw.intensities[[cell]]))
cat(paste(intensityl[[cell]], "\n"))
}
cat("\n")
intensities <- append(intensities, intensityl)
# Update cell count per trackl
cell.count[[i]] <- length(pos.points.indexed)
# Loop through each cell
for (k in 1:length(pos.points.indexed)){
mask.track.index=list()
length(mask.track.index)=length(trackll)
names(mask.track.index)=names(trackll)
## Filters all positive track centers
mask.track.index[[k]]=posTracks(track.center[[i]], pos.points.indexed[[k]])
## Collects track indexes to keep
index=rownames(mask.track.index[[k]])
## Filters only such indexes in the raw trackll[i]
masked.trackll[[length(masked.trackll)+1]]=lapply(trackll[i],function(x){x[as.numeric(index)]})[[1]]
}
}
#print(areas, row.names = FALSE)
#print(intensities, row.names = FALSE)
# Edit the names with concatenated cell number to trackl names
masked.names = list()
for (c in 1:length(cell.count)){
for (v in 1:cell.count[[c]]){
masked.names[[length(masked.names)+1]] <- paste(raw.names[c], toString(v), sep = "_")
}
}
names(masked.trackll) <- masked.names
if (export){
df <- data.frame(matrix(ncol = 3, nrow = 0))
colnames(df) <- c("Cell", "Area (pixel sq)", "Intensity (grayscale)")
}
# Filter
j = 1
for(i in 1:length(areas)){
if (areas[[i]] > areaFilter[[2]] || areas[[i]] < areaFilter[[1]] || intensities[[i]] > intensityFilter[[2]] || intensities[[i]] < intensityFilter[[1]]){
masked.trackll[[j]] <- NULL;
} else {
if (export){
df[nrow(df) + 1,] = list(names(masked.trackll)[[j]], areas[[i]], intensities[[i]])
}
j = j + 1
}
}
cat("\nOnly cell with areas between ", areaFilter[[1]], " and ", areaFilter[[2]], " pixels sq kept.", sep = "")
cat("\nOnly cell with avg intensities between ", intensityFilter[[1]], " and ", intensityFilter[[2]], " grayscale kept.", sep = "")
if (export){
write.csv(df, file = "indexCell.csv")
cat("\nindexCell.csv exported to working directory.")
}
cat("\n\nAll files masked, separated by cell indexes, and filters applied.\n")
return(masked.trackll)
}
filterOnCell=function(trackll, numTracks = 0){
if (numTracks == 0) {
cat("\nEnter a lower limit for number of tracks.\n")
} else {
i = 1
while(i != length(trackll)){
if (length(trackll[[i]]) < numTracks){
trackll[[i]] <- NULL
} else {
i = i + 1
}
}
}
return(trackll)
}
sampleTracks = function(trackll, num = 0){
if (num == 0) {
cat("\nEnter sample size.\n")
} else {
for (i in 1:length(trackll)){
trackll[[i]] <- sample(trackll[[i]], num)
}
return(trackll)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.