Python QT5 - 多进程 OpenCV 网络摄像头和 Requests.Get

Posted

技术标签:

【中文标题】Python QT5 - 多进程 OpenCV 网络摄像头和 Requests.Get【英文标题】:Python QT5 - Multiprocess OpenCV Webcam & Requests.Get 【发布时间】:2020-06-09 12:46:00 【问题描述】:

我是多处理的新手,我正在尝试在 PyQT5 中同时查看我的网络摄像头和发出 requests.get。 当前的结果是每次发出 get 请求时视频都会卡顿 - 这似乎没什么大不了的,但重要的是每秒都会发出流畅的视频。对于更复杂的 Post 请求(响应时间长达 5 秒),视频看起来基本冻结。

所以预期的结果是我有一个黄油般流畅的视频,而每秒钟都在并行发出一个获取请求。

我知道这与我按照 link 调用进程的方式有关:

p1 = Process(target=self.start_webcam())
p2 = Process(target=self.start_web_req())

应该是:

p1 = Process(target=self.start_webcam)
p2 = Process(target=self.start_web_req)

但我收到此错误:

TypeError: cannot pickle 'Ui_MainWindow' object

这是我下面的代码。

您有什么想法、建议和答案来确保视频流畅,同时提出获取请求?

谢谢你!

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget,QTableWidgetItem, QDialog
import pandas as pd
from PyQt5.QtCore import QTimer, QSize

from PyQt5.QtGui import QImage, QPixmap
from PyQt5.uic import loadUi

import PIL
from PIL import Image
import requests
import sys
import cv2
import numpy as np

class Ui_MainWindow(QWidget):        
    def setupUi(self, MainWindow):
        MainWindow.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        MainWindow.setGeometry(0, 30, 800, 480) # x,y,w,h
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        # Camera Label
        self.imgLabel = QtWidgets.QLabel(self.centralwidget)
        self.imgLabel.setGeometry(QtCore.QRect(0, 0, 600, 480))
        self.imgLabel.setFrameShape(QtWidgets.QFrame.Box)
        self.imgLabel.setObjectName("imgLabel")

        MainWindow.setCentralWidget(self.centralwidget)

        # Initiate start_webcam
        p1 = Process(target=self.start_webcam())
        p1.start()

        # Initiate start_web_req
        p2 = Process(target=self.start_web_req())
        p2.start()

    def start_webcam(self):
        self.capture=cv2.VideoCapture(0)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH,600)
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT,480)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_frame)
        self.timer.start(5)

    def update_frame(self):
        ret,frame=self.capture.read()
        self.cv2_im=frame
        self.pil_im = Image.fromarray(self.cv2_im)
        self.processedImage=self.cv2_im
        self.display_image(1)

    def display_image(self, window=1):
        qformat = QImage.Format_Indexed8
        if len(self.processedImage.shape) == 3:  # rows[0],cols[1],channels[2]
            if (self.processedImage.shape[2]) == 4:
                qformat = QImage.Format_RGBA8888
            else:
                qformat = QImage.Format_RGB888
        img = QImage(self.processedImage, self.processedImage.shape[1], self.processedImage.shape[0],
                     self.processedImage.strides[0], qformat)
        # BGR > RGB
        img = img.rgbSwapped()
        if window == 1:
            self.imgLabel.setPixmap(QPixmap.fromImage(img))
            self.imgLabel.setScaledContents(True)
        if window == 2:
            self.processedLabel.setPixmap(QPixmap.fromImage(img))
            self.processedLabel.setScaledContents(True)


    def start_web_req(self):
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.web_req)
        self.timer.start(1000)        

    def web_req(self):
        res = requests.get('https://www.google.com.au')
        print(res)

    def appExec(self):
        app.exec_()
        self.timer.stop()
        self.capture.release()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    app = QApplication.instance()
    if app is None:
        app = QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(ui.appExec())

【问题讨论】:

1) “进程”要求类是pickleable,但小部件不是。 2)我没有看到在另一个进程中执行相机的启动或停止会提高性能。 @eyllanesc,伙计,非常感谢您提供的大量信息!以新鲜的眼光醒来并听取了您的建议 - 我将“开始”和“停止”函数合并在一起,这帮助我意识到使用 QTimer 调用函数不是做事的合适方式,因此我将其更新为线程。计时器,它工作得很好。 【参考方案1】:

在eyllanesc的帮助下,我解决了。

问题出在 start_webcam() 和 start_web_req() - 我有 QTimers 分别调用 update_frame() 和 web_req()。我删除了那些启动函数并将 threading.Timer 直接放在 update_frame() 和 web_req() 中,这解决了我的视频断断续续的问题。

完整代码如下。

from multiprocessing import Process
import threading

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget,QTableWidgetItem, QDialog
import pandas as pd
from PyQt5.QtCore import QTimer, QSize

from PyQt5.QtGui import QImage, QPixmap
from PyQt5.uic import loadUi

import PIL
from PIL import Image
import requests
import sys
import cv2
import numpy as np

class Ui_MainWindow(QWidget):        
    def setupUi(self, MainWindow):
        MainWindow.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        MainWindow.setGeometry(0, 30, 800, 480) # x,y,w,h
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        # Camera Label
        self.imgLabel = QtWidgets.QLabel(self.centralwidget)
        self.imgLabel.setGeometry(QtCore.QRect(0, 0, 600, 480))
        self.imgLabel.setFrameShape(QtWidgets.QFrame.Box)
        self.imgLabel.setObjectName("imgLabel")

        MainWindow.setCentralWidget(self.centralwidget)

        self.capture=cv2.VideoCapture(1)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH,600)
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT,480)

        # Initiate start_webcam
        p1 = Process(target=self.update_frame())
        p1.start()

        # Initiate start_web_req
        p2 = Process(target=self.web_req())
        p2.start()

    def update_frame(self):
        self.t1 = threading.Timer(0.01, self.update_frame).start()
        ret,frame=self.capture.read()
        self.cv2_im=frame
        self.pil_im = Image.fromarray(self.cv2_im)
        self.processedImage=self.cv2_im
        self.display_image(1)

    def display_image(self, window=1):
        qformat = QImage.Format_Indexed8
        if len(self.processedImage.shape) == 3:  # rows[0],cols[1],channels[2]
            if (self.processedImage.shape[2]) == 4:
                qformat = QImage.Format_RGBA8888
            else:
                qformat = QImage.Format_RGB888
        img = QImage(self.processedImage, self.processedImage.shape[1], self.processedImage.shape[0],
                     self.processedImage.strides[0], qformat)
        # BGR > RGB
        img = img.rgbSwapped()
        if window == 1:
            self.imgLabel.setPixmap(QPixmap.fromImage(img))
            self.imgLabel.setScaledContents(True)
        if window == 2:
            self.processedLabel.setPixmap(QPixmap.fromImage(img))
            self.processedLabel.setScaledContents(True)        

    def web_req(self):
        self.t2 = threading.Timer(1.0, self.web_req).start()
        res = requests.get('https://www.google.com.au')
        print(res)

    def appExec(self):
        app.exec_()
        self.t1 = None
        self.t2 = None
        self.capture.release()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    app = QApplication.instance()
    if app is None:
        app = QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(ui.appExec())

【讨论】:

如果该代码有效,则不需要处理,请删除 # Initiate start_webcam p1 = Process(target=self.update_frame()) p1.start() # Initiate start_web_req p2 = Process(target=self.web_req()) p2.start() 并使用 self.update_frame() self.web_req() 更改它

以上是关于Python QT5 - 多进程 OpenCV 网络摄像头和 Requests.Get的主要内容,如果未能解决你的问题,请参考以下文章

Python爬虫实例多进程下载金庸网小说

OpenCV3.4.0+QT5.10.0配置

机器视觉行业实践技巧

Win7下qt5.3.1+opencv2.4.9编译环境的搭建(好多 Opencv2.4.9源码分析的博客)

如何使用Python和OpenCV进行多处理?

如何在 Qt5 中使用 OpenCV