Search code examples
pythonmatplotlib

How to clear contours and retain pan and zoom with Matplotlib?


This snippet:

plt.get_current_fig_manager().full_screen_toggle()

for img in imgs:
  plt.clf()
  plt.imshow(img, origin='upper', interpolation='None', aspect='equal', cmap='gray')
  plt.gca().contour(img, np.arange(0, 255, 8), colors='r')
  
  while not plt.waitforbuttonpress():
    pass

plots consecutive images from imgs list, each with their own contours, but it doesn't preserve pan and zoom. When I comment out plt.clf(), pan and zoom are preserved, but all previous contours will be displayed, instead of just the current one. How to preserve pan and zoom and have only contours of the current image displayed?


Solution

  • The QuadContourSet created by plt.contour will be stored in the Axes.collections ArtistList.

    The AxesImage created by plt.imshow will be stored in the Axes.images ArtistList.

    The are multiple ways to remove them. You could retain a handle to them, by giving them a name as you create them, then call the .remove method on that handle; e.g.

    CS = plt.contour(...)
    ...
    CS.remove()
    

    and

    IM = plt.imshow(...)
    ...
    IM.remove()
    

    If you did not, or could not, store a those handles for some reason, you can access them from the Axes instance they are plotted on. Let's assume it's called ax:

    ax.contour(...)
    ax.imshow(...)
    ...
    ax.collections[-1].remove()
    ax.images[-1].remove()
    

    Note, I'm using the index -1 here to remove the last added collection and image to those lists. If you know there is only one collection and image, you could use 0 instead.

    If you don't have a reference to the Axes, you can do this with plt.gca():

    plt.contour(...)
    plt.imshow(...)
    ...
    plt.gca().collections[-1].remove()
    plt.gca().images[-1].remove()
    

    You'll notice this can quickly become difficult to keep track of which was the last added collection to an Axes, especially if you start increasing the complexities of your plots. Which is why the recommended way to do this is the first method of storing the name of the thing you want to access later, so you don't need to remember when it was added to an Axes.


    Putting this all together, here's how I would rewrite your code:

    plt.get_current_fig_manager().full_screen_toggle()
    ax = plt.gca()  # ideally you would have stored ax when you created it.
                    # if not you can use gca() here
    
    IM = CS = None
    
    for img in imgs:
        
        if IM is not None:
            IM.remove()
        if CS is not None:
            CS.remove()
    
        IM = ax.imshow(img, origin='upper', interpolation='None', 
                       aspect='equal', cmap='gray')
        CS = ax.contour(img, np.arange(0, 255, 8), colors='r')
      
        while not plt.waitforbuttonpress():
            pass