Creating an animating GIF

This is a simple example to illustrate how to write your own custom 'analysis' code and functions to integrate with mrTools-4.5. The function writes an animated gif file that shows the mean or median fMRI/EPI data over time in false color, superimposed on your inplane anatomies. Not particularly life-changing, but quite useful for (0) learning how to get started coding, (1) inspecting whether you inplane anatomies and EPI data are aligned (2) how bad distortions of the EPI data is compared to nondistorted anatomy and (3) impressing your audience with moving false-color images if they like that kind of thing.

Things you will need: most recent mrTools-4.5 install. svn update if you are not sure. The OSX command line tool gifmerge for turning individual gif images into animated gif. If you have FSL installed on your machine, you should have it. Try which gifmerge or gifmerge at the Terminal prompt. It's also handy to have the imagemagick convert tools available.

Overview of code

  1. Function definition
  2. Checking that all directories and unix tools are available on users machine
  3. Loading in previous analysis (timeSeriesStats) and the anatomy
  4. Getting information about number of slices, range of data, etc. using viewGet
  5. Set range of overlay colors
  6. Step through slices, render each one, grab image, save to tiff and convert to gif (because

matlab gif-writing is a pain)

  1. Glue all gif images together to make an animated gif
  2. Open the resulting image up in Safari (which can display animated gifs dynamically)
  3. Clean up by removing temporary files


1. Function definition

Usual pre-amble with some comments on how to use the function. You can save the function anywhere on your matlab path. Its only input is the mrLoadRet view. So to use the function, the user will have to keep hold of it:

v = newView;
mrSliceExportTest( v )

Open a new file called mrSliceExport in your editor and start adding to the file:

% mrSliceExportTest - output series of images across all slices 
%      usage: [ ] = mrSliceExportTest( v )
%         by: denis schluppeck
%       date: 2007-11-22
%        $Id: mrSliceExportTest.m,v 1.1 2008/01/25 17:12:20 ds1 Exp $:
%     inputs: v
%    outputs: 
%    purpose: export merged images of EPI data and underlying anatomy to an animated gif
%        e.g: v = mrLoadRet; mrSliceExport( v );
function [ ]=mrSliceExportTest( v )
% make sure user supplies input
if nargin < 1 
  help mrSliceExportTest
% get access to mrTools global variables 

2. Checking directories and unix tools are available

We assume that the user has been good and installed the imagemagick tools for converting images and the gifmerge program. Also he/she should have calculated the timeSeriesStats for the current scan/group. If not, the code just warns and returns:

% check that the the timeSeriesStats have been calculated
analysisdir = sprintf('%s/timeSeriesStats',viewGet(v,'datadir'));
if ~exist(analysisdir, 'dir')
  mrWarnDlg('(mrSliceExportTest) You need to calculate timeSeriesStats first')
% check that gifmerge and /usr/local/convert are installed for imageconversion
[status,result]=system('which convert');
if status == 0 % unix success
  mrDisp(sprintf('(mrSliceExportTest) imagemagick "convert" found: %s', result)); 
  mrWarnDlg('(mrSliceExportTest) You need to install imagemagick first...');
[status,result]=system('which gifmerge');
if status == 0 % unix success
  mrDisp(sprintf('(mrSliceExportTest) "gifmerge" found: %s', result));
  mrWarnDlg('(mrSliceExportTest) you need to install gifmerge first...');

3. Loading in previous analysis (timeSeriesStats) and the anatomy

The next lines of code just load in the data from the analysis performed previously. For good measure read in the anatomy image again; this also lets the user specify another anatomy image (maybe the volume anatomy):

% load in timeSeriesStatistics
v = loadAnalysis(v, 'timeSeriesStats', analysisdir);
% load anatomy 
v = loadAnat(v);

By changing the type of analysis you are loading in, you can obviously change the overlay. You could use similar code to superimpose e.g. functional maps on very high resolution anatomies by loading the corresponding files.

4. Getting information about number of slices, range of data, etc. using **viewGet**

This bit of code illustrates the power of the viewGet command - basically anything that you might ever want to query is accessible by the viewGet/viewSet commands. To see what's available for reading/writing type viewGet (or viewSet) at the matlab prompt and you will get a nicely formatted list of all the parameters. The code switches to the median view (which is the 2nd entry in the cell array of statistical maps), and gets various paramters. We then get the medianData and calculate its 5 and 95 percentiles to give the colormap a smaller range to display.

% the timeSeriesStats data contains several statistics; #2 is the median. 
% let's grab that.
v = viewSet(v, 'curoverlay', 2);
viewNum = viewGet(v,'viewnum');
curGroup = viewGet(v,'curgroup');
curScan = viewGet(v,'curscan');
curOverlay = viewGet(v,'currentoverlay');
% set the range of displayed median values to 5 and 95 percentile
medianData = viewGet(v,'overlaydata',curScan, curOverlay);
robustRange = prctile(medianData(medianData>0), [5 95]); 
% clip can handle n-d data, use nanclip to set values to nan (instead of cmin,max)
robustMedianData = nanclip(medianData, robustRange(1), robustRange(2));

5. Set range of overlay colors

Next set the display range used by the mrLoadRet GUI and the alpha transparency…

v = viewSet(v,'overlaymin', robustRange(1));
v = viewSet(v,'overlaymax', robustRange(2));
% for alpha transparency, have to go via mlrGuiSet -- is this bad?
mlrGuiSet(v,'alpha', 0.5);

6. Step through slices, render each one, grab image, save to tiff and convert to gif (because matlab gif-writing is a pain)

Get info on how many slices we need to process. Next create a temporary directory, which we will clean up again later.

% loop over slices and get images
nSlices = viewGet(v,'nslices');
% clear a variable
img = []; 
% make a temporary folder called '.tmptif'
tmpdir = sprintf('%s/.tmptif',viewGet(v,'datadir')); 
% ignore warnings about directory already existing!

Loop over slices, display, grab image, write to tiff and convert in place to gif (using the OSX command line tool).

for ii = 1:nSlices
  mlrGuiSet(v, 'slice', ii);
  disp(sprintf('..rendering slice %d to %s..',ii, tmpdir));
  img(:,:,1:3,ii) = refreshMLRDisplay(viewNum);
  mglWaitSecs(0.1); % let the GUI catch up 
  fname = sprintf('%sim%02d.tif', tmpdir,ii);
  imwrite(img(:,:,1:3,ii), fname , 'tif');
  % convert to gif right here.
  % could increase imagesize by adding an option
  % convertcmd = sprintf('convert %s  -resize 200% %s ' ,fname , strrep(fname, '.tif','.gif'));
  convertcmd = sprintf('convert %s %s', fname, strrep(fname, '.tif', '.gif'));

7. Glue all gif images together to make an animated gif

The unix tool gifmerge glues all of the gif frames together with some options: frame time 50, means 0.5s, -l0 means loop forever.

% gifmerge - merge all the gif images together.
animgifname = sprintf('%s/animatedGif.gif', viewGet(v,'datadir'));
mrDisp(sprintf('(mrSliceExport) writing animated gif to %s',animgifname))
system(sprintf('gifmerge -50 -l0 %s/*.gif > %s', tmpdir, animgifname ));

8. Open the resulting image up in safari (which can display animated gifs dynamically)

If you haven't used the Mac OSX command open yet, learn about it with man open in the Terminal. It's really useful for opening image files up in preview, text files in TextEdit, etc. (it's actually smart about filetypes, but we need to help it out here and point it to Safari browser).

% open in safari...
mrDisp(sprintf('(mrSliceExport) opening up in'))
system(sprintf('open -a Safari %s', animgifname));

9. Finally clean up and return

% clean up.
mrDisp(sprintf('(mrSliceExport) cleaning up temporary directoryn'))
system(sprintf('rm -r %s', tmpdir ));

10. The final product

Looks different depending on what kind of color map, clip values, and alpha transparency you specify. Here a simple example: