Matlab animations tutorial

Matlab icon

Below is a PDF of my presentation and the code for a tutorial on making elaborate multi-axes movies with Matlab. The tutorial was part of the "hallway salon" series that has become a tradition in the Daniel Lab.

The code contained on this page contains a framework for saving movies from MATLAB that should be easily adaptable for your own work. I tried to add as many comments in the code, so it should be fairly self-explanatory.

Code output: an AVI file

The embedded video below shows you the output of the code featured on this page. (It's of much lower quality than the AVI file you would get on your computer by running the script.)

MATLAB code in chunks

Copy the following pieces of code into your MATLAB editor. You should create two new m files. Copy cells 1 to 4 (below) into one document, saved as something like "myanimation.m". The other file has to be named "plotfilledcircle.m". This is a helper function for plotting a circle.

Cell 1: Creating data for our animation

This piece of code just creates the path for our circle to move along; a simple spiral. There are two vectors, xpos and ypos, that contain the center coordinates of our circle for each frame. The last line in the listing below creates a vector that defines the circle's radius for each frame.
  1. %% Create test data for our animation: a dot moving along a path
  2. % Our aim is to make a circle move around an image along a specified path.
  3. % First, we'll create that path (xpos and ypos)
  4.  
  5. revolution_no = 5; % how often around the circle
  6. ang_resolution = 40; % how many points (i.e. frames) per circle
  7. da = 2*pi/ang_resolution; % delta angle
  8.  
  9. t = 0:da:revolution_no*2*pi; % time steps
  10.  
  11. % why not a spiral:
  12. pathradius = linspace(0,10,length(t)); % path radius increases each dt
  13. xpos = pathradius.*cos(t);
  14. ypos = pathradius.*sin(t);
  15.  
  16. % show what we have so far:
  17. figure(1);
  18. subplot(1,3,1:2)
  19. plot(t,xpos, '-k'); hold on;
  20. plot(t,ypos, '-r');
  21. set(gca, 'XLim', [0 max(t)]); box off;
  22. xlabel('time steps'); ylabel('x (black) and y (red) pos')
  23.  
  24. subplot(1,3,3)
  25. plot(xpos,ypos,'k'); axis equal; axis off;
  26. title('The path our circle will move along');
  27.  
  28. % We now want to use these x-y coordinates to place a cirle on an image.
  29. % Each iteration, we want the position to be updated so that it appears as
  30. % if the circle moved around the specified path.
  31. % In addition, we want the circle diameter to change as it goes along.
  32.  
  33. circlesize = linspace(0.2,2, length(t)); % circle size increases linearly

Cell 2: Plotting a circle that moves along the path specified above

This piece of code draws a circle on a figure with white background. It allows us to check whether everything looks OK, before going to the next cell in which we'll save the result of each iteration into a TIFF stack. This code depends on the helper function called "plotfilledcircle". Copy the code for this function (found a few paragraphs further down) into it's own m file. It has to be saved with the function's name (plotfilledcircle.m) and should either be in the same directory as your other m file, or in a directory that MATLAB knows about (i.e. in its path).
  1. %% Test the moving circle and save it as an image sequence
  2. % We'll now plot a circle for each time steps, according to the path and
  3. % size specifications above. I created a separate function to create the
  4. % circle, called 'plotfilledcircle'.
  5.  
  6. axlim = 100;
  7. figure('Position', [100 100 400 400]); % match goal dimensions;
  8. for c = 1:length(t)
  9. fprintf('Frame: %03d\n', c); % display counter
  10.  
  11. ph = plotfilledcircle(circlesize(c), [xpos(c) ypos(c)]); % plot circle
  12. axis off;
  13. % we need to set the axes to an appropriate limit, otherwise they'll
  14. % resize to always show the full circle:
  15. axis([-axlim axlim -axlim axlim]);
  16. axis square;
  17.  
  18. pause(0.05); % pause a bit to see animation
  19. end

Cell 3: same as cell 2, but now we save an image sequence

  1. %% Same as above, but now we save it as an image sequence (TIFF stack)
  2. %
  3.  
  4. axlim = 15;
  5. figh=figure('Position', [100 100 400 400]); % match goal dimensions
  6. for c = 1:length(t)
  7. fprintf('Frame: %03d\n', c); % display counter
  8.  
  9. ph=plotfilledcircle(circlesize(c), [xpos(c) ypos(c)]); % plot circle
  10. axis off;
  11. % we need to set the axes to an appropriate limit, otherwise they'll
  12. % resize to always show the full circle:
  13. axis([-axlim axlim -axlim axlim]);
  14. axis square;
  15.  
  16. % We'll save this animation as a tiff stack, so we can load it
  17. % back later for our grand finale
  18. currentframe = frame2im(getframe(figh)); % convert fig into image data
  19. currentframe = im2bw(currentframe,0.4); % make it binary to save space
  20. pause(0.1); % added to prevent skipped frames
  21. if c == 1
  22. imwrite(currentframe, 'tiffstack.tiff', 'WriteMode', 'overwrite');
  23. else
  24. imwrite(currentframe, 'tiffstack.tiff', 'WriteMode', 'append');
  25. end
  26.  
  27. pause(0.2); % pause a bit to see animation
  28. end

Cell 4: Putting it all together to make a multi-axes figure and save an AVI file

The following, long piece of code is the recipe for creating a movie file that I use. There are multiple ways of doing this, but using the avifile() and addframe() functions works well for me...
  1. %% Putting it all together
  2.  
  3. % We now want a fancy animation that includes the circle object, as well
  4. % as its position and size data in the form of 'evolving' graphs.
  5. %
  6. % At this point you should think about what resolution your final movie
  7. % might have. This depends on what you want to do. If you need a full-size
  8. % slide for an animation, you might as well just create the animation at
  9. % that size. Chances are, it looks better if PowerPoint doesn't have to
  10. % scale the movie.
  11. % If it's for an HD segment of a movie, use HD sizes (eg. 720p: 1280x720px
  12. % or 1080p: 1920x1080px). HD will result in HUGE movie files if no
  13. % compression is used. Here we'll use 1024x768 pixels, which is the native
  14. % resolution of many projectors and the default on my Keynote documents.
  15. %
  16. % We also want to think about where to place the individual graphs
  17. % It helps to draw it out on a piece of paper.
  18. % For the purpose of this tutorial, we want four axes:
  19. % 1) fill plot of the circle (just like above, but with diff. colors)
  20. % 2) plot of the images we exported above
  21. % 3) evolving graph of the circle size
  22. % 4) evolving graph of the circle's X and Y position
  23. %
  24. % I like to start by setting up the positions of all those elements:
  25.  
  26. fig_pos = [100 40 1024 768]; % position and size of the figure window
  27.  
  28. fillplot_ax_pos = [80 320 400 400]; % position and size of fill plot
  29. image_ax_pos = [580 320 400 400]; % image plot
  30. axlim = 15;
  31. sizedata_ax_pos = [50 170 1024-60 70]; % circle size graph
  32. posdata_ax_pos = [50 50 1024-60 100]; % circle position graph
  33.  
  34. % It's also often useful to define all the colors with variables set up
  35. % top:
  36.  
  37. fig_col = [1 1 1]; % figure background color
  38. text_col = [0 0 0]; % text color
  39. light_grey = [.4 .4 .4];
  40. dark_grey = [.2 .2 .2];
  41. nice_blue = [51/255 51/255 102/255];
  42. light_red = [.6 .4 .4];
  43.  
  44.  
  45. % I like to have a little if-statement, so that only when I set a flag to 1
  46. % will a movie file be created. Set to zero for setting stuff up. When
  47. % you're ready, set it to one.
  48. movieflag = 0;
  49. moviefilename = 'tutorialmovie.avi';
  50.  
  51. % only if our flag is set to 1, will we open up a movie object:
  52. if movieflag == 1
  53. aviobj = avifile(moviefilename, 'fps', 15, 'compression', 'none');
  54. end
  55.  
  56. startframe = 100; endframe = 120; % which frames to plot (useful for debugging)
  57. % startframe = 1; endframe = length(t); % complete loop
  58.  
  59. fh= figure('color', fig_col, 'name', 'Tutorial animation movie', ...
  60. 'Position', fig_pos);
  61. fprintf('\nWe are entering the loop...\n');
  62. % *************** START THE BIG LOOP AFTER CREATING FIG. ***************
  63. for k = startframe:endframe
  64.  
  65. titlestr = sprintf('Frame: %03d', k); % our title will change
  66. fprintf('Frame %d\n', k); % print loop counter
  67.  
  68.  
  69. % *************** 1) FILL PLOT AXES ***************
  70. fillplot_ax = axes;
  71. set(fillplot_ax, 'Units', 'pixels', 'Position', fillplot_ax_pos);
  72.  
  73. p1h(1)=plot(xpos,ypos, '-.');
  74. set(p1h(1), 'Color', light_grey);
  75. hold on;
  76.  
  77. p1h(2) = plotfilledcircle(circlesize(k), [xpos(k) ypos(k)], nice_blue); % plot circle
  78.  
  79. p1h(3) = line([0 xpos(k)], [0 ypos(k)]);
  80. set(p1h(3), 'Color', light_red, 'LineWidth', 3);
  81. hold off;
  82. % we need to set the axes to an appropriate limit, otherwise they'll
  83. % resize to always show the full circle:
  84. axis([-axlim axlim -axlim axlim]);
  85. axis square;
  86. th=title(titlestr);
  87. xlabel('x pos'); ylabel('y pos');
  88. set(th, 'FontSize', 14); % make title font larger
  89.  
  90.  
  91. % *************** 2) TIFF IMAGE IMPORT PLOT AXES ***************
  92. image_ax = axes;
  93. set(image_ax, 'Units', 'pixels', 'Position', image_ax_pos);
  94. try
  95. img = imread('tiffstack.tiff', k); % load image of current index
  96. catch ME1
  97. % Get last segment of the error message identifier.
  98. idSegLast = regexp(ME1.identifier, '(?<=:)\w+$', 'match');
  99. disp(idSegLast);
  100. error('Failed loading tiff image');
  101. end
  102. img = xor(1,img); % invert image to make it more exciting... XOR rules!
  103. imagesc(img); colormap gray; axis off; axis image;
  104.  
  105. th=title('Inverted image from TIFF stack');
  106. set(th, 'FontSize', 14);
  107.  
  108.  
  109.  
  110. % *************** 3) SIZE DATA PLOT AXES ***************
  111. sizedata_ax = axes;
  112. set(sizedata_ax, 'Units', 'pixels','Position', sizedata_ax_pos);
  113.  
  114. p2h(1)=plot(t,circlesize,'--');
  115. set(p2h(1), 'Color', light_grey, 'LineWidth', 1);
  116. % create evolving data graph:
  117. hold on;
  118. p2h(2)=plot(t(1:k),circlesize(1:k),'-');
  119. set(p2h(2), 'Color', nice_blue, 'LineWidth', 2);
  120.  
  121. hold off;
  122. ylabel('dot size','fontsize',12);
  123.  
  124. set(sizedata_ax, 'XLim', [0 max(t)]);
  125. set(gca, 'FontSize', 12);
  126.  
  127.  
  128. % *************** 4) POSITION DATA PLOT AXES ***************
  129.  
  130. posdata_ax = axes;
  131. set(posdata_ax, 'Units', 'pixels', 'Position', posdata_ax_pos);
  132.  
  133. p3h(1)=plot(t,xpos,'-.');
  134. set(p3h(1), 'Color', light_grey, 'LineWidth', 1);
  135. hold on;
  136. p3h(2)=plot(t,ypos,'--');
  137. set(p3h(2), 'Color', light_red, 'LineWidth', 1);
  138.  
  139. p3h(3)=plot(t(1:k), xpos(1:k),'-');
  140. set(p3h(3), 'Color', dark_grey, 'LineWidth', 2);
  141.  
  142. p3h(4)=plot(t(1:k), ypos(1:k),'-');
  143. set(p3h(4), 'Color', light_red, 'LineWidth', 2);
  144. set(posdata_ax, 'XLim', [0 max(t)]);
  145.  
  146. xlabel('time','fontsize',12); ylabel('dot pos','fontsize',12);
  147.  
  148. % Hack to move xlabel in line with the upper one (try without):
  149. ylabh = get(gca,'YLabel');
  150. set(ylabh,'Position', get(ylabh,'Position') + [.4 0 0]);
  151.  
  152. set(gca, 'FontSize', 12);
  153.  
  154.  
  155.  
  156. % *************** FINISH THE FRAME (AVI if selected) ***************
  157. pause(.2);
  158. if movieflag == 1
  159. frame = getframe(gcf); % capture current figure
  160. aviobj = addframe(aviobj,frame); % use addframe to append frame
  161. end
  162. if k < endframe
  163. clf; % clear figure except for very last frame
  164. end
  165.  
  166. end % of the big loop
  167. fprintf('\nDone looping...\n');
  168.  
  169. if movieflag == 1
  170. fprintf('Saving movie...\n\n');
  171. aviobj = close(aviobj);
  172. end

Helper function: plotfilledcircle.m

Save this file as "plotfilledcircle.m".
  1. function ph = plotfilledcircle(circle_radius,circlecenter, fcol)
  2. %
  3. % plotfilledcircle(circle_radius,circlecenter, fcol)
  4. %
  5. % Function to plot a filled circle with radius 'circle_radius'
  6. % 'circlecenter' ... center location [0 0] is default
  7. % 'fcol' is optional and defines the face color (black default)
  8. %
  9. % Armin H 2011
  10.  
  11. if nargin < 2
  12. circlecenter = [0 0];
  13. end
  14. if nargin < 3
  15. fcol = [0 0 0];
  16. end
  17.  
  18. theta = linspace(0,2*pi,100); % 100 points between 0 and 2pi
  19. x = circle_radius*cos(theta) + circlecenter(1);
  20. y = circle_radius*sin(theta) + circlecenter(2);
  21. ph = fill(x, y, 'k');
  22. set(ph, 'FaceColor', fcol);
  23. box off; axis equal;
  24.  
  25. end
Category: 
Code

Comments

Hi, if I want to create a ball with a 'tail' that moves in asymmetrical sinusodial fashion, is there a way to track both the position of the ball and the positions along the tail? Ideally the ball/string would move across the video frame as if it were a moving sperm cell with a head and flagellum. Please let me know if that is possible! Thank you!

Hi there! It's certainly possible, although I don't know off-hand how to do it. (I haven't used Matlab in a while either.) I'd first see if you can create some sort of damped sine wave that looks like flagellar motion, but is 'static', i.e. it wiggles, but doesn't move forward. (https://en.wikipedia.org/wiki/Damped_sine_wave) The amplitude of where the (for now fixed) head is should be zero. If you just want to move from left to right, it should be easy: you can just offset all the calculated values of the sine for each time point, so that they move across the window with the ball. If the motion of the ball is more complicated, it get's trickier. You'd probably have to find the normal to each point on the tail, and then calculate an offset along that normal... too bad I don't have time to try it out now. Sounds like a fun problem. Sorry I couldn't be of more help and good luck!