可滚动的 Matplotlib 图无法通过函数工作

Scrollable Matplotlib figure not working from function

我目前正在研究医学图像并编写用于准备训练集的代码。 为此,我需要滚动卷数据切片。

我的主要 IDE 是 Spyder,但 IndexTracker 对象的标准实现在函数内对我不起作用。

这个标准实现对我有用: https://matplotlib.org/gallery/animation/image_slices_viewer.html

但是一旦我将绘图的创建放入函数中,创建的绘图就不再可滚动:

import numpy as np
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt

class IndexTracker(object):
    def __init__(self, ax, X):
        self.ax = ax
        ax.set_title('use scroll wheel to navigate images')

        self.X = X
        rows, cols, self.slices = X.shape
        self.ind = self.slices//2

        self.im = ax.imshow(self.X[:, :, self.ind])
        self.update()

    def onscroll(self, event):
        print("%s %s" % (event.button, event.step))
        if event.button == 'up':
            self.ind = (self.ind + 1) % self.slices
        else:
            self.ind = (self.ind - 1) % self.slices
        self.update()

    def update(self):
        self.im.set_data(self.X[:, :, self.ind])
        self.ax.set_ylabel('slice %s' % self.ind)
        self.im.axes.figure.canvas.draw()

def plot(X):
    fig, ax = plt.subplots(1, 1)
    tracker = IndexTracker(ax, X)
    fig.canvas.mpl_connect('scroll_event', tracker.onscroll)
    plt.show()

plot(np.random.rand(200, 200, 500))

可能是什么问题?如何在函数内创建可滚动绘图?

我自己解决了这个问题: 问题是,matplotlib 没有正确识别绘图实例,因此必须在函数中设置它。 一个基本的但不是通用或实用的应用程序如下:

figList,axList=[],[]
def plotSlices(image, view):
    fig, ax = plt.subplots(1, 1)
    figList.append(fig)
    axList.append(ax)
    axList[-1].set_title(view)
    tracker = IndexTracker(axList[-1], image)
    figList[-1].canvas.mpl_connect('scroll_event', tracker.onscroll)
    plt.show(figList[-1],block=False)

其中读取了一个numpy数组,连同图片的透视图。这仅用于标题,因此您可以删除 it/hardcode 标题。 fig 和 ax objects 被附加到各自的列表中,只有最后添加的元素被提供给 matplotlib 进行绘图。

这样,滚动浏览函数中定义的绘图就可以了。 一个问题是我显然重载了 .show() 方法,它应该只接受 block=False/True 参数,但实际上确实正确地识别了正确的情节,如下所示: plt.show(图,块=假) 它会抛出一个错误来解决这个问题,但最终会起作用。完美!

简答

以更简洁的方式,您应该在主上下文中保持对跟踪器实例的访问。否则,此跟踪器将在函数结束时销毁。

按照官方示例https://matplotlib.org/stable/gallery/event_handling/image_slices_viewer.html?highlight=slice%20viewer,我已经将其改编为将情节包装在一个函数中。

def ShowSlice():
    fig, ax = plt.subplots(1, 1)
    X = np.random.rand(20, 20, 40)
    tracker = IndexTracker(ax, X)
    fig.canvas.mpl_connect('scroll_event', tracker.on_scroll)
    plt.show()
    return tracker
    
# ShowSlice()  # Does not works    
tr = ShowSlice()  # Works

我的调查

感谢您的提问,这正是我目前的问题。您自己的回答对我找到这种行为的真正原因有很大帮助。

您提供的解决方案强调了一个问题,即了解至少与图形或轴相关联的某些变量。

我已经遇到过类似的问题,matplotlib 中的动画。这个问题的解决方案是在主上下文中保留一个动画实例,如下例所示: https://matplotlib.org/2.1.2/gallery/animation/basic_example.html,即使不使用动画对象(line_aniim_ani),也需要在内存中保留then。

这里的问题似乎与跟踪器实例完全相同。

深入阅读 mpl_connect 的文档可以确认: https://matplotlib.org/stable/users/event_handling.html#event-connections

The FigureCanvasBase.mpl_connect method returns a connection id (an integer), which can be used to disconnect the callback via

Note

The canvas retains only weak references to instance methods used as callbacks. Therefore, you need to retain a reference to instances owning such methods. Otherwise the instance will be garbage-collected and the callback will vanish. This does not affect free functions used as callbacks.

感谢您的阅读。