23
23
import six
24
24
from six .moves import xrange , zip
25
25
26
+ import numpy as np
26
27
import os
27
28
import platform
28
29
import sys
61
62
62
63
63
64
def adjusted_figsize (w , h , dpi , n ):
65
+ '''Compute figure size so that pixels are a multiple of n
66
+
67
+ Parameters
68
+ ----------
69
+ w, h : float
70
+ Size in inches
71
+
72
+ dpi : float
73
+ The dpi
74
+
75
+ n : int
76
+ The target multiple
77
+
78
+ Returns
79
+ -------
80
+ wnew, hnew : float
81
+ The new figure size in inches.
82
+ '''
83
+
84
+ # this maybe simplified if / when we adopt consistent rounding for
85
+ # pixel size across the whole library
86
+ def correct_roundoff (x , dpi , n ):
87
+ if int (x * dpi ) % n != 0 :
88
+ if int (np .nextafter (x , np .inf )* dpi ) % n == 0 :
89
+ x = np .nextafter (x , np .inf )
90
+ elif int (np .nextafter (x , - np .inf )* dpi ) % n == 0 :
91
+ x = np .nextafter (x , - np .inf )
92
+ return x
93
+
64
94
wnew = int (w * dpi / n ) * n / dpi
65
95
hnew = int (h * dpi / n ) * n / dpi
66
- return wnew , hnew
96
+ return ( correct_roundoff ( wnew , dpi , n ), correct_roundoff ( hnew , dpi , n ))
67
97
68
98
69
99
# A registry for available MovieWriter classes
@@ -214,8 +244,11 @@ def _adjust_frame_size(self):
214
244
verbose .report ('figure size (inches) has been adjusted '
215
245
'from %s x %s to %s x %s' % (wo , ho , w , h ),
216
246
level = 'helpful' )
247
+ else :
248
+ w , h = self .fig .get_size_inches ()
217
249
verbose .report ('frame size in pixels is %s x %s' % self .frame_size ,
218
250
level = 'debug' )
251
+ return w , h
219
252
220
253
def setup (self , fig , outfile , dpi ):
221
254
'''
@@ -235,7 +268,7 @@ def setup(self, fig, outfile, dpi):
235
268
self .outfile = outfile
236
269
self .fig = fig
237
270
self .dpi = dpi
238
- self ._adjust_frame_size ()
271
+ self ._w , self . _h = self . _adjust_frame_size ()
239
272
240
273
# Run here so that grab_frame() can write the data to a pipe. This
241
274
# eliminates the need for temp files.
@@ -285,6 +318,10 @@ def grab_frame(self, **savefig_kwargs):
285
318
verbose .report ('MovieWriter.grab_frame: Grabbing frame.' ,
286
319
level = 'debug' )
287
320
try :
321
+ # re-adjust the figure size in case it has been changed by the
322
+ # user. We must ensure that every frame is the same size or
323
+ # the movie will not save correctly.
324
+ self .fig .set_size_inches (self ._w , self ._h )
288
325
# Tell the figure to save its data to the sink, using the
289
326
# frame format and dpi.
290
327
self .fig .savefig (self ._frame_sink (), format = self .frame_format ,
@@ -334,16 +371,21 @@ def isAvailable(cls):
334
371
if not bin_path :
335
372
return False
336
373
try :
337
- p = subprocess .Popen (bin_path ,
338
- shell = False ,
339
- stdout = subprocess . PIPE ,
340
- stderr = subprocess .PIPE ,
341
- creationflags = subprocess_creation_flags )
342
- p . communicate ( )
343
- return True
374
+ p = subprocess .Popen (
375
+ bin_path ,
376
+ shell = False ,
377
+ stdout = subprocess .PIPE ,
378
+ stderr = subprocess . PIPE ,
379
+ creationflags = subprocess_creation_flags )
380
+ return cls . _handle_subprocess ( p )
344
381
except OSError :
345
382
return False
346
383
384
+ @classmethod
385
+ def _handle_subprocess (cls , process ):
386
+ process .communicate ()
387
+ return True
388
+
347
389
348
390
class FileMovieWriter (MovieWriter ):
349
391
'''`MovieWriter` for writing to individual files and stitching at the end.
@@ -514,10 +556,18 @@ def output_args(self):
514
556
515
557
return args + ['-y' , self .outfile ]
516
558
559
+ @classmethod
560
+ def _handle_subprocess (cls , process ):
561
+ _ , err = process .communicate ()
562
+ # Ubuntu 12.04 ships a broken ffmpeg binary which we shouldn't use
563
+ if 'Libav' in err .decode ():
564
+ return False
565
+ return True
566
+
517
567
518
568
# Combine FFMpeg options with pipe-based writing
519
569
@writers .register ('ffmpeg' )
520
- class FFMpegWriter (MovieWriter , FFMpegBase ):
570
+ class FFMpegWriter (FFMpegBase , MovieWriter ):
521
571
'''Pipe-based ffmpeg writer.
522
572
523
573
Frames are streamed directly to ffmpeg via a pipe and written in a single
@@ -538,7 +588,7 @@ def _args(self):
538
588
539
589
# Combine FFMpeg options with temp file-based writing
540
590
@writers .register ('ffmpeg_file' )
541
- class FFMpegFileWriter (FileMovieWriter , FFMpegBase ):
591
+ class FFMpegFileWriter (FFMpegBase , FileMovieWriter ):
542
592
'''File-based ffmpeg writer.
543
593
544
594
Frames are written to temporary files on disk and then stitched
0 commit comments