Giter Club home page Giter Club logo

Comments (7)

agude avatar agude commented on August 17, 2024 1

Hi @stas-sl!

I'm using "blitting the technique" not "blitting the functionality built into FuncAnimation". 😄

The easiest way to animate a plot is to do as I mentioned in the post and just redraw everything, like I did here: https://github.com/agude/agude.github.io/blob/master/files/names/Most%20Popular%20Names%20Blit%20Same%20Time.ipynb (Cell 14). This takes a long time because you have the overhead of redrawing everything every frame.

What I do in the Blitting example is manually update just the artists that need to change. That's the 20x improvement. I believe I took this technique from the Matplotlib tutorial here: https://matplotlib.org/stable/tutorials/advanced/blitting.html Although their method goes an extra step and implements it's own handler. I think they used to have a tutorial that was not very clear and used the method I do in my post. In essence I was trying to make a clear version of their tutorial.

All that said, it looks like you can now let FuncAnimation handle more of the blitting process for you by setting blitting=True, as you say. But you need to modify what your functions do if you use that. You'll notice my update function updates the artists it needs to and returns None where as if you want FuncAnimation to handle blitting you have to return the changed artists.

from agude.github.io.

stas-sl avatar stas-sl commented on August 17, 2024

Thanks for quick response! But I'm still quite confused 🤔

I checked your another notebook that you mentioned, but TBH I don't see much difference. In both notebooks on each iteration you are updating data (set_data(...)) for existing artists. You are not creating the whole plot/artists from scratch. The only difference is that in "Names" notebook you have many lines/artists, whereas in "Supernova" there is only a single line.

I also don't see in your "Supernova" example where is exactly the "blit technique" used? I do see it in Matplotlib tutorial you referenced. They explicitly save background before starting animation: bg = fig.canvas.copy_from_bbox(fig.bbox) and restore it on each animation iteration: fig.canvas.restore_region(bg) and they do not use FuncAnimation or any other Animation subclass. Probably, I also should go that way, but I just was hoping that FuncAnimation could do all bit bliting for me, instead of manually implementing it. But I've tried and seems like setting blit=True didn't have any effect for me, at least when saving to file. I spent some time debugging/digging through their source code, and it seems that the methods actually responsible for bliting just not being called. I believe, it depend on matplotlib backend, and as I said probably it could work in interactive backend, but not when saving to file, that looks strange to me, but that's what I understood.

Moreover, they do not save animation anywhere, so you need to implement it as well to produce a video, probably by copying parts of their code. But if feels already more complicated than it should be.

I'm not sure when your post was written, but looks like blit parameter was there already 12 years ago 🙂:
image

from agude.github.io.

stas-sl avatar stas-sl commented on August 17, 2024

Basically here is comment at the top of animation.py from 12 years ago (last line):

image

It confirms that bliting is not used for movies, but probably could be.

from agude.github.io.

stas-sl avatar stas-sl commented on August 17, 2024

BTW, the blog looks really cool! I mean not just content, but overall look - typography (larger font), spacing, alignment, illustrations - it feels just "right" 👍

from agude.github.io.

agude avatar agude commented on August 17, 2024

I checked your another notebook that you mentioned, but TBH I don't see much difference.

You're right, because I actually use blitting there too. I never checked in the slow version (but thought I had). Oops!

I also don't see in your "Supernova" example where is exactly the "blit technique" used?

Here it is:

# Apply the three plotting functions written above
init = partial(init_fig, fig=fig, ax=ax, artists=artists)
step = partial(step_through_frames, -15, 25)
update = partial(update_artists, artists=artists, lambdas=df["lambda"].unique())

# Generate the animation
anim = animation.FuncAnimation(
    fig=fig,
    func=update,
    frames=step,
    init_func=init,
    save_count=150, #len(list(step())),
    repeat_delay=5000,
)

My init() function is passed in with init_func=init, which is (from the docs):

A function used to draw a clear frame. If not given, the results of drawing from the first item in the frames sequence will be used. This function will be called once before the first frame.

So that draws the unchanging portion.

Then the update() function is called each frame and updates the artists that change. FuncAnimation then saves that frame and moves on.

But if you look closely I have to use some global variables to track the artists:

# Set the artists
artists = Artists(
    plt.plot([], [], animated=True, label="Flux")[0],
    ax.text(0.987, 0.955, "", fontsize=20, transform=ax.transAxes, horizontalalignment='right', verticalalignment='center'),
)

This tuple of the two artists (the line and the text) is passed in to the functions via partial which means that it's passed in each call with its current values. It's a really bad way to hack around the fact that I need to track state.

So that's the blitting.

But turns out that FuncAnimation has a blit= option where instead of doing the weird, global values with partial application, will just return the artists from the function and use those. I didn't know about it when trying to figure this out 5 years ago so I wrote some worse code. I really should update the post. 😖

But I've tried and seems like setting blit=True didn't have any effect for me, at least when saving to file.

I've never tried using that unfortunately. Maybe I'll get to it next month and try to rewrite this post with better code. But if you're correct maybe it never worked for the movie backend... 🤔 So my version (horrible as it is) seems to work for movies.

But just to check, you're changing your init_func and func to return artists right? My version doesn't because I'm not setting blit=True. If you don't make that change, I suspect nothing happens...

BTW, the blog looks really cool! I mean not just content, but overall look - typography (larger font), spacing, alignment, illustrations - it feels just "right" 👍

Thanks! I'm no designer, but I've tried to make it look nice.

from agude.github.io.

stas-sl avatar stas-sl commented on August 17, 2024

Ok, seems like I understood where is the source of my confusion. Probably we have a bit different understanding of what is “blitting”.

Just for clarity, in your non-optimized version which was 20x slower, you were recreating the whole figure/artists including legends, bands and calling plt.plot() for each frame, not just setting new data on existing artists? I understand that this is an optimization, definitely, but I probably wouldn’t call it “blitting”.

As I understand, what happens under the hood, when matplotlib renders each frame, it can’t “undraw” only changed artists, as they could overlap with others, so it has to clear whole canvas and redraw all artists one by one - those that changed as well as those that are considered “background”, that didn’t change. Yes, in your optimized case no additional objects/artists created, but still everything is redrawn on each frame.

So what could be optimized further is what I would call “blitting”. Instead of redrawing all artists when rendering each frame, some of them that are static, and don’t change from frame to frame could be drawn just once and saved into a buffer as rasterized bitmap, and then on each frame instead of clearing whole canvas and redrawing all of them, this bitmap just quickly gets copied from memory buffer, and then only changed artists being redrawn on top. This is what happens in the matplotlib example you linked and also if you set blit=True (but sadly not for movies, as I found out).

The reason why I’m particularly interested in this optimization is because in my case I have pretty heavy background layer that renders a map (which is essentially plt.imshow(), just quite high resolution) - and it takes about 300 ms on each frame to redraw it, although I’m adding it to the figure only once in init and update data only for some artists on top of it on each iteration.

I’m actually not sure if the latter optimization would give much gain in your case, as you don’t have heavy background layers, that’s probably why you are not noticing if it is turned on or off.

from agude.github.io.

agude avatar agude commented on August 17, 2024

The reason why I’m particularly interested in this optimization is because in my case I have pretty heavy background layer that renders a map (which is essentially plt.imshow(), just quite high resolution) - and it takes about 300 ms on each frame to redraw it, although I’m adding it to the figure only once in init and update data only for some artists on top of it on each iteration.

Sounds like it isn't caching that for whatever reason then. Unfortunate.

I think we're past the stage where I can help. You'll need to ask for help from either the mapping people, or the Matplotlib people. Best off luck!

from agude.github.io.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.