Last active
May 23, 2019 03:28
-
-
Save jirilukavsky/19c7614310e2c88a66e67e7ee2ad652c to your computer and use it in GitHub Desktop.
Make MOT videos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library(ggplot2) | |
library(animation) | |
library(circular) | |
# utility functions for trajectory generation ------------------- | |
# - these function came from motAnalysis package | |
random.positions <- function(n, xlim = c(-10, +10), ylim = xlim, | |
dot.radius = 0.5, min.dist = 1) { | |
pos <- list( | |
n = n, xlim = xlim, ylim = ylim, | |
x = runif(n, xlim[1], xlim[2]), | |
y = runif(n, ylim[1], ylim[2]), | |
radius = dot.radius | |
) | |
while (!valid.positions(pos, min.dist = min.dist)) { | |
pos$x <- runif(n, xlim[1], xlim[2]) | |
pos$y <- runif(n, ylim[1], ylim[2]) | |
} | |
class(pos) <- "positions" | |
return(pos) | |
} | |
valid.positions <- function(pos, min.dist = 1) { | |
distances <- dist(data.frame(pos$x, pos$y)) | |
distances <- as.numeric(distances) | |
valid.dist <- all(distances >= min.dist) | |
border.dist <- | |
(pos$x >= pos$xlim[1] + min.dist) & | |
(pos$x <= pos$xlim[2] - min.dist) & | |
(pos$y >= pos$ylim[1] + min.dist) & | |
(pos$y <= pos$ylim[2] + min.dist) | |
border.dist <- all(border.dist) | |
ok <- valid.dist & border.dist | |
return(ok) | |
} | |
vonmises.trajectory <- function(pos, speed = 5, secs = 10, fps = 100, | |
frame.step = 10, | |
initial.dir = numeric(pos$n), | |
kappa = 50, sep = 1) { | |
time <- seq(0, secs, 1 / fps) | |
n.frames <- length(time) | |
x <- matrix(0, nrow = n.frames, ncol = pos$n) * NA | |
y <- matrix(0, nrow = n.frames, ncol = pos$n) * NA | |
if (initial.dir[1] == "runif") { | |
dir <- runif(pos$n, min = 0, max = 2 * pi) | |
} else { | |
dir <- rep(1, pos$n) * initial.dir | |
# initial directions 0=up, pi/2=right (=CW) | |
} | |
# is there any other good default direction? => SEARCH | |
step <- speed / fps | |
for (f in 1:n.frames) { | |
if (f == 1) { # first frame from input positions | |
x[f, ] <- pos$x | |
y[f, ] <- pos$y | |
last.change <- 1 | |
} else { | |
if (!is.na(frame.step) & (f > last.change + frame.step)) { | |
# dir = runif(pos$n, min=0, max=2*pi) | |
jitt <- rvonmises(n = pos$n, mu = circular(0), kappa = kappa) | |
dir <- (dir + as.numeric(jitt)) %% (2 * pi) | |
last.change <- f | |
} | |
dir <- bounce(x[f - 1, ], y[f - 1, ], dir, step, pos$xlim, pos$ylim) | |
dir <- bounce.mutual(x[f - 1, ], y[f - 1, ], dir, step, sep = sep) | |
x[f, ] <- x[f - 1, ] + sin(dir) * step | |
y[f, ] <- y[f - 1, ] - cos(dir) * step | |
} | |
} | |
track <- list( | |
n = pos$n, xlim = pos$xlim, ylim = pos$ylim, | |
time = time, x = x, y = y | |
) | |
class(track) <- "trajectory" | |
return(track) | |
} | |
bounce <- function(x, y, dir, step, xlim = c(-10, +10), ylim = xlim) { | |
dir2 <- dir | |
x2 <- x + sin(dir) * step | |
y2 <- y - cos(dir) * step | |
too.R <- x2 > xlim[2] | |
too.L <- x2 < xlim[1] | |
too.U <- y2 < ylim[1] | |
too.D <- y2 > ylim[2] | |
corner <- (too.R | too.L) & (too.U | too.D) | |
side <- xor(too.R | too.L, too.U | too.D) | |
dir2[corner] <- (dir2[corner] + pi) %% (2 * pi) | |
dir2[too.R | too.L] <- 2 * pi - dir2[too.R | too.L] | |
dir2[too.U | too.D] <- (pi - dir2[too.U | too.D]) %% (2 * pi) | |
# bounce.inspect(x,y,dir,dir2,xlim,ylim) | |
return(dir2) | |
} | |
bounce.mutual <- function(x, y, dir1, step, sep = 1) { | |
# je to tak? http://en.wikipedia.org/wiki/Elastic_collision | |
n <- length(x) | |
dir2 <- dir1 | |
next.x <- x + sin(dir1) * step | |
next.y <- y - cos(dir1) * step | |
ro <- matrix(rep(1:n, n), n, n) | |
ro <- ro[lower.tri(ro)] | |
co <- matrix(rep(1:n, each = n), n, n) | |
co <- co[lower.tri(co)] | |
di <- as.matrix(dist(data.frame(next.x, next.y))) | |
di <- di[lower.tri(di)] | |
# print(co); print(ro); print(di) | |
colli <- which(di < sep) | |
for (i in colli) { | |
e1 <- ro[i] | |
e2 <- co[i] | |
dir2[c(e1, e2)] <- dir2[c(e2, e1)] | |
} | |
return(dir2) | |
} | |
# bounce.mutual(c(1,2,3), c(1,2,1), c(0,0,0), .1, sep=1.9) | |
snapshot.trajectory <- function(track, time, time.index = NA) { | |
if (is.na(time.index)) { | |
time.index <- which.min(abs(track$time - time)) | |
} | |
pos <- list( | |
n = track$n, xlim = track$xlim, ylim = track$ylim, | |
x = track$x[time.index, ], | |
y = track$y[time.index, ], | |
radius = 0.5 | |
) # TODO | |
class(pos) <- "positions" | |
return(pos) | |
} | |
# test generation code -------------------------------------------- | |
set.seed(101) | |
xy <- random.positions(8) | |
xyt <- vonmises.trajectory(xy, initial.dir = 3.14 * (1:8) / 4) | |
plot(xy) | |
plot(xyt) | |
# display functions --------------------------------------------- | |
xplot.positions <- function(pos, targets = NA, labels = F, | |
legend = F, expand = c(0, 1)) { | |
expand.add <- c(-1, +1) * expand[2] | |
expand <- as.numeric(na.omit(c(expand, 0, 0))) # add one or two zeros | |
width <- diff(pos$xlim) | |
height <- diff(pos$ylim) | |
margin.x <- width * expand[1] | |
margin.y <- height * expand[1] | |
xlim.new <- c( | |
pos$xlim[1] - margin.x - expand[2], | |
pos$xlim[2] + margin.x + expand[2] | |
) | |
ylim.new <- c( | |
pos$ylim[1] - margin.y - expand[2], | |
pos$ylim[2] + margin.y + expand[2] | |
) | |
n <- pos$n | |
d <- data.frame( | |
dot = factor(1:n), x = pos$x, y = pos$y, | |
type = "dot", stringsAsFactors = F | |
) | |
d$type[d$type != "target"] <- "distractor" | |
if (!any(is.na(targets))) { | |
d$type[targets] <- "target" | |
d$type[d$type != "target"] <- "distractor" | |
} | |
pp <- qplot(x, y, | |
data = d, | |
geom = "point", colour = type, | |
asp = 1, size = I(10) | |
) + | |
labs( | |
x = "", y = "", title = "", | |
colour = "", shape = "" | |
) + | |
coord_cartesian(xlim = xlim.new, ylim = ylim.new) + | |
scale_y_reverse() + | |
scale_color_manual(values = c("#AAAAAA", "#00FF00")) + | |
theme( | |
rect = element_rect(fill = "white"), text = element_blank(), | |
line = element_blank(), | |
panel.background = element_rect(fill = "white"), | |
plot.background = element_rect(fill = "white") | |
) | |
if (labels) { | |
pp <- pp + geom_text(aes(label = dot), | |
colour = I("black"), size = I(8) | |
) | |
} | |
# better or _no_ legend | |
pp <- pp + theme(legend.position = "none") | |
return(pp) | |
} | |
# xplot.positions(xy, targets = 1:3) | |
plot.trajectory.x <- function(tr, legend = F, expand = c(1, 1)) { | |
expand.add <- c(-1, +1) * expand[2] | |
xlim.new <- tr$xlim * expand[1] + expand.add | |
ylim.new <- rev(tr$ylim * expand[1] + expand.add) | |
n <- tr$n | |
d <- long.trajectory(tr) | |
pp <- qplot(x, y, | |
data = d, | |
geom = "point", colour = factor(object), asp = 1 | |
) + | |
labs(x = "", y = "", title = "") + | |
coord_cartesian(xlim = xlim.new, ylim = ylim.new) + | |
scale_y_reverse() | |
pp <- pp + theme(legend.position = "none") | |
pp <- pp + theme() | |
return(pp) | |
} | |
# plot.trajectory.x(xyt) | |
make.videox <- function(tt, fname, fps = 25, | |
outdir = getwd(), targets) { | |
tmin <- min(tt$time) | |
tmax <- max(tt$time) | |
tlen <- tmax - tmin | |
oopt <- ani.options( | |
interval = 1 / fps, nmax = fps * tlen * 2, | |
outdir = outdir, ani.width = 1920 / 2, ani.height = 1080 / 2 | |
) | |
saveVideo({ | |
for (i in 1:(2 * fps)) { | |
p <- snapshot.trajectory(tt, tmin) | |
print(xplot.positions(p, targets = targets)) | |
} | |
for (tim in seq(tmin, tmax, 1 / fps)) { | |
p <- snapshot.trajectory(tt, tim) | |
print(xplot.positions(p, targets = NA)) | |
} | |
for (i in 1:(2 * fps)) { | |
p <- snapshot.trajectory(tt, tmax) | |
print(xplot.positions(p, targets = targets)) | |
} | |
}, video.name = fname, other.opts = "-pix_fmt yuv420p -b 300k", clean = T) | |
ani.options(oopt) | |
} | |
# making videos ------------------------------------------------------------ | |
ani.options(ffmpeg = "CHANGE/Apps/ffmpeg/ffmpeg") # TODO | |
xy1 <- random.positions(8) | |
xyt1 <- vonmises.trajectory(xy1, speed = 5, initial.dir = "runif") | |
make.videox(xyt1, "video_s05_n1_01.mp4", targets = 1) | |
xy2 <- random.positions(8) | |
xyt2 <- vonmises.trajectory(xy2, speed = 10, initial.dir = "runif") | |
make.videox(xyt2, "video_s10_n1_01.mp4", targets = 1) | |
xy3 <- random.positions(8) | |
xyt3 <- vonmises.trajectory(xy3, speed = 5, initial.dir = "runif") | |
make.videox(xyt3, "video_s05_n4_01.mp4", targets = 1:4) | |
xy4 <- random.positions(8) | |
xyt4 <- vonmises.trajectory(xy4, speed = 10, initial.dir = "runif") | |
make.videox(xyt4, "video_s10_n4_01.mp4", targets = 1:4) | |
xy5 <- random.positions(8) | |
xyt5 <- vonmises.trajectory(xy5, speed = 10, initial.dir = "runif") | |
make.videox(xyt5, "video_s10_n4_02.mp4", targets = 1:4) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Removed dependency on motAnalysis