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) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Removed dependency on motAnalysis