knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(ARCOS) library(ggplot2) # Custom functions myPlotBinTS <- function(objTS, xlim = c(0,65), ylim = c(65,0), ncol = 4, plotType = c("raster", "point"), pointSize = 1) { locFrame = attr(objTS, "colFrame") locPos = attr(objTS, "colPos") locM = attr(objTS, "colMeas") locP = ggplot(objTS, aes(x = get(locPos[2]), y = get(locPos[1]))) if (plotType == "point") { locP = locP + geom_point(aes(color = as.factor(get(locM))), size = pointSize) + scale_color_manual(values = c("grey80", "grey20")) } else if (plotType == "raster") { locP = locP + geom_raster(aes(fill = as.factor(get(locM)))) + scale_fill_manual(values = c("grey80", "grey20")) } locP = locP + facet_wrap(locFrame, ncol = ncol) + coord_fixed(ratio=1) + scale_x_continuous(limits = xlim) + scale_y_continuous(trans = "reverse", limits = ylim) + theme_void() + theme(text = element_text(size = 20), legend.position = "none") return(locP) } myPlotRasterColl <- function(objColl, xlim = c(0,65), ylim = c(65,0), ncol = 4) { ggplot(objColl, aes(x = x, y = y)) + geom_raster(aes(fill = as.factor(collid))) + scale_fill_discrete(name = "ID coll.") + facet_wrap(~ frame, ncol = ncol) + coord_fixed(ratio=1) + scale_x_continuous(limits = xlim) + scale_y_continuous(trans = "reverse", limits = ylim) + theme_void() + theme(text = element_text(size = 20), legend.position = "none") }
Here we demonstrate tracking of collective events from a sequence of binary images. Each pixel in the image is treated as a separate object, to which an objID
number is assigned. The objID
identifier is the same for objects with the same X/Y position in subsequent frames.
Pixel values, the measurement, assume values 0 or 1, which correspond to an inactive or active object, respectively. Objects with value 1 are active and our goal is to detect and track a collective activation of all such objects in all frames of the time sequence.
Here we detect and track a concentrically growing cluster of active objects directly from a sequence of 12 binary PNG images at 64-by-64 pixel resolution.
We use the loadDataFromImages
function to read data from PNG images. The function returns an arcosTS
object, which is a data.table
with additional attributes that specify position, measurement, and object ID columns.
Since images are binary, we set the thres
parameter to -1
to load all data, zeroes and ones, without thresholding.
dtIm = ARCOS::loadDataFromImages(file.path(system.file("testImagePatterns/concentricGrowth", package="ARCOS"), "png_64"), "*.png", thres = -1)
The final long-format input data with active/inactive objects. Columns:
x
and y
correspond to X/Y coordinates of an object,m
contains binarised measurement ${0,1}$ that corresponds to inactive/active objects, respectively. For identification of collective events we will only use active objects, i.e. $m > 0$,objID
holds an object identifier, which should be at least unique within a single time frame,time
indicates the time frame number.knitr::kable(head(dtIm))
Here we plot the time sequence with a growing concentric circle. Each frame (time point) consists of a matrix of 64x64 objects, where each object can assume a value of 0 (inactive) or 1 (active). In this example objects do not change their positions or identities across frames. In general, both positions and object identifiers may change between frames.
myPlotBinTS(dtIm, plotType = "raster")
We use the ARCOS::trackColl
function to identify collective events in frames and to link them between frames.
dtColl = ARCOS::trackColl(dtIm[m>0])
The result from tracking of collective events is a long-format data.table
with columns:
time
with the frame number,objID
with an identifier of an object involved in a collective event,collID
with an identifier of a collective event that is unique across the entire frame sequence.knitr::kable(head(dtColl))
Here we display frames only with collective events. The algorithm identified a single event in 10 subsequent frames:
myPlotRasterColl(dtColl)
Detect and track a moving cluster of active objects directly from a sequence of 16 binary PNG images at 64-by-64 pixel resolution.
dtIm = ARCOS::loadDataFromImages(file.path(system.file("testImagePatterns/movingCircle", package="ARCOS"), "png_64"), "*.png", thres = -1)
myPlotBinTS(dtIm, plotType = "raster")
dtColl = ARCOS::trackColl(dtIm[m>0])
myPlotRasterColl(dtColl)
Detect and track a cluster of active objects that splits into two parts. Analysis directly from a sequence of 15 binary PNG images at 64-by-64 pixel resolution.
The analysis should consider everything as a single cluster even after the split.
dtIm = ARCOS::loadDataFromImages(file.path(system.file("testImagePatterns/splitCircle", package="ARCOS"), "png_64"), "*.png", thres = -1)
myPlotBinTS(dtIm, plotType = "raster", ncol = 5)
dtColl = ARCOS::trackColl(dtIm[m>0])
myPlotRasterColl(dtColl, ncol = 5)
Detect and track multiple moving clusters of active objects directly from a sequence of 9 binary PNG images at 64-by-64 pixel resolution.
dtIm = ARCOS::loadDataFromImages(file.path(system.file("testImagePatterns/movingMulti", package="ARCOS"), "png_64"), "*.png", thres = -1)
myPlotBinTS(dtIm, plotType = "raster", ncol = 3)
dtColl = ARCOS::trackColl(dtIm[m>0])
myPlotRasterColl(dtColl, ncol = 3)
Detect and track multiple activation sites from a sequence of 20 binary PNG images at 32-by-32 pixel resolution. Sprites created with Piskel web-app. Small random Gaussian noise added to X/Y positions.
dtIm = ARCOS::loadDataFromImages(file.path(system.file("testImagePatterns/4events", package="ARCOS"), "png_32"), "*.png", thres = -1) # add Gaussian noise to X/Y # Change the seed to explore other configurations set.seed(7) dtIm[, `:=`(x = x + rnorm(.N, 0, .1), y = y + rnorm(.N, 0, .1))]
myPlotBinTS(dtIm, xlim = c(0,25), ylim = c(25,0), plotType = "point", ncol = 5, pointSize = .25)
dtColl = ARCOS::trackColl(dtIm[m > 0], eps = 2.5) # Create convex hulls around collective events fro visualisation dtCollCH = dtColl[, .SD[grDevices::chull(x, y)], by = .(frame, collid)]
# Colour palette from ggthemes::tableau_color_pal(palette = "Tableau 10")(10) # to avoid dependency on ggthemes. myColorPal = c("#4E79A7", "#F28E2B", "#E15759", "#76B7B2", "#59A14F", "#EDC948", "#B07AA1", "#FF9DA7", "#9C755F", "#BAB0AC") p2 = ggplot(dtIm, aes(x = x, y = y)) + geom_point(aes(color = as.factor(m)), size = .25) + scale_color_manual(values = c("0" = "grey80", "1" = "grey20")) + ggnewscale::new_scale_color() + geom_point(data = dtColl, aes(color = as.factor(collid)), size = .1) + geom_polygon(data = dtCollCH, aes(color = as.factor(collid)), fill = NA, size = 0.5) + facet_wrap(~ frame, ncol = 5) + coord_fixed(ratio=1) + scale_x_continuous(limits = c(0, 25)) + scale_y_continuous(trans = "reverse", limits = c(25, 0)) + theme_void() + theme(text = element_text(size = 20), legend.position = "none") p2
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.