简体   繁体   English

QImage内存泄漏

[英]QImage memory leak

I've written a OpenCV application which basically grabs frames from a camera, does some image processing and displays the image in two edited variants. 我编写了一个OpenCV应用程序,它基本上从相机中抓取帧,进行一些图像处理并以两种编辑的变体显示图像。 First, I've used cv2.imshow() to display the images, but while OpenCV (Build without Qt support) isn't able to provide modern GUI elements, I decided to use PySide for my GUI. 首先,我使用cv2.imshow()来显示图像,但是当OpenCV(没有Qt支持的Build)无法提供现代GUI元素时,我决定将PySide用于我的GUI。

But since this I get this error after processing about 830-850 frames (no matter, what timer rate I use, or how much image processing I do): 但是因为我在处理了大约830-850帧之后得到了这个错误(无论我使用什么计时器速率,或者我做了多少图像处理):

QImage: out of memory, returning null image

for both of my image views in the GUI, and then in each loop this one: 对于我在GUI中的两个图像视图,然后在每个循环中这个:

OpenCV Error: Unspecified error (The numpy array of typenum=2, ndims=3 can not be created) in NumpyAllocator::allocate, file ..\..\..\opencv-3.1.0\modules\python\src2\cv2.cpp, line 184
OpenCV Error: Insufficient memory (Failed to allocate 921600 bytes) in cv::OutOfMemoryError, file ..\..\..\opencv-3.1.0\modules\core\src\alloc.cpp, line 52
Traceback (most recent call last):
  File "C:/myfile.py", line 140, in process_frame
    img = QtGui.QImage(cv2.cvtColor(thresh_img, cv2.COLOR_RGB2BGR), self.width, self.height,
cv2.error: ..\..\..\opencv-3.1.0\modules\core\src\alloc.cpp:52: error: (-4) Failed to allocate 921600 bytes in function cv::OutOfMemoryError

Here's a part of my code (without image processing, but it also produces the error): 这是我的代码的一部分(没有图像处理,但它也产生错误):

import cv2
import sys
from PySide import QtGui, QtCore
from threading import Thread


class MainWindow(QtGui.QMainWindow):
    def __init__(self, cam=0, parent=None):
        super(MainWindow, self).__init__(parent)

        self.camera = Camera(cam).start()
        self.title = "Cam %s" % cam
        self.counter = 0

        widget = QtGui.QWidget()
        self.layout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight)

        self.video_frame = QtGui.QLabel()
        self.thresh_frame = QtGui.QLabel()

        self.layout.addWidget(self.video_frame)
        self.layout.addWidget(self.thresh_frame)
        self.layout.addStretch()

        self.setCentralWidget(widget)
        widget.setLayout(self.layout)

        self.setMinimumSize(640, 480)
        self._timer = QtCore.QTimer(self)
        self._timer.timeout.connect(self.process_frame)
        self._timer.start(20)

    def process_frame(self):
        self.counter += 1
        print(self.counter)
        self.frame = self.camera.read()
        self.height, self.width = self.frame.shape[:2]

        thresh_img = cv2.threshold(cv2.cvtColor(self.frame, cv2.COLOR_RGB2GRAY), 0, 255,
                                   cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        thresh_img = cv2.erode(thresh_img, None, iterations=2)
        thresh_img = cv2.dilate(thresh_img, None, iterations=2)
        thresh_img = cv2.cvtColor(thresh_img, cv2.COLOR_GRAY2RGB)

        img = QtGui.QImage(cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR), self.width, self.height,
                           QtGui.QImage.Format_RGB888)
        img = QtGui.QPixmap.fromImage(img)
        self.video_frame.setPixmap(img)

        img = QtGui.QImage(cv2.cvtColor(thresh_img, cv2.COLOR_RGB2BGR), self.width, self.height,
                           QtGui.QImage.Format_RGB888)
        img = QtGui.QPixmap.fromImage(img)
        self.thresh_frame.setPixmap(img)

    def closeEvent(self, event):
        self.camera.stop()
        event.accept()


class Camera:
    def __init__(self, src=0):
        self.stream = cv2.VideoCapture(src)
        (self.grabbed, self.frame) = self.stream.read()

        self.stopped = False

    def start(self):
        Thread(target=self.update, args=()).start()
        return self

    def update(self):
        while True:
            if self.stopped:
                return
            (self.grabbed, self.frame) = self.stream.read()

    def read(self):
        return self.frame

    def stop(self):
        self.stopped = True

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow(0)
    window.show()
    sys.exit(app.exec_())

In the Windows task manager I can see the RAM usage of my program: 在Windows任务管理器中,我可以看到我的程序的RAM使用情况:

在此输入图像描述

At the point of crash, the app uses about 1.5 GB of RAM. 在崩溃时,该应用程序使用大约1.5 GB的RAM。 I've tried using the gc module and gc.collect() after del img , no success. 我尝试在del img之后使用gc模块和gc.collect() ,但没有成功。

What else can I do? 我还可以做些什么?

EDIT: 编辑:

The threaded Camera class doesn't matter here, the error does also appear without it. 线程Camera类在这里没关系,错误也会在没有它的情况下出现。

It seems to be a PySide specific bug, using PyQt will fix it. 它似乎是一个PySide特定的bug,使用PyQt会修复它。 It's not even OpenCV related. 它甚至与OpenCV无关。 It doesn't look like there will be a solution for using PySide right now... 现在看起来不会有使用PySide的解决方案......

Apparently this bug is due to garbage collection issues when using python 3.x 显然这个错误是由于使用python 3.x时的垃圾收集问题

A simple workaround using ctypes has been provided here https://bugreports.qt.io/browse/PYSIDE-140 and https://github.com/matplotlib/matplotlib/issues/4283#issuecomment-92773487 这里提供了一个使用ctypes的简单解决方法https://bugreports.qt.io/browse/PYSIDE-140https://github.com/matplotlib/matplotlib/issues/4283#issuecomment-92773487

On the last link see the post "mfitzp commented on Apr 24, 2015". 在最后一个链接上看到帖子“mfitzp评论于2015年4月24日”。 This worked for me! 这对我有用!

In my case, I was using QStackedWidget for switching between different views and QTimer to trigger the view switch (on certain conditions). 在我的例子中,我使用QStackedWidget在不同视图和QTimer之间切换以触发视图切换(在某些条件下)。

I was using functools.partial for passing arguments from one view to another, among which I did pass instances, such as PIL.Image , QImage , ImageQt and QPixmap . 我使用functools.partial将参数从一个视图传递到另一个视图,其中我确实传递了实例,例如PIL.ImageQImageImageQtQPixmap

That's where it went wrong. 那是它出错的地方。 When such resources are passed from one view to another, they are not being cleaned well by the garbage collector. 当这些资源从一个视图传递到另一个视图时,垃圾收集器不能很好地清理它们。

What worked for me. 什么对我有用。

  1. Declare all variables that you plan to pass from one view to another as class properties (in the constructor of your QWidget ). 将您计划从一个视图传递到另一个视图的所有变量声明为类属性(在QWidget的构造函数中)。 Store all image resources (such as PIL.Image , QImage , ImageQt and QPixmap ) in those variables/properties. 将所有图像资源(例如PIL.ImageQImageImageQtQPixmap )存储在这些变量/属性中。

  2. Don't pass any arguments (containing image resources) from one view to another. 不要将任何参数(包含图像资源)从一个视图传递到另一个视图。


Additionally if you're using PyQT with OpenCV, note that OpenCV 2.9 contains a bug, which causes memory leak. 另外,如果您在OpenCV中使用PyQT,请注意OpenCV 2.9包含一个错误,导致内存泄漏。 Make sure to upgrade to latest version (either latest 2.x or 3.x) before you spend hours on finding out where the memory leaks come from. 确保在花费数小时查找内存泄漏的来源之前升级到最新版本(最新的2.x或3.x)。

Look at my comment at PYSIDE-140: https://bugreports.qt.io/browse/PYSIDE-140 看看我对PYSIDE-140的评论: https ://bugreports.qt.io/browse/PYSIDE-140

Avoiding mixing python objects with Qt objects. 避免将python对象与Qt对象混合。 Try example below and avoid playing with refcounts fixing. 尝试下面的示例,避免使用refcounts修复。

stream = cv2.VideoCapture(0)
grabbed, frame = stream.read()

height, width = frame.shape[:2]
data = Qt.QByteArray(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR).tostring())
qimg = Qt.QImage(data, width, height, Qt.QImage.Format_RGB888)

data.clear()
del data

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM