--- title: "Optical Flow Analysis using R" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{optical_flow_tutorial} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This tutorial demonstrates how to track movement in gridded regions over successive frames using the R package imagefx. Applications for this type of analysis may include tracking granular flow experiments, clouds in satellite images, traffic patterns, or general movement in video frames.
The tutorial is broken into the following sections: 1. [Read in image data](#read) 1. [Grid Image](#grid) 1. [Track Motion in Grids](#track) 1. [Plot Grid Motion](#plot) We assume the user has R installed on their computer and has a basic familiarity with R syntax, base functions, and coding in general. To install the `imagefx` package from the R console use `install.packages('imagefx')` and then load with: ```{r setup} library(imagefx) ``` ## Read in image data Data used in this tutorial can be found on [github](https://github.com/alexjcwitsil/Optical-Flow.git). Navigate to the Optical Flow repository and then: 1. Click *Clone or download* 1. Click *Download Zip* 1. Use your computers software to extract the repository contents in a location you can easily navigate to 1. Open up RSTUDIO or an R terminal and navigate to the directory you specified above. For this tutorial I saved the optical_flow-Master repository on my Desktop but the reader should modify any code to reflect the location where they unpacked the zip file or to the location of the data of interest.
All the data we analyze are jpegs and thus require the 'jpeg' package, which can be installed using ```{r, eval=FALSE} install.packages('jpeg') ```
Now we can use the *imagefx* package and *jpeg* packages to read in some example image data. ```{r,eval=FALSE} library(imagefx) library(jpeg) ## identify where the image files are located ##img.dir <- '~/Desktop/optical_flow-master/example_datasets/fluidized_flow/' ##img.dir <- '~/Desktop/optical_flow-master/example_datasets/granular_flow/' img.dir <- '~/Desktop/optical_flow-master/example_datasets/traffic_flow/' ## list all the files within this directory img.files <- list.files(img.dir) ## choose the first file in the image files cur.file = img.files[1] ## read in the JPEG image cur.img <- readJPEG(paste(img.dir,cur.file,sep="")) ```
## Grid the Image Optical flow algorithms generally break an image into gridded regions whose motions will be tracked between successive frames. ```{r,fig.width=7,fig.height=5,eval=FALSE} ## at this point is important to rotate the image 90 degrees clockwise ## this is necessary because R reads in images with the top left corner as the reference frame. ## in other words, rotate the image so the origin is located in the bottom right img.rot <- t(apply(cur.img,2,rev)) ## define the domain of this image xdim <- nrow(img.rot) ydim <- ncol(img.rot) ## choose the number of grids in the x and y direction num.xgrid = 25 num.ygrid = 20 ## find the grid xs and ys grid.xs <- seq(1,xdim,length.out=num.xgrid+1) grid.ys <- seq(1,ydim,length.out=num.ygrid+1) ## plot the grid locations image(1:xdim,1:ydim,img.rot,col=gray.colors(20),asp=1) abline(v=grid.xs,col='red',lwd=0.5) abline(h=grid.ys,col='red',lwd=0.5) ``` ## Track Motion in Grids Now read in successive images, grid each image, and track the relative movement between frames. This is done with two nested loops that loop over each gridded region in the x and y direction. The relative movement between gridded regions in successive frames is accomplished by either `xocrr3d` or `pcorr3d`, which perform cross correlations and phase correlations, respectively. The choice in the algorithm will depend on the dataset used but, in general, phase correlations work better with high frequency images. ```{r,eval=FALSE} ## initilize a list to hold all the shifts in each grid over every image sequence pair shift.list <- list() ## loop over all the images ii=1 while(ii The object `shift.list` holds all the shift vectors (x,y) for every grid location and for every image sequence pair. With this list you can visualize the 'flow' of your data by plotting the vectors as arrows. This can be done for every frame or over the entire sequence by taking the average, which we do below: ```{r,fig.width=7,fig.height=5,eval=FALSE} ## convert each matix in the shift list into a data frame shift.list <- lapply(shift.list,as.data.frame) ## create a matrix of all the shift values in each list element all.shift.xs <- matrix(unlist(lapply(shift.list, "[", 3)),nrow=num.xgrid*num.ygrid) all.shift.ys <- matrix(unlist(lapply(shift.list, "[", 4)),nrow=num.xgrid*num.ygrid) ## take the row averages for all the shifts in the x and y direction avg.shift.xs <- rowMeans(all.shift.xs) avg.shift.ys <- rowMeans(all.shift.ys) ## take the x and y grid locations from an example shift element avg.xs = shift.list[[1]][,1] avg.ys = shift.list[[1]][,2] ## find the shifts that are greater than some minimum tolerance ## this is to avoid warnings when plotting arrows later on pos.shift.inds <- which(abs(avg.shift.xs) > 0.1 | abs(avg.shift.ys) > 0.1) ## limit the average shift xs, ys, and x and y locations to the positive indices pos.shift.xs <- avg.shift.xs[pos.shift.inds] pos.shift.ys <- avg.shift.ys[pos.shift.inds] pos.xs <- avg.xs[pos.shift.inds] pos.ys <- avg.ys[pos.shift.inds] ## what are the useres margins mar.org=par()$mar ## set the margins for this particular plot par(mar=c(0,0,0,0)) ## plot an example image image(1:xdim,1:ydim,img1,col=gray.colors(20),useRaster=TRUE,asp=1,axes=FALSE,xlab='',ylab='') ## return the margins to the users original values par(mar=mar.org) ## define how to scale the arrows (for easier visualization) arrow.scale = 5 ## define the arrow locations x0=pos.xs y0=pos.ys x1=x0+(pos.shift.xs * arrow.scale) y1=y0+(pos.shift.ys * arrow.scale) ## plot the motion indicating the average shift between frames arrows(x0=x0,y0=y0,x1=x1,y1=y1,length=0.05,lwd=2,angle=25,col='gray30') arrows(x0=x0,y0=y0,x1=x1,y1=y1,length=0.05,lwd=1,angle=25,col='red') ```