大型图像堆栈处理后 matplotlib wxPython 后端崩溃
matplotlib wxPython backend crashing after large image stack processing
我在处理大型图像堆栈时遇到了 wxAssertionError(如下所示)。让我用一个例子来解释。
我用 wxPython 制作了一个界面,只有一个面板、一个按钮和一个仪表条。
一旦用户点击按钮,代码就会读取一堆 8 位二进制图像 (1500 x 100 x 50),在面板上显示第一张图像并开始处理堆栈。可以下载示例堆栈here,点击'open->download'并将其放入与下面代码相同的文件夹中,如果您无法访问堆栈,请在评论中告诉我)。处理步骤是一个循环,其中堆栈中的每个图像都被标记并绘制在图形中(而不是在面板中)。在每次迭代中保存并关闭此图。代码如下:
# 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)
来避免在绘制和保存图像时更新主界面,但效果不佳。
有人知道是什么原因造成的吗?
以下是我的软件包版本:
康达 4.4.1
python3.6.8
wxPython 4.0.6
间谍 3.3.6
scikit-image 0.15.0(编辑:也尝试使用最新版本 = 0.16.2,仍然没有成功)
matplotlib 3.1.1
麻木 1.16.4
我发布这个问题已经 4 个月了,显然没有人知道答案。
我想说在处理大量数据时,matplotlib wxpython 后端存在一些错误。
好的,我想我已经根据 this answer 解决了类似的问题。
似乎不应该在循环内创建新图形,即使在循环内关闭该图形也是如此。无论关闭每个新图形,我的 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
我在处理大型图像堆栈时遇到了 wxAssertionError(如下所示)。让我用一个例子来解释。
我用 wxPython 制作了一个界面,只有一个面板、一个按钮和一个仪表条。
一旦用户点击按钮,代码就会读取一堆 8 位二进制图像 (1500 x 100 x 50),在面板上显示第一张图像并开始处理堆栈。可以下载示例堆栈here,点击'open->download'并将其放入与下面代码相同的文件夹中,如果您无法访问堆栈,请在评论中告诉我)。处理步骤是一个循环,其中堆栈中的每个图像都被标记并绘制在图形中(而不是在面板中)。在每次迭代中保存并关闭此图。代码如下:
# 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)
来避免在绘制和保存图像时更新主界面,但效果不佳。
有人知道是什么原因造成的吗?
以下是我的软件包版本:
康达 4.4.1
python3.6.8
wxPython 4.0.6
间谍 3.3.6
scikit-image 0.15.0(编辑:也尝试使用最新版本 = 0.16.2,仍然没有成功)
matplotlib 3.1.1
麻木 1.16.4
我发布这个问题已经 4 个月了,显然没有人知道答案。
我想说在处理大量数据时,matplotlib wxpython 后端存在一些错误。
好的,我想我已经根据 this answer 解决了类似的问题。 似乎不应该在循环内创建新图形,即使在循环内关闭该图形也是如此。无论关闭每个新图形,我的 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