1
1
import os
2
2
from os .path import join as pjoin
3
+ from tempfile import mkdtemp
3
4
from warnings import warn
4
5
5
6
import numpy as np
16
17
from . import utils , io
17
18
from .config import config
18
19
from .utils import (Surface , verbose , create_color_lut , _get_subjects_dir ,
19
- string_types )
20
+ string_types , has_ffmpeg , ffmpeg )
20
21
21
22
22
23
import logging
@@ -1623,13 +1624,20 @@ def set_data_smoothing_steps(self, smoothing_steps, verbose=None):
1623
1624
data ["smoothing_steps" ] = smoothing_steps
1624
1625
self ._toggle_render (True , views )
1625
1626
1626
- def set_time (self , time ):
1627
- """Set the data time index to the time point closest to time
1627
+ def index_for_time (self , time , rounding = 'closest' ):
1628
+ """Find the data time index closest to a specific time point
1628
1629
1629
1630
Parameters
1630
1631
----------
1631
1632
time : scalar
1632
1633
Time.
1634
+ rounding : 'closest' | 'up' | 'down
1635
+ How to round if the exact time point is not an index.
1636
+
1637
+ Returns
1638
+ -------
1639
+ index : int
1640
+ Data time index closest to time.
1633
1641
"""
1634
1642
if self .n_times is None :
1635
1643
raise RuntimeError ("Brain has no time axis" )
@@ -1644,7 +1652,27 @@ def set_time(self, time):
1644
1652
"[%s, %s]" % (time , tmin , tmax ))
1645
1653
raise ValueError (err )
1646
1654
1647
- idx = np .argmin (np .abs (times - time ))
1655
+ if rounding == 'closest' :
1656
+ idx = np .argmin (np .abs (times - time ))
1657
+ elif rounding == 'up' :
1658
+ idx = np .nonzero (times >= time )[0 ][0 ]
1659
+ elif rounding == 'down' :
1660
+ idx = np .nonzero (times <= time )[0 ][- 1 ]
1661
+ else :
1662
+ err = "Invalid rounding parameter: %s" % repr (rounding )
1663
+ raise ValueError (err )
1664
+
1665
+ return idx
1666
+
1667
+ def set_time (self , time ):
1668
+ """Set the data time index to the time point closest to time
1669
+
1670
+ Parameters
1671
+ ----------
1672
+ time : scalar
1673
+ Time.
1674
+ """
1675
+ idx = self .index_for_time (time )
1648
1676
self .set_data_time_index (idx )
1649
1677
1650
1678
def _get_colorbars (self , row , col ):
@@ -1845,7 +1873,7 @@ def screenshot_single(self, mode='rgb', antialiased=False, row=-1, col=-1):
1845
1873
brain = self .brain_matrix [row , col ]
1846
1874
return mlab .screenshot (brain ._f , mode , antialiased )
1847
1875
1848
- def save_imageset (self , prefix , views , filetype = 'png' , colorbar = 'auto' ,
1876
+ def save_imageset (self , prefix , views , filetype = 'png' , colorbar = 'auto' ,
1849
1877
row = - 1 , col = - 1 ):
1850
1878
"""Convenience wrapper for save_image
1851
1879
@@ -2034,6 +2062,96 @@ def save_montage(self, filename, order=['lat', 'ven', 'med'],
2034
2062
cb .visible = colorbars_visibility [cb ]
2035
2063
return out
2036
2064
2065
+ def save_movie (self , dst , tstart = None , tstop = None , step = None ,
2066
+ time_idx = None , montage = 'current' , orientation = 'h' ,
2067
+ border_size = 15 , colorbar = 'auto' , framerate = 10 ,
2068
+ codec = 'mpeg4' , row = - 1 , col = - 1 , movie_tool = 'ffmpeg' ):
2069
+ """Save a movie (for data with a time axis)
2070
+
2071
+ Parameters
2072
+ ----------
2073
+ dst : str
2074
+ Path at which to sae the movie.
2075
+ tstart : None | float
2076
+ First time point to include (default: all data).
2077
+ tstop : None | float
2078
+ Time point at which to stop the movie (exclusive; default: all
2079
+ data).
2080
+ step : None | int
2081
+ Number of data frames to step forward between movie frames
2082
+ (default 1).
2083
+ time_idx : None | array
2084
+ Index that selects time points form the time axis from which to
2085
+ make the movie. If time_idx is specified, neither of tstart, tstop
2086
+ or tstep should be specified.
2087
+ montage: 'current' | 'single' | list
2088
+ Views to include in the images: 'current' (default) uses the
2089
+ currently displayed image; 'single' uses a single view, specified
2090
+ by the ``row`` and ``col`` parameters; a list can be used to
2091
+ specify a complete montage (see :meth:`save_montage`).
2092
+ orientation: {'h' | 'v'}
2093
+ montage image orientation (horizontal of vertical alignment; only
2094
+ applies if ``montage`` is a flat list)
2095
+ border_size: int
2096
+ Size of image border (more or less space between images)
2097
+ colorbar: None | 'auto' | [int], optional
2098
+ if None no colorbar is visible. If 'auto' is given the colorbar
2099
+ is only shown in the middle view. Otherwise on the listed
2100
+ views when a list of int is passed.
2101
+ framerate : int
2102
+ Framerate of the movie (frames per second).
2103
+ codec : str
2104
+ Codec to use (default 'mpeg4').
2105
+ row : int
2106
+ row index of the brain to use
2107
+ col : int
2108
+ column index of the brain to use
2109
+ movie_tool : 'ffmpeg'
2110
+ Tool to use to convert image sequence into a movie (default:
2111
+ 'ffmpeg').
2112
+ """
2113
+ if movie_tool .lower () == 'ffmpeg' :
2114
+ if not has_ffmpeg ():
2115
+ err = ("FFmpeg is not in the path and is needed for saving "
2116
+ "movies. Install FFmpeg and try again. It can be "
2117
+ "downlaoded from http://ffmpeg.org/download.html." )
2118
+ raise RuntimeError (err )
2119
+ else :
2120
+ err = "Currently the only possible movie tool is FFmpeg"
2121
+ raise ValueError (err )
2122
+
2123
+ if tstart is not None :
2124
+ start = self .index_for_time (tstart , rounding = 'up' )
2125
+ else :
2126
+ start = 0
2127
+
2128
+ if tstop is not None :
2129
+ stop = self .index_for_time (tstop , rounding = 'up' )
2130
+ else :
2131
+ stop = self .n_times
2132
+
2133
+ if all (x is None for x in (tstart , tstop , step )):
2134
+ if time_idx is None :
2135
+ time_idx = np .arange (self .n_times )
2136
+ elif time_idx is None :
2137
+ time_idx = np .arange (start , stop , step )
2138
+ else :
2139
+ err = ("Both slice parameters (tstart, tstop, step) and "
2140
+ "time_idx can not be specified at the same time." )
2141
+ raise TypeError (err )
2142
+
2143
+ n_times = len (time_idx )
2144
+ if n_times == 0 :
2145
+ raise ValueError ("No time points selected" )
2146
+
2147
+ tempdir = mkdtemp ()
2148
+ frame_pattern = 'frame%%0%id.png' % (np .floor (np .log10 (n_times )) + 1 )
2149
+ fname_pattern = os .path .join (tempdir , frame_pattern )
2150
+ self .save_image_sequence (time_idx , fname_pattern , False , row ,
2151
+ col , montage , orientation , border_size ,
2152
+ colorbar )
2153
+ ffmpeg (dst , fname_pattern , framerate , codec )
2154
+
2037
2155
def animate (self , views , n_steps = 180. , fname = None , use_cache = False ,
2038
2156
row = - 1 , col = - 1 ):
2039
2157
"""Animate a rotation.
0 commit comments