【问题标题】:How to record the video from a webcam in a pyqt5 gui using OpenCV and QThread?如何使用 OpenCV 和 QThread 在 pyqt5 gui 中录制来自网络摄像头的视频?
【发布时间】:2020-09-28 10:25:31
【问题描述】:

我正在尝试制作 pyqt5 Gui,它可以显示网络摄像头实时提要,同时记录提要,并在关闭时将其保存在本地。我设法在pyqt gui中使用Timer(QTimer)实现了这一点,但是当我尝试使用Qthread(我真的需要)实现它时,只有实时提要正在工作。

每当我添加录制视频所需的代码并运行程序时,它都会显示 Python 已停止工作 并关闭。这是我的代码:

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


class Thread(QThread):
    changePixmap = pyqtSignal(QImage)

    def run(self):
        self.cap = cv2.VideoCapture(0)

        self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

        self.codec = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
        self.writer = cv2.VideoWriter('output.avi', self.codec, 30.0, (self.width, self.height))

        while self.cap.isOpened():
            ret, self.frame = self.cap.read()
            if ret:
                self.frame = cv2.flip(self.frame, 1)
                rgbimage = cv2.cvtColor(self.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 MyApp(QWidget):
    def __init__(self):
        super(MyApp, self).__init__()
        self.title = 'Camera'
        self.initUI()

    def initUI(self):
        self.label = QLabel(self)
        lay = QVBoxLayout()
        lay.addWidget(self.label)
        self.setLayout(lay)

        self.th = Thread()
        self.th.changePixmap.connect(self.setImage)
        self.th.start()
        self.show()

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


def main():
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

我尝试将.write() 也放在 Thread 类的 run() 中,这也显示了相同的错误。你们能指出我做错了什么以及如何使它工作。我是 python 和 pyqt 的新手。 提前致谢。

【问题讨论】:

    标签: python-3.x pyqt pyqt5 qthread opencv-python


    【解决方案1】:

    您需要分离线程。第一个线程用于信号,第二个用于记录,主线程用于 GUI。试试下面的代码。有一个按钮可以开始/停止记录。

    import sys
    import cv2
    
    from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout
    from PyQt5.QtGui import QImage, QPixmap
    from PyQt5.QtCore import QTimer, QThread, pyqtSignal, pyqtSlot
    from PyQt5 import QtWidgets, QtCore, QtGui
    
    #https://ru.stackoverflow.com/a/1150993/396441
    
    class Thread1(QThread):
        changePixmap = pyqtSignal(QImage)
        
        def __init__(self, *args, **kwargs):
            super().__init__()
    
        def run(self):
            self.cap1 = cv2.VideoCapture(0, cv2.CAP_DSHOW)
            self.cap1.set(3,480)
            self.cap1.set(4,640)
            self.cap1.set(5,30)
            while True:
                ret1, image1 = self.cap1.read()
                if ret1:
                    im1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
                    height1, width1, channel1 = im1.shape
                    step1 = channel1 * width1
                    qImg1 = QImage(im1.data, width1, height1, step1, QImage.Format_RGB888)
                    self.changePixmap.emit(qImg1)
    
    class Thread2(QThread):
        
        def __init__(self, *args, **kwargs):
            super().__init__()
            self.active = True
    
        def run(self):
            if self.active:            
                self.fourcc = cv2.VideoWriter_fourcc(*'XVID') 
                self.out1 = cv2.VideoWriter('output.avi', self.fourcc, 30, (640,480))
                self.cap1 = cv2.VideoCapture(0, cv2.CAP_DSHOW)
                self.cap1.set(3, 480)
                self.cap1.set(4, 640)
                self.cap1.set(5, 30)
                while self.active:                      
                    ret1, image1 = self.cap1.read()
                    if ret1:
                        self.out1.write(image1)     
                    self.msleep(10)                      
    
        def stop(self):
            self.out1.release()
    
           
    class MainWindow(QWidget):
        
        def __init__(self):
            super().__init__()
            self.resize(660, 520)
            self.control_bt = QPushButton('START')
            self.control_bt.clicked.connect(self.controlTimer)
            self.image_label = QLabel()
            self.saveTimer = QTimer()
            self.th1 = Thread1(self)
            self.th1.changePixmap.connect(self.setImage)
            self.th1.start()
            
            vlayout = QVBoxLayout(self)
            vlayout.addWidget(self.image_label)
            vlayout.addWidget(self.control_bt)   
    
        @QtCore.pyqtSlot(QImage)
        def setImage(self, qImg1):
            self.image_label.setPixmap(QPixmap.fromImage(qImg1))
    
        def controlTimer(self):
            if not self.saveTimer.isActive():
                # write video
                self.saveTimer.start()
                self.th2 = Thread2(self)
                self.th2.active = True                                
                self.th2.start()
                # update control_bt text
                self.control_bt.setText("STOP")
            else:
                # stop writing
                self.saveTimer.stop()
                self.th2.active = False                   
                self.th2.stop()                         
                self.th2.terminate()                    
                # update control_bt text
                self.control_bt.setText("START")
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        mainWindow = MainWindow()
        mainWindow.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • Python 现在没有崩溃,并且正在录制视频,但现在我面临 2 个其他问题 - 1,即使在停止并关闭小部件后,代码仍然运行,我相信这是由于Thread1() 中的 while 循环。你能建议解决tis问题的方法吗? 2、
    【解决方案2】:

    你把 .write() 放在 Thread 类的 run() 里面是对的。

    喜欢:

    ...
            while self.cap.isOpened():
                ret, self.frame = self.cap.read()
                if ret:
                    self.frame = cv2.flip(self.frame, 1)
                    rgbimage = cv2.cvtColor(self.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)
    
                    # put your writer() here, make sure your param is frame
                    # not converted to QtImage format
                    self.writer.write(rgbimage)
    
                    self.changePixmap.emit(p)
    ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-16
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多