Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Transparency in Animations #5335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
cbreeden opened this issue Oct 27, 2015 · 5 comments
Closed

Transparency in Animations #5335

cbreeden opened this issue Oct 27, 2015 · 5 comments

Comments

@cbreeden
Copy link

This report is somewhat of an extension to #5302. It seems easy to miss handle transparent regions while working with nbagg. The following is an example taken from the animation examples:

%matplotlib nbagg

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

def update_line(num, data, line):
    line.set_data(data[...,:num])
    return line,

fig = plt.figure()

plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel('x')
plt.title('test')

data = np.random.rand(2, 25)
l, = plt.plot([], [], 'r-')

ani = animation.FuncAnimation(fig, update_line, 25, fargs=(data, l),
    interval=50, blit=True)

ani.save('im.gif', writer='imagemagick', fps=8)
ani.save('im.mp4', extra_args=['-vcodec', 'libx264'])

The output produced is:

im

As you can see, there is again some issues with the rendering of text in the figure (not axes) area where nbagg has set transparency to true. The im.mp4 file looks nearly identical to the gif.

I believe matplotlib saves each frame as a png and then sends those pngs to be saved as an animation through convert (imagemagick) or ffmpeg. I believe the issue here is that while gifs properly support transparency, they only support either full alpha or no alpha, which forces imagemagick to decide between alpha or no alpha for each intermediate alpha which gives a pixelated appearance.

If we insert the code [for some reason with plt.rc_context({'nbagg.transparent': False}) doesn't work here]:

import matplotlib
matplotlib.rcParams['nbagg.transparent'] = False

Then we will get this:

im2

Perhaps we can just disable transparency for gif files all together (I don't think matplotlib can properly handle boolean alpha levels), and then set the default frame format to gif files for animations; animation.frame_format: 'gif'

@dopplershift dopplershift changed the title Tansparency in Animations Transparency in Animations Oct 28, 2015
@cbreeden
Copy link
Author

This is just a post to document an attempt and the difficulties I ran into. Good for me to reference when I try again, or perhaps useful if anyone else takes a peek.

I tried to give (while crossing my fingers) _png.write_png a 32-Bit RGBA bitmap from a numpy array as an experiment:

%matplotlib nbagg

import pylab
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib import _png
import numpy as np

pylab.plot([1,2,3])

canvas = pylab.get_current_fig_manager().canvas
agg = canvas.switch_backends(FigureCanvasAgg)
agg.draw()

s = agg.buffer_rgba()
# get the width and the height to resize the matrix
l,b,w,h = agg.figure.bbox.bounds
w, h = int(w), int(h)

s = np.array(s.tolist()).reshape((w,h,4)) #Convert to RGBA to np.array

filename_or_obj = 'testwrite.png'
renderer = agg.renderer

with open(filename_or_obj, 'wb') as fn:
    _png.write_png(memoryview(s), fn, agg.figure.dpi)

Get error: IndexError: Unexpected SeqBase<T> length.

I think it has to do with write_png wants s to have a array_view::converter_contiguous method:

&buffer.converter_contiguous,

Haven't figured a way around that yet. The goal is to then use numpy to flatten RGBA -> RGB to see if something like this would work for making the animations better by default.

EDIT: This error has been fixed. I was referencing github src code for write_png (which wants 2 or 3 arguments) where matplotlib 1.4.3 wanted 5 arguments for width and height. I guess this has changed since. Note, whoever changed the error message to Unexpected SeqBase<T> to write_png wnats at most 3 arguments... kodus 👍

@jenshnielsen
Copy link
Member

Could you try setting blit=False It looks to me like the issue is that the frames are not blitted correctly and all frames but the first ones are have the text plotted multiple times.

@cbreeden
Copy link
Author

I got the same results with blit=False. I also tried using writer=imagemagick_file to see what frames we are giving imagemagick and here are a few samples:

_tmp0000001

_tmp0000006

I used convert's +adjoin argument to deconstruct those frames from the gif and this is what they look like:

frame_001

frame_007

It looks like imagemagick really wants to keep the alpha channel (as shown in the first gif), but is then forced to decide between full alpha or no alpha which is what gives the pixelated appearance, and then for the later frames I guess it gives up on the whole alpha channel idea for some reason unknown to me.

@cbreeden
Copy link
Author

Issue update:

I have written some quick code as a possible solution, but I ran into issues with animations in my git-installed version of matplotlib. I will need to figure this out, but here is something I was considering:

    if kwargs.pop('flatten', False):
        bg = mcolors.colorConverter.to_rgb(
                 rcParams.get('savefig.facecolor', 'white'))
        bg = tuple([int(x * 255.0) for x in bg])
        w, h = int(renderer.width), int(renderer.height)

        src_a = renderer.buffer_rgba()
        src_a = np.array(memoryview(src_a))
        src_a = src_a.reshape((w,h,4))

        alpha = src_a[:,:,3]
        src = src_a[:,:,0:3]

        dest = np.empty((w,h,3))  
        dest[:,:,0] = (255 - alpha)*bg[0] + alpha*src[:,:,0]    #Red
        dest[:,:,1] = (255 - alpha)*bg[1] + alpha*src[:,:,1]    #Green
        dest[:,:,2] = (255 - alpha)*bg[2] + alpha*src[:,:,2]    #Blue
        dest = (dest/255).astype(np.uint8)                      #24-bit RGB, flattened

        _png.write_png(dest, filename_or_obj, self.figure.dpi)
    else:
         _png.write_png(renderer._renderer, filename_or_obj, self.figure.dpi)

This would add an flatten kwarg in savefig that can be called when animation.py when it wants it.

@jklymak
Copy link
Member

jklymak commented Aug 16, 2018

Closing, as I don't see the issue with current nbagg and master matplotlib. OTOH, feel free to reopen if I'm in error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants