在Gtk Widget中显示OpenCV图像时内存泄漏

时间:2015-06-03 15:52:54

标签: opencv python-3.x gtk glib gdk

我正在尝试创建一个Gtk Widget,您可以将OpenCV图像传递给它,然后显示它。我创建了一个继承自Gtk.Image的类,用于显示图像。您使用show_frame方法将OpenCV图像传递给此类,然后更新Gtk.Image以显示该图像。

我已对此进行了测试并且工作正常,即在调用show_frame方法时图像被正确显示和更新。但是每次更新图像时,使用的内存都会增加,直到内存不足并且程序崩溃。

我认为这是由于图像没有正确释放的内存。然而,我不知道如何解决这个问题。一旦收到新帧,我就尝试不引用gbytes,但这没有帮助。内存仅在调用set_from_pixbuf函数时建立。如果这被注释掉,则内存使用量保持不变。

class OpenCVImageViewer(Gtk.Image):
    def __init__(self):
        Gtk.Image.__init__(self)

    def show_frame(self, frame):
        # Convert to opencv BGR to Gtk RGB
        rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # Get details about frame in order to set up pixbuffer
        height = rgb_image.shape[0]
        width = rgb_image.shape[1]
        nChannels = rgb_image.shape[2]

        gbytes = GLib.Bytes.new(rgb_image.tostring())
        pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(gbytes, GdkPixbuf.Colorspace.RGB, False,
                                                 8, width, height, width*nChannels)

        # Add Gtk to main thread loop for thread safety
        GLib.idle_add(self.set_from_pixbuf, pixbuf)
        GLib.idle_add(self.queue_draw)

2 个答案:

答案 0 :(得分:3)

那么,

我找到了一个解决方案,但我不明白它为什么会起作用:用pixbuffer的副本设置图像。

 imageWidget.set_from_pixbuf(pixbuffer.copy())

在观察到缩放的pixbuffers(即pixbuffer.scale_simple的结果)内存泄漏消失后,我找到了这个解决方案。

答案 1 :(得分:0)

摘自PyGTK常见问题解答,section 5.17

  

python包装器与其底层C对象之间存在引用循环;这意味着当没有更多用户引用时,对象将不会被自动释放,并且您将需要垃圾回收器(这可能需要几个周期)。这偶尔会导致奇怪的问题,例如FAQ 8.4中描述的pixbufs

来自section 8.4

  

答案是"有趣的GC行为"在Python中。显然,一旦对象超出范围,终结器就不一定被调用。我的猜测是python内存管理器并不直接知道为图像缓冲区分配的存储空间(因为它是由gdk分配的),因此它并不知道内存的存储速度有多快消耗。   解决方案是在适当的地方调用gc.collect()。

     

例如,我有一些看起来像这样的代码:

for image_path in images:
   pb = gtk.gdk.pixbuf_new_from_file(image_path)
   pb = pb.scale_simple(thumb_width, thumb_height, gtk.gdk.INTERP_BILINEAR)
   thumb_list_model.set_value(thumb_list_model.append(None), 0, pb)
     

这为任何合理的图像集咀嚼了无法接受的大量内存。将代码更改为这样可以解决问题:

import gc

for image_path in images:
   pb = gtk.gdk.pixbuf_new_from_file(image_path)
   pb = pb.scale_simple(thumb_width, thumb_height, gtk.gdk.INTERP_BILINEAR)
   thumb_list_model.set_value(thumb_list_model.append(None), 0, pb)
   del pb
   gc.collect()

我不确定你应该在代码中调用垃圾收集器(因为我不太了解Python),但我相信这是解决问题的方法。