我试图创建一个Gtk小部件,您可以通过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)
嗯,
我找到了一个解决方案,但我不明白为什么它工作:设置图像与pixbuffer的副本。
imageWidget.set_from_pixbuf(pixbuffer.copy())
我来到这个解决方案后,观察到内存泄漏消失缩放像素缓冲区(即pixbuffer.scale_simple
的结果)。
节选自PyGTK FAQ,第5.17节:
在python包装器和它的底层C对象之间有一个引用循环;这意味着当没有更多的用户引用时,对象将不会被自动释放,并且您将需要垃圾收集器启动(这可能需要几个周期)。这偶尔会导致一些奇怪的问题,比如FAQ 8.4
中描述的pixbufs。
和8.4节中的
答案是Python中的"有趣的GC行为"。显然,当对象超出作用域时,不一定会立即调用终结器。我的猜测是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),但我相信这是解决它的方法。