PyQt 显示来自 opencv 的视频流

Posted

技术标签:

【中文标题】PyQt 显示来自 opencv 的视频流【英文标题】:PyQt showing video stream from opencv 【发布时间】:2017-06-07 05:39:40 【问题描述】:

尝试链接 PyQt 和 Opencv 视频源,无法理解如何应用 while 循环来连续流式传输视频。它只是拍一张静止的照片。请任何人帮忙解决这个问题。

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

【问题讨论】:

【参考方案1】:

问题是获取图片的函数只执行了一次,并没有更新标签。 正确的做法是把它放在一个循环里面,但是这样会导致主窗口阻塞。这种主窗口阻塞可以通过使用QThread 类并通过发送信号QImage 来更新标签来解决。例如:

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://***.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()

【讨论】:

你介意在这个答案上加上必要的import 语句,以及使它完整和可运行的任何其他必要的东西吗? 看起来需要 cv2 和 PyQt(4/5)【参考方案2】:

为 PySide2 和 qimage2ndarray 更新这个

from PySide2.QtCore import *
from PySide2.QtGui import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting

# Minimal implementation...

def displayFrame():
    ret, frame = cap.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    image = qimage2ndarray.array2qimage(frame)
    label.setPixmap(QPixmap.fromImage(image))

app = QApplication([])
window = QWidget()

# OPENCV

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

# timer for getting frames

timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button 
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()

# See also: https://gist.github.com/bsdnoobz/8464000

【讨论】:

非常感谢这个例子!对于 PySide6 导入和 requirements.txt,请参阅我在链接要点中的评论。【参考方案3】:

感谢 Taimur Islam 的提问。 感谢 eyllanesc 的精彩回答,我已经稍微修改了您的代码。我用的是PtQt=4 Python=2.7 没用opencv

import sys    
import numpy as np
import flycapture2 as fc2

from PyQt4.QtCore import (QThread, Qt, pyqtSignal)
from PyQt4.QtGui import (QPixmap, QImage, QApplication, QWidget, QLabel)

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

    def __init__(self, parent=None):
        QThread.__init__(self, parent=parent)       
        self.cameraSettings()


    def run(self):      
        while True:
            im = fc2.Image()
            self.c.retrieve_buffer(im)
            a = np.array(im)    

            rawImage = QImage(a.data, a.shape[1], a.shape[0], QImage.Format_Indexed8)

            self.changePixmap.emit(rawImage)

    def cameraSettings(self):
        print(fc2.get_library_version())
        self.c = fc2.Context()
        numberCam = self.c.get_num_of_cameras()
        print(numberCam)    
        self.c.connect(*self.c.get_camera_from_index(0))
        print(self.c.get_camera_info())
        m, f = self.c.get_video_mode_and_frame_rate()
        print(m, f)
        print(self.c.get_property_info(fc2.FRAME_RATE))
        p = self.c.get_property(fc2.FRAME_RATE)
        print(p)
        self.c.set_property(**p)
        self.c.start_capture()


class App(QWidget):
    def __init__(self):
            super(App,self).__init__()
            self.title = 'PyQt4 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(800, 600)
            # create a label
            self.label = QLabel(self)
            self.label.move(0, 0)
            self.label.resize(640, 480)
            th = Thread(self)
            th.changePixmap.connect(lambda p: self.setPixMap(p))
            th.start()

    def setPixMap(self, p):     
        p = QPixmap.fromImage(p)    
        p = p.scaled(640, 480, Qt.KeepAspectRatio)
        self.label.setPixmap(p)


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

【讨论】:

以上是关于PyQt 显示来自 opencv 的视频流的主要内容,如果未能解决你的问题,请参考以下文章

PyQt 显示来自 opencv 的视频流

PyQt 显示来自 opencv 的视频流

Python-PyQt5+OpenCV获取数据流(视频流)

OpenCV-PyQT项目实战项目案例04:视频播放

OpenCV-PyQT项目实战项目案例04:视频播放

Python-pyqt5+opencv视频播放器,上传本地视频