Search code examples
pythonopencvpyqtpyqt5qpixmap

PyQt showing video stream from opencv


Try to link PyQt and Opencv video feed, can't understand how to apply while loop for continuously streaming video. It just take a still picture.Please can anyone help to solve the problem.

  • PtQt=5

  • Python=3.6.1


class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 Video'
        self.left = 100
        self.top = 100
        self.width = 640
        self.height = 480
        self.initUI()


    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.resize(1800, 1200)
        #create a label
        label = QLabel(self)
        cap = cv2.VideoCapture(0)
        ret, frame = cap.read()
        rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
                                         QtGui.QImage.Format_RGB888)
        convertToQtFormat = QtGui.QPixmap.fromImage(convertToQtFormat)
        pixmap = QPixmap(convertToQtFormat)
        resizeImage = pixmap.scaled(640, 480, QtCore.Qt.KeepAspectRatio)
        QApplication.processEvents()
        label.setPixmap(resizeImage)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Solution

  • The problem is that the function that obtains the image is executed only once and not updating the label.
    The correct way is to place it inside a loop, but it will result in blocking the main window. This blocking of main window can be solved by using the QThread class and send through a signal QImage to update the label. For example:

    import cv2
    import sys
    from PyQt5.QtWidgets import  QWidget, QLabel, QApplication
    from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
    from PyQt5.QtGui import QImage, QPixmap
    
    class Thread(QThread):
        changePixmap = pyqtSignal(QImage)
    
        def run(self):
            cap = cv2.VideoCapture(0)
            while True:
                ret, frame = cap.read()
                if ret:
                    # https://stackoverflow.com/a/55468544/6622587
                    rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    h, w, ch = rgbImage.shape
                    bytesPerLine = ch * w
                    convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
                    p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
                    self.changePixmap.emit(p)
    
    
    class App(QWidget):
        def __init__(self):
            super().__init__()
            [...]
            self.initUI()
    
        @pyqtSlot(QImage)
        def setImage(self, image):
            self.label.setPixmap(QPixmap.fromImage(image))
    
        def initUI(self):
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            self.resize(1800, 1200)
            # create a label
            self.label = QLabel(self)
            self.label.move(280, 120)
            self.label.resize(640, 480)
            th = Thread(self)
            th.changePixmap.connect(self.setImage)
            th.start()
            self.show()