I am trying to implement an option in my GUI to save an image sequence displayed using matplotlib. The code looks something like this:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import \
FigureCanvasQTAgg as FigureCanvas
from matplotlib.animation import FuncAnimation
from PIL import Image
plt.rcParams['savefig.bbox'] = 'tight'
class Printer:
def __init__(self, data):
self.fig, self.ax = plt.subplots()
self.canvas = FigureCanvas(self.fig)
# some irrelevant color adjustment here
#self.ax.spines['bottom'].set_color('#f9f2d7')
#self.ax.spines['top'].set_color('#f9f2d7')
#self.ax.spines['right'].set_color('#f9f2d7')
#self.ax.spines['left'].set_color('#f9f2d7')
#self.ax.tick_params(axis='both', colors='#f9f2d7')
#self.ax.yaxis.label.set_color('#f9f2d7')
#self.ax.xaxis.label.set_color('#f9f2d7')
#self.fig.subplots_adjust(left=0.1, right=0.975, bottom=0.09, top=0.98)
self.fig.patch.set_alpha(0)
self.fig.patch.set_visible(False)
self.canvas.setStyleSheet("background-color:transparent;")
self.fig.set_size_inches(10, 10, True)
self.fig.tight_layout()
self.data = data
self.image_artist = self.ax.imshow(data[0])
def animate(self, i):
self.image_artist.set_data(self.data[i])
self.canvas.draw()
def save_animation():
data = [
Image.open("test000.png"),
Image.open("test001.png"),
]
file = 'test.gif'
printer = Printer(data)
ani = FuncAnimation(
printer.fig, printer.animate, interval=100, frames=len(data),
)
# writer = animation.writers['pillow'](bitrate=1000)
ani.save(
file, writer='pillow', savefig_kwargs={'transparent': True, 'bbox_inches': 'tight'}
)
save_animation()
Transparency:
As you can see I have already tried several different approaches as suggested elsewhere (1, 2), but didn't manage to find a solution. All of the settings and arguments patch.set_alpha(0)
, patch.set_visible(False)
, canvas.setStyleSheet("background-color:transparent;")
, savefig_kwargs={'transparent': True}
seem to have no effect at all on the transparency. I found this post but I didn't get the code to work (for one I had to comment out this %matplotlib inline
, but then I ended up getting some error during the MovieWriter.cleanup out = TextIOWrapper(BytesIO(out)).read() TypeError: a bytes-like object is required, not 'str'
). Here, it was suggested that this is actually a bug, but the proposed workaroud doesn't work for me since I would have to rely on third-party software. There also exists this bug report which was supposedly solved, so maybe it is unrelated.
Tight layout
I actually couldn't really find much on this, but all the things I tried (plt.rcParams['savefig.bbox'] = 'tight'
, fig.tight_layout()
, savefig_kwargs={'bbox_inches': 'tight'}
) don't have any effect or are even actively discarded in the case of the bbox_inches argument
. How does this work?
High quality
Since I cannot use ImageMagick
and can't get ffmpeg
to work (more on this below), I rely on pillow to save my animation. But the only argument in terms of quality that I can pass on seems to be the bitrate
, which doesn't have any effect. The files still have the same size and the animation still looks like mush. The only way that I found to increase the resolution was to use fig.set_size_inches(10, 10, True)
, but this still doesn't improve the overall quality of the animation. It still looks bad. I saw that you can pass on codec
and extra_args
so maybe that is something that might help, but I have no idea how to use these because I couldn't find a list with allowed arguments.
ffmpeg
I can't get ffmpeg to work. I installed the python package from here and can import it into a python session but I don't know how I can get matplotlib to use that. I also got ffmpeg from here (Windows 64-bit version) and set the plt.rcParams['animation.ffmpeg_path']
to where I saved the files (there was no intaller to run, not sure if I did it correctly). But this didn't help either. Also this is of course also third-party software, so if somebody else were to use my code/program it wouldn't work.