Как запустить видео в контейнере PyQt?

В контейнере QVideoWidget на PyQt5 нужно запустить видео с компа, на котором должны искаться объекты через 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_())

Ответы (1 шт):

Автор решения: S. Nick

cap.release() - Закрывает видеофайл или записывающее устройство.

import sys
import os
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets                             #, uiс
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QLabel, QVBoxLayout, QPushButton, QWidget
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__()
        self.source = source

    def run(self):
#        VIDEO_NAME = '20201024161726.mp4'
        CWD_PATH = os.getcwd()
        PATH_TO_VIDEO = os.path.join(CWD_PATH, self.source)    # 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)            # ? frame_expanded
                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
                    
            self.msleep(20)                                                    # +++  
# Закрывает видеофайл или записывающее устройство.            
#            cap.release()                                                     # ---  

# ? Уничтожает все окна HighGUI. 
# ? Функция destroyAllWindows уничтожает все открытые окна HighGUI.
            cv2.destroyAllWindows()


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

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

        self.centralWidget = QWidget()              # +++
        self.setCentralWidget(self.centralWidget)   # +++

        self.label_video = QLabel('QLabel')

        layout = QVBoxLayout(self.centralWidget)    # +++ self.centralWidget
        layout.addWidget(self.label_video)

#        self.widget.setLayout(layout)

        self.thread = ThreadOpenCV('Samonastrojka.avi')     # <---- ('20201024161726.mp4')
        self.thread.changePixmap.connect(self.setImage)

        self.btn1 = QPushButton('Загрузить видео')
        self.btn1.clicked.connect(self.playVideo)
        layout.addWidget(self.btn1)
        

    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_())

введите сюда описание изображения

→ Ссылка