This is a tutorial on how to load images and make them ready for downstream analysis.
The images has to be .tiff or .tif files
The images can be either binary or greyscale. Therefore, different colours/channels should be split in different .tiff files. If they are greyscale they should be thresholded before further analysis (see section on thresholding below)
The .tiff files can contain multiple layers, or there can be a .tiff for each layer. Both are accepted (see details in loadIMG
). Also, a .tiff can contain multiple images. As long as the channels are in different .tiff files.
Four species biofilm (for quantification and layer-based analysis): https://github.com/Russel88/RCon3D/blob/master/ExampleData.zip
Two species in alginate beads (for finding aggregates): https://github.com/Russel88/RCon3D/blob/master/ExampleData2.7z
The last is the one used in this example
library(RCon3D)
Specify paths of images
path <- "C:/Images"
loadIMG
creates the RDS files, and when we run the script again we can just use findIMG
to get the paths for the RDS files.
channels
should be unique and exclusive names in the filenames of the images.
The following might give warnings about TIFF image tags that cannot be read. This should not be a problem.
img <- loadIMG(path, channels = c("C0","C1"))
Naming the images properly is important before loading them into R.
The channels has to be unique strings in the names.
In general having unique strings for all treatments/timepoint/etc. makes subsequent analysis easier, as the functions can grap this information from the image names.
The above naming is bad, because the channel naming “C1” and “C2” (before the underscore) is part of the naming (after underscore), which will confuse the functions. Calling the channels “C1_” and “C2_” in loadIMG
would fix the problem.
Check that the number of images are as expected:
print(img)
If the images has been loaded previously, we can use findIMG
to get the paths:
img <- findIMG(path)
We test the dimensions of the loaded images to ensure they are as expected:
lapply(img, function(x) dim(readRDS(x)))
img
contains the paths for the RDS files, and this is the input for subsequent analyses.
If the images has not been thresholded yet, they should be before further analysis.
threshIMG
applies either Otsu’s threshold, BEM (https://www.nature.com/articles/s41598-018-31012-5), or manual thresholds on the images. Use Otsu’s method if the intensity histogram is bimodal (two peaks), and use BEM if the intensity histogram peaks at zero.
The output of threshIMG
are the paths for the thresholded images, which can be used for further analysis.
The default options in threshIMG
is no threshold, so remember to choose a method!
imgT <- threshIMG(img, method = "Otsu")
You can use the mmand package to plot the output:
# The 1 means image number 1
# The 5 means layer number 5
mmand::display(readRDS(imgT[1])[,,5])
We can run different algorithms on different channels by simply subsetting img
:
# Splitting by channels
img.C0 <- img[grep("C0",img)]
img.C1 <- img[grep("C1",img)]
# Thresholding
img.C0T <- threshIMG(img.C0, method = "Otsu")
img.C1T <- threshIMG(img.C1, method = "BEM")
# Combining
img.T <- c(img.C0T,img.C1T)
If the channels do not correspond to the microbial strains we need to prepare the images before further analysis.
In this example channel C1 stains both strains. C0 correspond to only one of the strains (Xanthomonas). Xanthomonas is therefore the pixels where both C0 and C1 is detected. The other strain, Paenibacillus, is only the C1 pixels where C0 is not observed
img.pan <- merge_channels(path, img, channels = c("C1","C0"), method = "subtract")
img.xan <- merge_channels(path, img, channels = c("C1","C0"), method = "intersect")
# img.pan <- img[grep("RCon3D.C1.C0.subtract",img)]
# img.xan <- img[grep("RCon3D.C1.C0.intersect",img)]
Check that the paths are correct:
print(img.pan)
print(img.xan)
We might want to smooth images before downstream analysis. So far only median smoothing is included
img.s <- smoothIMG(img, kern.smooth = c(3, 3, 3), type.smooth = "box", cores = 1)
type.smooth
: Type of kernel for smooth “box” includes diagonals, “diamond” is without diagonals kern.smooth
: Range of median smoothing in the x,y,z directions. Has to be odd integers. c(3,3,3) means immediate neighbours in all directions; imagine a box of size 3x3x3 centered on a focal pixel. The value of the focal pixel will then be the median of all 27 pixels in the box.
We might want to morph images before downstream analysis. This is basically image manipulation, and should only be applied if there is good reason for it. See details in ?morphIMG
and ?mmand::erode
.
img.m <- morphIMG(imgs, morph = NULL, kern = c(3, 3, 3), type = "box", cores = 1)
For some analyses we might want arrays corresponding to all the empty space in the images. This could for example be used to estimate the amount of empty space around a certain channel.
img_empty <- create_nulls(img, path, channels = c("C0","C1"))
img_empty
then contains the paths for the RDS files corresponding to the empty space, or more precisely, the space note occupied by either “C0” or “C1”.