diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index fa03f90d7ad8..03bc155c63a6 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -23,6 +23,7 @@ import six from six.moves import xrange, zip +import numpy as np import os import platform import sys @@ -62,9 +63,38 @@ def adjusted_figsize(w, h, dpi, n): + '''Compute figure size so that pixels are a multiple of n + + Parameters + ---------- + w, h : float + Size in inches + + dpi : float + The dpi + + n : int + The target multiple + + Returns + ------- + wnew, hnew : float + The new figure size in inches. + ''' + + # this maybe simplified if / when we adopt consistent rounding for + # pixel size across the whole library + def correct_roundoff(x, dpi, n): + if int(x*dpi) % n != 0: + if int(np.nextafter(x, np.inf)*dpi) % n == 0: + x = np.nextafter(x, np.inf) + elif int(np.nextafter(x, -np.inf)*dpi) % n == 0: + x = np.nextafter(x, -np.inf) + return x + wnew = int(w * dpi / n) * n / dpi hnew = int(h * dpi / n) * n / dpi - return wnew, hnew + return (correct_roundoff(wnew, dpi, n), correct_roundoff(hnew, dpi, n)) # A registry for available MovieWriter classes @@ -278,8 +308,11 @@ def _adjust_frame_size(self): verbose.report('figure size (inches) has been adjusted ' 'from %s x %s to %s x %s' % (wo, ho, w, h), level='helpful') + else: + w, h = self.fig.get_size_inches() verbose.report('frame size in pixels is %s x %s' % self.frame_size, level='debug') + return w, h def setup(self, fig, outfile, dpi=None): ''' @@ -301,7 +334,7 @@ def setup(self, fig, outfile, dpi=None): if dpi is None: dpi = self.fig.dpi self.dpi = dpi - self._adjust_frame_size() + self._w, self._h = self._adjust_frame_size() # Run here so that grab_frame() can write the data to a pipe. This # eliminates the need for temp files. @@ -337,6 +370,10 @@ def grab_frame(self, **savefig_kwargs): verbose.report('MovieWriter.grab_frame: Grabbing frame.', level='debug') try: + # re-adjust the figure size in case it has been changed by the + # user. We must ensure that every frame is the same size or + # the movie will not save correctly. + self.fig.set_size_inches(self._w, self._h) # Tell the figure to save its data to the sink, using the # frame format and dpi. self.fig.savefig(self._frame_sink(), format=self.frame_format, @@ -386,16 +423,21 @@ def isAvailable(cls): if not bin_path: return False try: - p = subprocess.Popen(bin_path, - shell=False, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - creationflags=subprocess_creation_flags) - p.communicate() - return True + p = subprocess.Popen( + bin_path, + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + creationflags=subprocess_creation_flags) + return cls._handle_subprocess(p) except OSError: return False + @classmethod + def _handle_subprocess(cls, process): + process.communicate() + return True + class FileMovieWriter(MovieWriter): '''`MovieWriter` for writing to individual files and stitching at the end. @@ -570,10 +612,18 @@ def output_args(self): return args + ['-y', self.outfile] + @classmethod + def _handle_subprocess(cls, process): + _, err = process.communicate() + # Ubuntu 12.04 ships a broken ffmpeg binary which we shouldn't use + if 'Libav' in err.decode(): + return False + return True + # Combine FFMpeg options with pipe-based writing @writers.register('ffmpeg') -class FFMpegWriter(MovieWriter, FFMpegBase): +class FFMpegWriter(FFMpegBase, MovieWriter): '''Pipe-based ffmpeg writer. Frames are streamed directly to ffmpeg via a pipe and written in a single @@ -594,7 +644,7 @@ def _args(self): # Combine FFMpeg options with temp file-based writing @writers.register('ffmpeg_file') -class FFMpegFileWriter(FileMovieWriter, FFMpegBase): +class FFMpegFileWriter(FFMpegBase, FileMovieWriter): '''File-based ffmpeg writer. Frames are written to temporary files on disk and then stitched diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py index 5f1d8a5ebf8c..017727016fe0 100644 --- a/lib/matplotlib/tests/test_animation.py +++ b/lib/matplotlib/tests/test_animation.py @@ -145,6 +145,14 @@ def test_save_animation_smoketest(tmpdir, writer, extension): ax.set_xlim(0, 10) ax.set_ylim(-1, 1) + dpi = None + codec = None + if writer == 'ffmpeg': + # Issue #8253 + fig.set_size_inches((10.85, 9.21)) + dpi = 100. + codec = 'h264' + def init(): line.set_data([], []) return line, @@ -160,7 +168,8 @@ def animate(i): with tmpdir.as_cwd(): anim = animation.FuncAnimation(fig, animate, init_func=init, frames=5) try: - anim.save('movie.' + extension, fps=30, writer=writer, bitrate=500) + anim.save('movie.' + extension, fps=30, writer=writer, bitrate=500, + dpi=dpi, codec=codec) except UnicodeDecodeError: pytest.xfail("There can be errors in the numpy import stack, " "see issues #1891 and #2679") pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy