R package for motion capture data analysis and visualisation - r
I am a newbie in R, love it, but I am surprised by a complete lack of solid package to analyse motion capture data.
The simplest motion capture file is just a massive table with 'XYZ' coordinates for each point attached to a recorded subject, and for every frame captured. I know that I can find individual methods and functions in R to perform complex operations (like principal component analysis) or I can plot time series for all the points. But when I am looking for examples that could also educate me statistically about analysing human movement, and provide with nice toolbox for visual representation of data, R turns out to be a cold desert. On the other hand, MATLAB has Motion capture toolbox and MoCap Toolbox and especially the latter has quite good options for plotting and analysing the captures. But let's be honest - MATLAB has quite ugly visualisation engine comparing to R.
Some specific requests for R motion capture package would include:
reading, editing, visualizing and transforming mocap data
kinetic and kinematic analysis
time-series and principal component analysis
animating data
Am I missing something here (in my Googling) or is there really no mocap packages out there for R? Have anyone tried playing with motion capture data in R? Can you give me some directions?
UPDATE, December 2019: It seems like the mocapr package by Steen Harsted is a much more powerful tool than the one I built. Enjoy.
Have a look at my package, the mocap package:
https://github.com/gsimchoni/mocap
It is far from perfect but it's a start, currently tested only on CMU Graphics Lab Motion Capture Database ASF/AMC files.
And here is a blog post with some more details.
I used the package rgl to create an animation from a motion gesture dataset. Although it's not a package made specifically for gesture data, you can work with it.
In the example below, we have gesture data for 8 points on the upper body: spine, shoulder center, head, left shoulder, left wrist, right shoulder, and right wrist. The subject has his hands down and his right arm is making an upward movement.
I restricted the dataset to 6 time observations (seconds, if you will), because otherwise it would get to big to post here.
Each line from the original dataset corresponds to a time observation, and the coordinates of each body point are defined in sets of 4 (every four columns is one body point). So at each line, we have "x", "y", "z", "br" for the spine, then "x", "y", "z", "br" for the shoulder center, and so on. The "br" is always 1, in order to separate the three coordinates (x,y,z) of each body part.
Here is the original (restricted) dataset:
DATA.time.obs<-rbind(c(-0.06431,0.101546,2.990067,1,-0.091378,0.165703,3.029513,1,-0.090019,0.518603,3.022399,1,-0.042211,0.687271,2.987086,1,-0.231384,0.419869,2.953286,1,-0.299824,0.173991,2.882627,1,0.063367,0.399478,3.136306,1,0.134907,0.176191,3.159998,1),
c(-0.067185,0.102249,2.990185,1,-0.095083,0.166589,3.028688,1,-0.093098,0.519146,3.019775,1,-0.043808,0.687041,2.987671,1,-0.234622,0.417481,2.94581,1,-0.300324,0.169313,2.869782,1,0.056816,0.398384,3.135578,1,0.134536,0.180875,3.162843,1),
c(-0.069282,0.102964,2.989943,1,-0.098594,0.167465,3.027638,1,-0.097184,0.52169,3.019556,1,-0.046626,0.695406,2.989244,1,-0.23478,0.417057,2.943475,1,-0.300101,0.168628,2.860515,1,0.053793,0.395444,3.143226,1,0.134175,0.182816,3.172053,1),
c(-0.070924,0.102948,2.989369,1,-0.101156,0.167554,3.026474,1,-0.100244,0.522901,3.018919,1,-0.049834,0.696996,2.987933,1,-0.235301,0.416329,2.939331,1,-0.301339,0.170203,2.85497,1,0.04762,0.390872,3.142792,1,0.14041,0.186844,3.182172,1),
c(-0.071973,0.103372,2.988788,1,-0.103215,0.16776,3.025409,1,-0.102334,0.52281,3.019341,1,-0.051298,0.697003,2.991192,1,-0.235497,0.414859,2.935161,1,-0.297678,0.15788,2.833734,1,0.045973,0.386249,3.147609,1,0.14408,0.1916,3.204443,1),
c(-0.073223,0.104598,2.988132,1,-0.106597,0.168971,3.022554,1,-0.106778,0.522688,3.015138,1,-0.051867,0.697781,2.990767,1,-0.236137,0.414773,2.931317,1,-0.297552,0.153462,2.827027,1,0.039316,0.39146,3.166831,1,0.175061,0.214336,3.207459,1))
For each time point, we can create a matrix where each row will be a body point, and the columns will be the coordinates:
# Single time point for analysis
time.point<-1
# Number of coordinates
coordinates<-4
# Number of body points
body.points<-dim(DATA.time.obs)[2]/coordinates
# Total time of gesture
total.time<-dim(DATA.time.obs)[1]
# Transform data for a single time. observation into a matrix
DATA.matrix<-matrix(DATA.time.obs[1,],c(body.points,coordinates),byrow = TRUE)
colnames(DATA.matrix)<-c("x","y","z","br")
rownames(DATA.matrix)<-c("hip_center","spine","shoulder_center","head",
"left_shoulder","left_wrist","right_shoulder",
"right_wrist")
So, we have, at each point of time, a matrix like this:
x y z br
hip_center -0.064310 0.101546 2.990067 1
spine -0.091378 0.165703 3.029513 1
shoulder_center -0.090019 0.518603 3.022399 1
head -0.042211 0.687271 2.987086 1
left_shoulder -0.231384 0.419869 2.953286 1
left_wrist -0.299824 0.173991 2.882627 1
right_shoulder 0.063367 0.399478 3.136306 1
right_wrist 0.134907 0.176191 3.159998 1
And now we use rgl to plot the data from this matrix:
#install.packages("rgl")
library(rgl)
# INITIAL PLOT
x<-unlist(DATA.matrix[,1])
y<-unlist(DATA.matrix[,2])
z<-unlist(DATA.matrix[,3])
# OPEN A BLANK 3D PLOT AND SET INITIAL NEUTRAL VIEWPOINT
open3d()
rgl.viewpoint(userMatrix=rotationMatrix(0,0,0,0))
# SET FIGURE POSITION
# This is variable. It will depend on your dataset
# I've found that for this specific dataset a rotation
# of -0.7*pi on the Y axis works
# You can also plot and select the best view with
# your mouse. This selected view will be passed on
# to the animation.
U <- par3d("userMatrix")
par3d(userMatrix = rotate3d(U, -0.7*pi, 0,1,0))
# PLOT POINTS
points3d(x=x,y=y,z=z,size=6,col="blue")
text3d(x=x,y=y,z=z,texts=1:8,adj=c(-0.1,1.5),cex=0.8)
# You can also plot each body point name.
# This might be helpful when you don't know the
# initial orientation of your plot
# text3d(x=x,y=y,z=z,texts=rownames(DATA.matrix),
# cex=0.6,adj=c(-0.1,1.5))
# Based on the plotted figure, connect the line segments
CONNECTOR<-c(1,2,2,3,3,4,3,5,3,7,5,6,7,8)
segments3d(x=x[CONNECTOR],y=y[CONNECTOR],z=z[CONNECTOR],col="red")
Then, we have this:
To create an animation, we can put all this into a function and use lapply.
movement.points<-function(DATA,time.point,CONNECTOR,body.points,coordinates){
DATA.time<-DATA[time.point,]
DATA.time<-matrix(DATA.time,c(body.points,coordinates),byrow = TRUE)
x<-unlist(DATA.time[,1])
y<-unlist(DATA.time[,2])
z<-unlist(DATA.time[,3])
# I used next3d instead of open3d because now I want R to plot
# several plots on top of our original, creating the animation
next3d(reuse=FALSE)
points3d(x=x,y=y,z=z,size=6,col="blue")
segments3d(x=c(x,x[CONNECTOR]),y=c(y,y[CONNECTOR]),z=c(z,z[CONNECTOR]),col="red")
# You can control the "velocity" of the animation by changing the
# parameter below. Smaller = faster
Sys.sleep(0.5)
}
I know this solution is not elegant, but it works.
Judging by a quick search on RSeek, there isn't a motion capture package available for R. It looks like you'll need to find equivalents for each function. The more general ones should be fairly easy to find (interpolation, subsetting, transformation/ projection, time-series analysis, pca, matrix analysis etc) and the very process of writing your own custom functions for specific things like estimating instantaneous kinetic energy is probably the best way to learn!
You may find plyr useful for knocking the data into shape and the animation package for visualising motion.
Related
R - counting the number of objects in an image using bwlabel
i'm rather new to image analysis in R and was wondering how i can assess the number of individual plants within a picture such as this one: I thought of converting the picture to a black/white picture and then using the bwlabel function to count the number of objects within the picture like this: R<-R(image) G<-G(image) B<-B(image) ExGreen<-2*G-R-B plot(ExGreen) ExGreen<-threshold(ExGreen,thr = "auto",approx=FALSE,adjust=1) plot(ExGreen) ExGreen<-clean(ExGreen,10) plot(ExGreen) labels=bwlabel(ExGreen) max(labels) However, I'm running into the issue that my white colored potato plants do not always form 1 contiguous unity. I was therefore wondering whether there is some option to connect the white pixels which are very close to each other or whether it is possible to draw a circle around every potato plant and then using the bwlabel function... Or is there any other option to solve my problem. Thanks in advance!
I was not aware that R has a package imager for image processing that already has a good deal of builtin functions for solving this problem. Thanks for pointing me to it through this interesting question. Here is my solution (beware that some thresholds are hard coded and thus not scale invariant!): library(imager) image <- load.image("plants.png") R<-R(image); G<-G(image); B<-B(image) ExGreen<-2*G-R-B plot(ExGreen) # blur before thresholding to fill some gaps ExGreen <- isoblur(ExGreen, 3) ExGreen <- threshold(ExGreen, thr="auto", approx=FALSE, adjust=1) plot(ExGreen) # split into connected component and keep only large CCs ccs <- split_connected(ExGreen) largeccs <- purrr::keep(ccs, function(x) {sum(x) > 800}) plot(add(largeccs)) # count CCs cat(sprintf("Number of large CCs: %i\n", length(largeccs)))
R plotting strangeness with large dataset
I have a data frame with several million points in it - each having two values. When I plot this like this: plot(myData) All the points are plotted, but the plot is quite busy, so I thought I'd plot it as a line: plot(myData, type="l") But while the x axis doesn't change (i.e. goes from 0 to 7e+07), the actual plotting stops at about 3e+07 and I don't actually get a proper line plot either. Is there a limitation on line plotting? Update If I use plot(myData, type="h") I get correct and useable output, but I still wonder why the type="l" option fails so badly. Further update I am plotting a time series - here is one output using type="h": That's perfectly usable, but having a line would allow me to compare several outputs.
High dimensional data graphic representation is growing issue in data analysis. The problem, actually, is not create the graph. The problem is make the graph capable of communicate information that we could transform in useful knowledge. Allow me to present an example to produce this point, by considering a data with a million observations, that is, not that big. x <- rnorm(10^6, 0, 1) y <- rnorm(10^6, 0, 1) Let's plot it. R can yes easily manage such a problem. But can we? Probably not. Afterall, what kind of information can we deduce from an ink hard stain? Probably, no more than a tasseographyst trying to divinate the future in patterns of tea leaves, coffee grounds, or wine sediments. plot(x, y) A different approach is represented by the smoothScatter function. It creates a density plot of bivariate data. There, we create two examples. First, with defaults. smoothScatter(x, y) Second, the bandwidth was specified to be a little larger than the default, and five points are specified to be shown using a different symbol pch = 3. smoothScatter(x, y, bandwidth=c(5,1)/(1/3), nrpoints=5, pch=3) As you can see, the problem is not solved. Nevertheless, we can have a better grasp on the distribution of our data. This kind of approach is still in development, and there are several matters that are discussed and evolved. If this approach represents a more suitable approach to represent your big dataset, I suggest you to visit this blog that discuss throughfully the issue.
For what it's worth, all the evidence I have is that is computer - even though it was a lump of big iron - ran out of memory.
Make density cloud from point cloud
My question consists of two sub questions. I have a graphical illustration presenting (some virtual) worst case scenarios sampled from history organized based on two parameters. Image: At this moment I have a point cloud. I would like to create nicely splined density cloud of my results. I would like the 3d spline to consider density of points when aproximating (so aproximate further around when there are less samples availabe and more exactly in more dense region of space) Because then, having that density cloud, I would be able scale the density in each vertical line specified by the two input parameters, and that would make it a likehood function of each outcome - [the worst case scenario]) Second part is, I would like to plot it, at best as semi-transparent 3d-regions that would be forming sometihng like a fog around the most dense region. Uh,wow.. that wasn't easy to explain. Sigh. :) Thanks for reading that far.
So here is a way to generate 3D density plots using the ks package. Since you provided no data this example is taken directly from the documentation to plot(...) in the ks package library(MASS) library(ks) x <- iris[,1:3] H.pi <- Hpi(x, pilot="samse") fhat <- kde(x, H=H.pi, compute.cont=TRUE) plot(fhat, drawpoints=TRUE)
Visualizing Clusters
I have done cluster analysis using hclust(), and now I wish to plot those clusters on to a map. X and Y coordinates of data are available. Finally I have x,y coordinate of the data point, and I have the specific number(cluster to which the data belongs) that must be visible at that specific point. I have plotted Dendrogram already but I wish to plot points using x,y coordinates available. Suppose a data belongs to the group: 1 and it's coordinates are x1 and y1, I wish to mark/label the point as 1 showing that this specific point belongs to cluster: 1. Please tell me how to do so, if any better representation of clusters on maps is also available, please tell me about it too. I am really new to R.
I would suggest coloring each point as the cluster. I am particularly fond of the beautiful graphics in ggplot2 one of the more popular graphics packages. library(ggplot2) dat<-data.frame(x=rnorm(10), y=rnorm(10), cluster=rep(c("a","b"),5)) qplot(x=x,y=y,data=dat,color=cluster) You can do something similar with base R graphics, but the resulting graph is much less attractive IMO. You could certainly "beautify" it, but the simplicity of 1 liner in ggplot2 and the beauty of the output seem to make more sense to me, from a return on code perspective. plot(x=dat$x, y=dat$y,col=dat$cluster) legend("topleft",c("a","b"),cex=1,text.col=c("black","red"),bty="n")
R: update plot [xy]lims with new points() or lines() additions?
Background: I'm running a Monte Carlo simulation to show that a particular process (a cumulative mean) does not converge over time, and often diverges wildly in simulation (the expectation of the random variable = infinity). I want to plot about 10 of these simulations on a line chart, where the x axis has the iteration number, and the y axis has the cumulative mean up to that point. Here's my problem: I'll run the first simulation (each sim. having 10,000 iterations), and build the main plot based on its current range. But often one of the simulations will have a range a few orders of magnitude large than the first one, so the plot flies outside of the original range. So, is there any way to dynamically update the ylim or xlim of a plot upon adding a new set of points or lines? I can think of two workarounds for this: 1. store each simulation, then pick the one with the largest range, and build the base graph off of that (not elegant, and I'd have to store a lot of data in memory, but would probably be laptop-friendly [[EDIT: as Marek points out, this is not a memory-intense example, but if you know of a nice solution that'd support far more iterations such that it becomes an issue (think high dimensional walks that require much, much larger MC samples for convergence) then jump right in]]) 2. find a seed that appears to build a nice looking version of it, and set the ylim manually, which would make the demonstration reproducible. Naturally I'm holding out for something more elegant than my workarounds. Hoping this isn't too pedestrian a problem, since I imagine it's not uncommon with simulations in R. Any ideas?
I'm not sure if this is possible using base graphics, if someone has a solution I'd love to see it. However graphics systems based on grid (lattice and ggplot2) allow the graphics object to be saved and updated. It's insanely easy in ggplot2. require(ggplot2) make some data and get the range: foo <- as.data.frame(cbind(data=rnorm(100), numb=seq_len(100))) make an initial ggplot object and plot it: p <- ggplot(as.data.frame(foo), aes(numb, data)) + layer(geom='line') p make some more data and add it to the plot foo <- as.data.frame(cbind(data=rnorm(200), numb=seq_len(200))) p <- p + geom_line(aes(numb, data, colour="red"), data=as.data.frame(foo)) plot the new object p
I think (1) is the best option. I actually don't think this isn't elegant. I think it would be more computationally intensive to redraw every time you hit a point greater than xlim or ylim. Also, I saw in Peter Hoff's book about Bayesian statistics a cool use of ts() instead of lines() for cumulative sums/means. It looks pretty spiffy: