matplotlib wxPython后台在处理大型图像堆栈后崩溃。

问题描述 投票:0回答:1

当处理一个大型图像堆栈时,我被一个wxAssertionError卡住了(如下图所示)。让我用一个例子来解释。

我用wxPython做了一个界面,只有一个面板、一个按钮和一个仪表条。

一旦用户点击按钮,代码就会读取一个8位二进制图像的堆栈(1500 x 100 x 50),在面板上显示第一个图像并开始处理堆栈。可以下载一个堆栈的例子 此处,点击'open->下载',并把它和下面的代码放在同一个文件夹里,如果你无法访问堆栈,请在评论中告诉我)。) 处理步骤是一个循环,在这个循环中,堆栈中的每张图像都被标记并绘制在一个图中(而不是在面板中)。这个图在每个迭代动作中都会被保存和关闭。代码如下图所示。

# Label and save big image stack
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class CanvasPanelA1(wx.Panel):
    def __init__(self, parent, ID):
        wx.Panel.__init__(self, parent, ID, style=wx.SUNKEN_BORDER)

        self.figure = Figure()
        self.figure.set_facecolor("BLACK")
        self.canvas = FigureCanvas(self, -1, self.figure)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)
        self.SetSizer(self.sizer)
        self.Fit()

    def OnPlot(self, event):
        self.frame = frame
        from numpy import amin, amax
        self.vmin = amin(frame.video)
        self.vmax = amax(frame.video)
        self.nframes = frame.video.shape[0]
        self.axes = self.figure.add_subplot(111)
        self.axes.imshow(frame.video[0],cmap='gray',vmin=self.vmin,vmax=self.vmax)
        self.axes.axis('off')
        self.canvas.draw()
        self.SetSizer(self.sizer)
        frame.Layout()

class MyFrame(wx.Frame):
    def __init__(self, parent, ID, title):
        wx.Frame.__init__(self, parent, ID, title)
        self.panel = CanvasPanelA1(self, wx.ID_ANY)
        self.panel.SetMinSize((200,200))
        self.button_RUN = wx.Button(self, label="Run")
        self.gauge = wx.Gauge(self, range = 100, size = (250, 25), style = wx.GA_HORIZONTAL)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(self.panel, 3, wx.EXPAND)

        mainSizer.Add(self.button_RUN,0,flag=wx.CENTER|wx.ALL)
        mainSizer.Add(self.gauge,0,flag=wx.CENTER|wx.EXPAND)
        self.button_RUN.Bind(wx.EVT_BUTTON, self.OnRun)
        self.SetSizer(mainSizer)
        self.SetAutoLayout(True)
        self.Layout()
    def OnRun(self,event):
        from skimage.color import label2rgb 
        from skimage.measure import label
        import matplotlib as mpl
        import matplotlib.pyplot as plt
        from skimage import io
        from skimage.util import img_as_ubyte
        import os
        #read video
        video_name = './video1.tif'
        self.video = io.imread(video_name, as_gray=False, plugin='tifffile')
        self.video = img_as_ubyte(self.video)
        self.nframes = self.video.shape[0]
        self.gauge.SetRange(self.nframes-1)

        #show one image on panel
        self.panel.OnPlot(wx.EVT_DISPLAY_CHANGED)
        self.panel.Refresh()
        # Create Output Directory
        dirname = 'Outputs'
        try:
            os.mkdir(os.path.join('.',dirname))
            print("Directory " , dirname ,  " Created ") 
        except FileExistsError:
            print("Directory " , dirname ,  " already exists")

        plt.ioff()
        for fr in range(self.nframes):
            print('frame=',fr)
            label_image = label(self.video[fr], background=0) 
            image_label_overlay = label2rgb(label_image,bg_label=0)
            fig, ax = plt.subplots(figsize=(10, 6))
            ax.imshow(image_label_overlay)
            plt.axis('off')
            imname = 'imgout'+str(fr).zfill(len(str(self.nframes)))+'.tif'
            output_path = os.path.join('.',dirname,imname)
            plt.savefig(output_path,bbox_inches='tight',facecolor='black',edgecolor='none')
            plt.close(fig)
            self.gauge.SetValue(fr)
        plt.ion()

app = wx.App()
frame = MyFrame(None, -1, "Label videos")
frame.Show()
app.MainLoop()  

到目前为止,这段代码在较小的堆栈中是可行的,但是一旦我提供大于1250帧的图像堆栈,它就会给出以下错误。

File "C:\Users\...\lib\site-packages\matplotlib\backends\backend_wx.py", line 1413, in _init_toolbar    
self.Realize()    
**wxAssertionError**: C++ assertion "Assert failure" failed at ..\..\src\msw\toolbar.cpp(938) in wxToolBar::Realize(): Could not add bitmap to toolbar

因为这个错误似乎与工具栏有关,而我又没有使用它,所以我试着用以下方法来禁用它 mpl.rcParams['toolbar'] = 'None',放在循环之前,但我又得到了另一个断言错误(AssertionError)。

File "C:\Users\...\lib\site-packages\matplotlib\backends\backend_wx.py", line 1585, in __init__
self.SetFieldsCount(2)
**wxAssertionError**: C++ assertion "m_hdc" failed at ..\..\src\msw\textmeasure.cpp(64) in wxTextMeasure::BeginMeasuring(): Must not be used with non-native wxDCs

我也试过使用 wx.CallAfter(self.gauge.SetValue, fr) 以此来避免在绘制和保存图片时更新主界面,但效果并不理想。

有人知道是什么原因吗?

以下是我的软件包版本:conda 4.4.1 python 3.6.8 wxPython 4.0.6 spyder 3.3.6 scikit-image 0.15.0 (编辑:用最新的版本也试过了=0.16.2,还是不成功) matplotlib 3.1.1 numpy 1.16.4。

python matplotlib image-processing wxpython large-data
1个回答
0
投票

我发这个问题已经4个月了,显然没有人知道答案。

我想说是matplotlib wxpython后台在处理大量数据的时候出现了一些bug。


0
投票

好吧,我想我解决的方法是根据 本回答 遇到类似的问题,似乎不应该在循环中创建新的图形,即使在循环中关闭该图形。我的RAM在每次循环迭代后都在增加,不管关闭每一个新的图形。

解决方法:在循环前创建图实例。然后,在循环中绘制(如果你想保存的话),并且,不关闭图形,而只需用 plt.clf(). 只有在循环后才关闭图,如下面的例子。

fig, ax = plt.subplots(figsize=(10, 6)) #create figure and axes
for fr in range(self.nframes):
    print('frame=',fr)
    label_image = label(self.video[fr], background=0) #some processing
    image_label_overlay = label2rgb(label_image,bg_label=0)   
    ax.imshow(image_label_overlay) #plot the image
    plt.axis('off')
    imname = 'imgout'+str(fr).zfill(len(str(self.nframes)))+'.tif'  
    output_path = os.path.join('.',dirname,imname)
    plt.savefig(output_path,bbox_inches='tight',facecolor='black',edgecolor='none') #save the figure
    self.gauge.SetValue(fr) #update gauge bar
    plt.clf() #clear current figure
plt.close(fig) #closes figure
© www.soinside.com 2019 - 2024. All rights reserved.