2016-04-16 1 views
3

Я написал приложение OpenCV, которое в основном захватывает кадры с камеры, выполняет некоторую обработку изображения и отображает изображение в двух отредактированных вариантах. Во-первых, я использовал cv2.imshow() для отображения изображений, но в то время как OpenCV (Build без поддержки Qt) не может предоставить современные элементы GUI, я решил использовать PySide для моего графического интерфейса.Ошибка памяти QImage

Но так как это я получаю эту ошибку после обработки около 830-850 кадров (независимо от того, какая скорость таймера я использую, или сколько обработки изображений я):

QImage: out of memory, returning null image 

для обоих моих взглядов изображения в графическом интерфейсе, а затем в каждом цикле это один:

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, я могу видеть использование оперативной памяти моей программы:

enter image description here

В момент аварии, приложение использует около 1,5 Гб оперативной памяти. Я пробовал использовать модуль gc и gc.collect() после del img, без успеха.

Что еще я могу сделать?

EDIT:

Резьбовое Camera класс здесь не имеет значения, ошибка делает также появляться без него.

+0

Можете ли вы предоставить полную автономную программу, которая позволяет нам воспроизводить вашу проблему? – tfv

+0

Im не дома прямо сейчас, но я отправлю его в ближайшее время! Любые идеи о том, как удалить экземпляр img? – linusg

+0

@tfv Я обновил свой код прямо сейчас. Было бы неплохо, если бы вы могли посмотреть на него ... – linusg

ответ

1

Кажется, это ошибка PySide, использующая PyQt, исправит ее. Это даже не связано с OpenCV. Это не похоже на то будет решение для использования PySide прямо сейчас ...

1

Видимо, это ошибка из-за проблем сбора мусора при использовании Python 3.x

Простой обходной путь с помощью ctypes была предоставлена здесь https://bugreports.qt.io/browse/PYSIDE-140 и https://github.com/matplotlib/matplotlib/issues/4283#issuecomment-92773487

На последнем звене см. сообщение «mfitzp прокомментировал 24 апреля 2015 года». Это сработало для меня!

+0

Спасибо за указание, что я нашел, что около 3 месяцев назад: D – linusg

0

Посмотрите на мой комментарий на PySide-140: https://bugreports.qt.io/browse/PYSIDE-140

Избежание смешивания объектов питона с объектами 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 
+0

Это было looooong ago, но спасибо вам в любом случае. Метод refcounter как-то отлично работал для меня. – linusg

+0

Оставил его не только для вас, но и для тех, кто ищет решение той же/подобной проблемы. Обходной путь Refcount не работает с Qt5 с использованием QTextEdit. Мое решение выше работает для этой ситуации ;-) – jkolczasty

1

В моем случае, я использовал QStackedWidget для переключения между различными видами и QTimer, чтобы вызвать переключатель вида (на определенных условиях).

Я использовал functools.partial для передачи аргументов от одного вида к другому, среди которых я передать экземпляры, такие как PIL.Image, QImage, ImageQt и QPixmap.

Вот где все пошло не так.Когда такие ресурсы передаются из одного представления в другое, они не очищаются сборщиком мусора.

Что сработало для меня.

  1. Объявите все переменные, которые вы планируете перейти от одного вида к другому, как свойства класса (в конструкторе QWidget). Храните все ресурсы изображения (например, PIL.Image, QImage, ImageQt и QPixmap) в этих переменных/свойствах.

  2. Не передавать никакие аргументы (содержащие ресурсы изображения) из одного вида в другой.


Кроме того, если вы используете PyQT с OpenCV, обратите внимание, что OpenCV 2.9 содержит ошибку, которая вызывает утечку памяти. Обязательно обновите до последней версии (либо версии 2.x или 3.x), прежде чем потратить часы на поиск места утечки памяти.

+0

Спасибо за ваш ответ :) Несмотря на то, что это в настоящее время не имеет для меня значения, я помню, используя QStackedWidget, но не на 100% уверен, что он был в этом приложении. Как вы можете видеть в трассировке, я тогда использовал OpenCV 3.1.0! – linusg