Search code examples
pythonpython-3.xopencvpyqt5qthread

Pyqt crashes when trying to show opencv videostream


I tried code from this answer and it crashes with error Process finished with exit code -1073740771 (0xC000041D) after some time (2-10 sec.) and sometimes with 0xC0000005. It crashes immediately if I try to drag the window. However when I put time.sleep(0.1) in run it works fine. If I use sleeps shorter than 0.1 it crashes again.

from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel,QMessageBox
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, Qt
import cv2
import sys
import time

class CamThread(QThread):
    changemap = pyqtSignal('QImage')

    def run(self):
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

        while True:
            ret, img_rgb = cap.read()
            if ret:
                self.rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB)
                self.convert = QImage(self.rgb.data, self.rgb.shape[1], self.rgb.shape[0], QImage.Format_RGB888)
                self.p = self.convert.scaled(640, 480, Qt.KeepAspectRatio)
                self.changemap.emit(self.p)
                #time.sleep(0.1)


class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'webcam'

        self.initUI()

    @pyqtSlot('QImage')
    def setImage(self, image):
        self.label.setPixmap(QPixmap.fromImage(image))

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(100, 100, 640, 480)
        self.resize(640, 480)
        self.label = QLabel(self)
        self.label.resize(640, 480)
        thr = CamThread(self)
        thr.changemap.connect(self.setImage)
        thr.start()

app = QApplication(sys.argv)
win = App()
#win.setAttribute(Qt.WA_DeleteOnClose, True)
win.show()
app.exit(app.exec_())

I thought that the problem is somewhere in signals/slots but haven't been able to find anything relevant.

  • Windows 10

  • Python - 3.7

  • Pyqt - 5.12

  • OpenCV - 3.4.5.20


Solution

  • Fixed it using QMutex and QWaitCondition to prevent update call while main thread is already updating. Apparently, issue was in that. eyllanesc, I'm new here as you see, should I make an answer in original thread?

    from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QMessageBox
    from PyQt5.QtGui import QImage, QPixmap
    from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, Qt, QMutex, QWaitCondition
    import cv2
    import sys
    import time
    
    
    class CamThread(QThread):
        changemap = pyqtSignal('QImage')
    
        def __init__(self, mutex, condition):
            super().__init__()
            self.mutex = mutex
            self.condition = condition
    
        def run(self):
            cap = cv2.VideoCapture(0)
            cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
            while True:
                try:
                    ret, img_rgb = cap.read()
                    if ret:
                        rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB)
    
                        #any other image processing here
    
                        convert = QImage(rgb.data, rgb.shape[1], rgb.shape[0], QImage.Format_RGB888)
                        p = convert.scaled(640, 480, Qt.KeepAspectRatio)
                        self.changemap.emit(p)
                        self.condition.wait(self.mutex)
    
                except:
                    print('error')
    
    
    class App(QWidget):
        time = 0
    
        def __init__(self):
            super().__init__()
            self.title = 'webcam'
            self.mutex = QMutex()
            self.condition = QWaitCondition()
            self.initUI()
    
        @pyqtSlot('QImage')
        def setImage(self, image):
            self.mutex.lock()
            try:
                self.label.setPixmap(QPixmap.fromImage(image))
            finally:
                self.mutex.unlock()
                self.condition.wakeAll()
    
        def initUI(self):
            self.mutex.lock()
            self.setWindowTitle(self.title)
            self.setGeometry(100, 100, 640, 480)
            self.resize(640, 480)
            self.label = QLabel(self)
            self.label.resize(640, 480)
            self.thr = CamThread(mutex = self.mutex,condition=self.condition)
            self.thr.changemap.connect(self.setImage)
            self.thr.start()
    
    
    app = QApplication(sys.argv)
    win = App()
    win.show()
    app.exit(app.exec_())
    

    N.B. You still need to properly stop thread and close camera connection in this example.