Skip to content

Commit bbd681e

Browse files
RF Brain.save_movie(): use imageio library
1 parent 40621da commit bbd681e

File tree

3 files changed

+54
-44
lines changed

3 files changed

+54
-44
lines changed

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,6 @@ def check_dependencies():
9292
platforms='any',
9393
packages=['surfer', 'surfer.tests'],
9494
scripts=['bin/pysurfer'],
95-
install_requires=['nibabel >= 1.2'],
95+
install_requires=['nibabel >= 1.2',
96+
'imageio >= 1.5'],
9697
)

surfer/tests/test_viz.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import numpy as np
21
import os
32
import os.path as op
43
from os.path import join as pjoin
5-
import re
64
import shutil
7-
import subprocess
8-
from nose.tools import assert_equal
9-
from numpy.testing import assert_raises, assert_array_equal
105
from tempfile import mkdtemp, mktemp
6+
7+
import imageio
8+
from nose.tools import assert_equal
9+
from mayavi import mlab
1110
import nibabel as nib
11+
import numpy as np
12+
from numpy.testing import assert_raises, assert_array_equal
1213

1314
from surfer import Brain, io, utils
14-
from surfer.utils import requires_ffmpeg, requires_fsaverage
15-
from mayavi import mlab
15+
from surfer.utils import requires_fsaverage
1616

1717
subj_dir = utils._get_subjects_dir()
1818
subject_id = 'fsaverage'
@@ -214,7 +214,6 @@ def test_morphometry():
214214
brain.close()
215215

216216

217-
@requires_ffmpeg
218217
@requires_fsaverage
219218
def test_movie():
220219
"""Test saving a movie of an MEG inverse solution
@@ -234,18 +233,16 @@ def test_movie():
234233
tempdir = mkdtemp()
235234
try:
236235
dst = os.path.join(tempdir, 'test.mov')
236+
# test the number of frames in the movie
237237
brain.save_movie(dst)
238+
frames = imageio.mimread(dst)
239+
assert_equal(len(frames), 2)
240+
brain.save_movie(dst, time_dilation=10)
241+
frames = imageio.mimread(dst)
242+
assert_equal(len(frames), 7)
238243
brain.save_movie(dst, tmin=0.081, tmax=0.102)
239-
# test the number of frames in the movie
240-
sp = subprocess.Popen(('ffmpeg', '-i', 'test.mov', '-vcodec', 'copy',
241-
'-f', 'null', '/dev/null'), cwd=tempdir,
242-
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
243-
stdout, stderr = sp.communicate()
244-
m = re.search('frame=\s*(\d+)\s', stderr)
245-
if not m:
246-
raise RuntimeError(stderr)
247-
n_frames = int(m.group(1))
248-
assert_equal(n_frames, 3)
244+
frames = imageio.mimread(dst)
245+
assert_equal(len(frames), 2)
249246
finally:
250247
# clean up
251248
shutil.rmtree(tempdir)

surfer/viz.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from math import floor
22
import os
33
from os.path import join as pjoin
4-
from tempfile import mkdtemp
54
from warnings import warn
65

6+
import imageio
77
import numpy as np
88
from scipy import stats, ndimage, misc
99
from scipy.interpolate import interp1d
@@ -21,7 +21,7 @@
2121

2222
from . import utils, io
2323
from .utils import (Surface, verbose, create_color_lut, _get_subjects_dir,
24-
string_types, assert_ffmpeg_is_available, ffmpeg)
24+
string_types)
2525

2626

2727
import logging
@@ -730,6 +730,35 @@ def _get_display_range(self, scalar_data, min, max, sign):
730730

731731
return min, max
732732

733+
def _iter_time(self, time_idx, interpolation):
734+
"""Iterate through time points, then reset to current time
735+
736+
Parameters
737+
----------
738+
time_idx : array_like
739+
Time point indexes through which to iterate.
740+
interpolation : str
741+
Interpolation method (``scipy.interpolate.interp1d`` parameter,
742+
one of 'linear' | 'nearest' | 'zero' | 'slinear' | 'quadratic' |
743+
'cubic'). Interpolation is only used for non-integer indexes.
744+
745+
Yields
746+
------
747+
idx : int | float
748+
Current index.
749+
750+
Notes
751+
-----
752+
Used by movie and image sequence saving functions.
753+
"""
754+
current_time_idx = self.data_time_index
755+
for idx in time_idx:
756+
self.set_data_time_index(idx, interpolation)
757+
yield idx
758+
759+
# Restore original time index
760+
self.set_data_time_index(current_time_idx)
761+
733762
###########################################################################
734763
# ADDING DATA PLOTS
735764
def add_overlay(self, source, min=2, max="robust_max", sign="abs",
@@ -2041,12 +2070,9 @@ def save_image_sequence(self, time_idx, fname_pattern, use_abs_idx=True,
20412070
images_written: list
20422071
all filenames written
20432072
"""
2044-
current_time_idx = self.data_time_index
20452073
images_written = list()
2046-
rel_pos = 0
2047-
for idx in time_idx:
2048-
self.set_data_time_index(idx, interpolation)
2049-
fname = fname_pattern % (idx if use_abs_idx else rel_pos)
2074+
for i, idx in enumerate(self._iter_time(time_idx, interpolation)):
2075+
fname = fname_pattern % (idx if use_abs_idx else i)
20502076
if montage == 'single':
20512077
self.save_single_image(fname, row, col)
20522078
elif montage == 'current':
@@ -2055,10 +2081,6 @@ def save_image_sequence(self, time_idx, fname_pattern, use_abs_idx=True,
20552081
self.save_montage(fname, montage, 'h', border_size, colorbar,
20562082
row, col)
20572083
images_written.append(fname)
2058-
rel_pos += 1
2059-
2060-
# Restore original time index
2061-
self.set_data_time_index(current_time_idx)
20622084

20632085
return images_written
20642086

@@ -2172,15 +2194,7 @@ def save_movie(self, fname, time_dilation=4., tmin=None, tmax=None,
21722194
bitrate : str | float
21732195
Bitrate to use to encode movie. Can be specified as number (e.g.
21742196
64000) or string (e.g. '64k'). Default value is 1M
2175-
2176-
Notes
2177-
-----
2178-
This method requires FFmpeg to be installed in the system PATH. FFmpeg
2179-
is free and can be obtained from `here
2180-
<http://ffmpeg.org/download.html>`_.
21812197
"""
2182-
assert_ffmpeg_is_available()
2183-
21842198
if tmin is None:
21852199
tmin = self._times[0]
21862200
elif tmin < self._times[0]:
@@ -2206,12 +2220,10 @@ def save_movie(self, fname, time_dilation=4., tmin=None, tmax=None,
22062220

22072221
logger.debug("Save movie for time points/samples\n%s\n%s"
22082222
% (times, time_idx))
2209-
tempdir = mkdtemp()
2210-
frame_pattern = 'frame%%0%id.png' % (np.floor(np.log10(n_times)) + 1)
2211-
fname_pattern = os.path.join(tempdir, frame_pattern)
2212-
self.save_image_sequence(time_idx, fname_pattern, False, -1, -1,
2213-
'current', interpolation=interpolation)
2214-
ffmpeg(fname, fname_pattern, framerate, codec=codec, bitrate=bitrate)
2223+
images = (self.screenshot() for _ in
2224+
self._iter_time(time_idx, interpolation))
2225+
imageio.mimwrite(fname, images, fps=framerate, codec=codec,
2226+
bitrate=bitrate)
22152227

22162228
def animate(self, views, n_steps=180., fname=None, use_cache=False,
22172229
row=-1, col=-1):

0 commit comments

Comments
 (0)