QImage内存泄漏
QImage memory leak
我编写了一个 OpenCV 应用程序,它基本上从相机抓取帧,进行一些图像处理并以两个编辑后的变体显示图像。首先,我使用 cv2.imshow()
来显示图像,但是当 OpenCV(不支持 Qt 的构建)无法提供现代 GUI 元素时,我决定使用 PySide
作为我的 GUI。
但是因为这个我在处理大约 830-850 帧后得到这个错误(无论我使用什么定时器速率,或者我做了多少图像处理):
QImage: out of memory, returning null image
对于我在 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
这是我的部分代码(没有图像处理,但也会产生错误):
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_())
在 Windows 任务管理器中,我可以看到我的程序的 RAM 使用情况:
在崩溃时,应用程序使用了大约 1.5 GB 的 RAM。我试过在 del img
之后使用 gc
模块和 gc.collect()
,但没有成功。
我还能做什么?
编辑:
线程Camera
class这里无所谓,没有它也会出现错误
这似乎是 PySide 特有的错误,使用 PyQt 会修复它。它甚至与 OpenCV 无关。看起来现在不会有使用 PySide 的解决方案...
显然这个错误是由于使用 python 3.x
时的垃圾收集问题造成的
此处提供了使用 ctypes 的简单解决方法 https://bugreports.qt.io/browse/PYSIDE-140 and https://github.com/matplotlib/matplotlib/issues/4283#issuecomment-92773487
最后 link 见 post "mfitzp commented on Apr 24, 2015"。这对我有用!
看看我在 PYSIDE-140 上的评论:https://bugreports.qt.io/browse/PYSIDE-140
避免将 python 对象与 Qt 对象混合。试试下面的例子,避免玩引用计数修复。
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
就我而言,我使用 QStackedWidget
在不同视图之间切换,并使用 QTimer
触发视图切换(在特定条件下)。
我使用 functools.partial
将参数从一个视图传递到另一个视图,其中我确实传递了实例,例如 PIL.Image
、QImage
、ImageQt
和 QPixmap
。
那就是它出错的地方。当这些资源从一个视图传递到另一个视图时,垃圾收集器没有很好地清理它们。
什么对我有用。
将您计划从一个视图传递到另一个视图的所有变量声明为 class 属性(在 QWidget
的构造函数中)。将所有图片资源(如PIL.Image
、QImage
、ImageQt
和QPixmap
)存储在variables/properties.
中
不要将任何参数(包含图像资源)从一个视图传递到另一个视图。
此外,如果您将 PyQT 与 OpenCV 结合使用,请注意 OpenCV 2.9 包含一个错误,该错误会导致内存泄漏。在花费数小时找出内存泄漏的来源之前,请确保升级到最新版本(最新的 2.x 或 3.x)。
我编写了一个 OpenCV 应用程序,它基本上从相机抓取帧,进行一些图像处理并以两个编辑后的变体显示图像。首先,我使用 cv2.imshow()
来显示图像,但是当 OpenCV(不支持 Qt 的构建)无法提供现代 GUI 元素时,我决定使用 PySide
作为我的 GUI。
但是因为这个我在处理大约 830-850 帧后得到这个错误(无论我使用什么定时器速率,或者我做了多少图像处理):
QImage: out of memory, returning null image
对于我在 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
这是我的部分代码(没有图像处理,但也会产生错误):
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_())
在 Windows 任务管理器中,我可以看到我的程序的 RAM 使用情况:
在崩溃时,应用程序使用了大约 1.5 GB 的 RAM。我试过在 del img
之后使用 gc
模块和 gc.collect()
,但没有成功。
我还能做什么?
编辑:
线程Camera
class这里无所谓,没有它也会出现错误
这似乎是 PySide 特有的错误,使用 PyQt 会修复它。它甚至与 OpenCV 无关。看起来现在不会有使用 PySide 的解决方案...
显然这个错误是由于使用 python 3.x
时的垃圾收集问题造成的此处提供了使用 ctypes 的简单解决方法 https://bugreports.qt.io/browse/PYSIDE-140 and https://github.com/matplotlib/matplotlib/issues/4283#issuecomment-92773487
最后 link 见 post "mfitzp commented on Apr 24, 2015"。这对我有用!
看看我在 PYSIDE-140 上的评论:https://bugreports.qt.io/browse/PYSIDE-140
避免将 python 对象与 Qt 对象混合。试试下面的例子,避免玩引用计数修复。
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
就我而言,我使用 QStackedWidget
在不同视图之间切换,并使用 QTimer
触发视图切换(在特定条件下)。
我使用 functools.partial
将参数从一个视图传递到另一个视图,其中我确实传递了实例,例如 PIL.Image
、QImage
、ImageQt
和 QPixmap
。
那就是它出错的地方。当这些资源从一个视图传递到另一个视图时,垃圾收集器没有很好地清理它们。
什么对我有用。
将您计划从一个视图传递到另一个视图的所有变量声明为 class 属性(在
QWidget
的构造函数中)。将所有图片资源(如PIL.Image
、QImage
、ImageQt
和QPixmap
)存储在variables/properties. 中
不要将任何参数(包含图像资源)从一个视图传递到另一个视图。
此外,如果您将 PyQT 与 OpenCV 结合使用,请注意 OpenCV 2.9 包含一个错误,该错误会导致内存泄漏。在花费数小时找出内存泄漏的来源之前,请确保升级到最新版本(最新的 2.x 或 3.x)。