如何在 PyQt 容器中运行视频?

Posted

技术标签:

【中文标题】如何在 PyQt 容器中运行视频?【英文标题】:How do I run a video in a PyQt container? 【发布时间】:2021-05-11 13:42:14 【问题描述】:

在 PyQt 上的 QVideoWidget 容器中,您需要从计算机启动一个视频,通过 TensorFlow (openCV, cv2) 在该视频上搜索对象。问题是当按下按钮时,视频只显示一帧而没有其他内容。可能是什么问题呢?使用 PyCharm、Python 3.7 制作。

from PyQt5 import QtCore, QtGui, QtWidgets, uiс
import os
import cv2
import numpy as np
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QLabel, QVBoxLayout
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QImage, QPixmap

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

    def __init__(self, source):
        super().__init__()

    def run(self):

        # MODEL_NAME = 'inference_graph'
        VIDEO_NAME = '20201024161726.mp4'
        #
        # # Grab path to current working directory
        CWD_PATH = os.getcwd()
        PATH_TO_VIDEO = os.path.join(CWD_PATH, VIDEO_NAME)
        cap = cv2.VideoCapture(PATH_TO_VIDEO)
        while True:
            ret, frame = cap.read()

            if ret:
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                frame_expanded = np.expand_dims(frame_rgb, axis=0)
                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)

                if cv2.waitKey(1) == ord('q'):
                    break
            cap.release()
            cv2.destroyAllWindows()


class Widget(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        uic.loadUi('fire_detection.ui', self)
        self.show()

        self.label_video = QLabel()

        layout = QVBoxLayout()
        layout.addWidget(self.label_video)

        self.widget.setLayout(layout)

        self.thread = ThreadOpenCV('20201024161726.mp4')
        self.thread.changePixmap.connect(self.setImage)

        self.btn1.clicked.connect(self.playVideo)

    def playVideo(self):
        self.thread.start()

    def setImage(self, image):
        self.label_video.setPixmap(QPixmap.fromImage(image))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    mw = Widget()
    mw.show()
    sys.exit(app.exec_())

【问题讨论】:

waitKey 如果你不显示 cv2 窗口是没用的。当你想退出循环时,你不应该使用while running: 并设置running =False 你可能有问题,因为你有错误的缩进并且你在 while True 中运行 cap.release() - 所以它会在第一帧后释放它。 如果你不使用cv2.imshow(或类似的)创建窗口,那么你不需要cv2.destroyAllWindows() 感谢解答 【参考方案1】:

所有问题都是因为你有错误的缩进 - 你在 while-loop 中运行 cap.release() 所以它会在第一帧之后释放流。

   while True:
        ret, frame = cap.read()

        if ret:
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            frame_expanded = np.expand_dims(frame_rgb, axis=0)

            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)

   # --- after `while` ---

   cap.release()

您不会在cv2 中创建窗口,因此您不会在cv2.destroyAllWindows() 中创建窗口。 而cv2.waitKey(1) 也将无用,因为系统仅在显示窗口并且此窗口处于活动状态(聚焦)时才会向cv2 发送键/鼠标事件。


编辑:

完整的工作代码。它使用网络摄像头。

它不需要UI 文件。

它有开始和停止流媒体的按钮,

它还有切换按钮:RGB GRAYSCALE、NORMAL BLURED。

import os
import sys
import numpy as np
import cv2

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtWidgets import QLabel, QVBoxLayout, QPushButton, QWidget
from PyQt5.QtGui import QImage, QPixmap

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

    def __init__(self, source):
        super().__init__()

        self.source = source

        self.running = True
        self.grayscale = False
        self.blur = False
        
    def run(self):
        print('start')

        cap = cv2.VideoCapture(self.source)

        self.running = True
        
        while self.running:
            ret, frame = cap.read()

            if ret:
                if self.grayscale:
                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)
                else:
                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    
                if self.blur:
                    frame = cv2.blur(frame, (15, 15))

                h, w, ch = frame.shape
                bytes_per_line = ch * w   # PEP8: `lower_case_names` for variables
                
                image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
                image = image.scaled(640, 480, Qt.KeepAspectRatio)

                self.changePixmap.emit(image)
            
        cap.release()
        print('stop')
        
    def stop(self):
        self.running = False


class Widget(QtWidgets.QMainWindow):
    
    def __init__(self):
        super().__init__()

        # MODEL_NAME = 'inference_graph'
        VIDEO_NAME = '20201024161726.mp4'
        CWD_PATH = os.getcwd()
        PATH_TO_VIDEO = os.path.join(CWD_PATH, VIDEO_NAME)
       
        # webcam
        PATH_TO_VIDEO = 0

        self.thread = ThreadOpenCV(PATH_TO_VIDEO)
        self.thread.changePixmap.connect(self.setImage)

        layout = QVBoxLayout()

        self.label_video = QLabel()
        layout.addWidget(self.label_video)

        self.btn1 = QPushButton("PLAY")
        self.btn1.clicked.connect(self.playVideo)
        layout.addWidget(self.btn1)

        self.btn_stop = QPushButton("STOP")
        self.btn_stop.clicked.connect(self.stopVideo)
        layout.addWidget(self.btn_stop)
        
        self.btn_gray = QPushButton("RGB <-> GRAYSCALE")
        self.btn_gray.clicked.connect(self.grayVideo)
        layout.addWidget(self.btn_gray)

        self.btn_blur = QPushButton("NORMAL <-> BLURED")
        self.btn_blur.clicked.connect(self.blurVideo)
        layout.addWidget(self.btn_blur)

        self.widget = QWidget()
        self.widget.setLayout(layout)

        self.setCentralWidget(self.widget)
        
    def playVideo(self):
        self.thread.start()

    def stopVideo(self):
        self.thread.running = False

    def grayVideo(self):
        self.thread.grayscale = not self.thread.grayscale

    def blurVideo(self):
        self.thread.blur = not self.thread.blur

    def setImage(self, image):
        self.label_video.setPixmap(QPixmap.fromImage(image))

if __name__ == '__main__':
    
    app = QtWidgets.QApplication([])

    mw = Widget()
    mw.show()
    
    app.exec()

RGB,正常:

灰度,模糊:

【讨论】:

感谢您的回答,它有帮助)

以上是关于如何在 PyQt 容器中运行视频?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写 Dockerfile 来运行 Python3 + PyQt5

如何在 pyqt5 webbrowser 中为 youtube 视频创建全屏模式

在 PyQt 中动态调整视频大小

界面开发--- PyQt5实现打开图像及视频播放功能

如何使用opencv在pyqt5中同时在不同窗口中运行一个网络摄像头

如何在 pyqt GUI(QTdesigner)中显示“运行”控制台?