【问题标题】:How to read frames using PyQt and OpenCV如何使用 PyQt 和 OpenCV 读取帧
【发布时间】:2020-05-07 15:14:28
【问题描述】:

我一直在尝试使用 OpenCV 和 PyQt 从网络摄像头读取帧。我知道有很多例子。但我想使用 MVC(模型-视图-控制器)。

OpenCV 处理控制器,我为模型创建了 models.py,为 GUI 创建了 views.py。

在运行代码之前我没有看到任何错误,当我运行代码时 GUI 会打开,然后我按下打开网络摄像头,我看到了这个警告和错误:

[ WARN:0] global C:\projects\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (674) SourceReaderCB::~SourceReaderCB terminating async callback

Process finished with exit code 1

这里是views.py

from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QLabel, QWidget, QPushButton, QVBoxLayout, QApplication, QHBoxLayout, QMessageBox
from models import Camera

class UI_Window(QWidget):

    def __init__(self, camera = None):
        super().__init__()
        self.camera = camera

        # Create a timer.
        self.timer = QTimer()
        self.timer.timeout.connect(Camera.nextFrameSlot)

        # Create a layout.
        layout = QVBoxLayout()

        # Add a button
        button_layout = QHBoxLayout()

        btnCamera = QPushButton("Open camera")
        btnCamera.clicked.connect(Camera.openCamera)
        button_layout.addWidget(btnCamera)
        layout.addLayout(button_layout)

        # Add a label
        self.label = QLabel()
        self.label.setFixedSize(640, 640)

        layout.addWidget(self.label)

        # Set the layout
        self.setLayout(layout)
        self.setWindowTitle("First GUI with QT")
        self.setFixedSize(800, 800)

    # https://stackoverflow.com/questions/1414781/prompt-on-exit-in-pyqt-application

class MovieThread(QThread):
    def __init__(self, camera):
        super().__init__()
        self.camera = camera

    def run(self):
        self.camera.acquire_movie(200)

if __name__ == '__main__':
    app = QApplication([])
    window = UI_Window()
    window.show()

models.py

import cv2
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtGui import QPixmap, QImage

class Camera:

    def __init__(self, camera):
        self.camera = camera
        self.cap = None

    def openCamera(self):
        self.vc = cv2.VideoCapture(0)
        # vc.set(5, 30)  #set FPS
        self.vc.set(3, 640)  # set width
        self.vc.set(4, 480)  # set height

        if not self.vc.isOpened():
            msgBox = QMessageBox()
            msgBox.setText("Failed to open camera.")
            msgBox.exec_()
            return

        self.timer.start(1000. / 24)

    # https://stackoverflow.com/questions/41103148/capture-webcam-video-using-pyqt
    def nextFrameSlot(self):
        rval, frame = self.vc.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(image)
        self.label.setPixmap(pixmap)

    def initialize(self):
        self.cap = cv2.VideoCapture(self.camera)

start.py

from PyQt5.QtWidgets import QApplication

from models import Camera
from views import UI_Window

camera = Camera(0)
camera.initialize()

app = QApplication([])
start_window = UI_Window(camera)
start_window.show()
app.exit(app.exec_())

【问题讨论】:

  • 您是否在 Google 中搜索过cap_msmf.cpp (674) SourceReaderCB::~SourceReaderCB terminating async callback?你发现了什么?
  • 一开始你使用initialize(),它使用cv2.VideoCapture(self.camera),但你还有一个运行Camera.openCamera的按钮,它使用self.vc = cv2.VideoCapture(0)——所以你创建了两次访问,但你没有释放第一次访问。
  • yes 。但它没有用(回答你的第一条评论)
  • 这是完整的错误信息吗?你没有更多的错误行吗?也许还有其他有用的信息 - 即。哪一行有问题。
  • 不幸的是它是完整的消息

标签: python opencv model-view-controller pyqt pyqt5


【解决方案1】:

此代码对我有用。为了测试,我将所有内容放在一个文件中。

我删除了

camera.initialize()

我将 nextFrameSlotCamera 移动到 UI_Window

我还在UI_Windows 中创建了start() 以将self.timer.start()Camera 移动到UI_Window


import cv2
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtGui import QPixmap, QImage

class Camera:

    def __init__(self, camera):
        self.camera = camera
        self.cap = None

    def openCamera(self):
        self.vc = cv2.VideoCapture(0)
        # vc.set(5, 30)  #set FPS
        self.vc.set(3, 640)  # set width
        self.vc.set(4, 480)  # set height

        if not self.vc.isOpened():
            print('failure')
            msgBox = QMessageBox()
            msgBox.setText("Failed to open camera.")
            msgBox.exec_()
            return

    # https://stackoverflow.com/questions/41103148/capture-webcam-video-using-pyqt
    def initialize(self):
        self.cap = cv2.VideoCapture(self.camera)



from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QLabel, QWidget, QPushButton, QVBoxLayout, QApplication, QHBoxLayout, QMessageBox
#from models import Camera

class UI_Window(QWidget):

    def __init__(self, camera = None):
        super().__init__()
        self.camera = camera
        print('UI')
        # Create a timer.
        self.timer = QTimer()
        self.timer.timeout.connect(self.nextFrameSlot)

        # Create a layout.
        layout = QVBoxLayout()

        # Add a button
        button_layout = QHBoxLayout()

        btnCamera = QPushButton("Open camera")
        btnCamera.clicked.connect(self.start)
        button_layout.addWidget(btnCamera)
        layout.addLayout(button_layout)

        # Add a label
        self.label = QLabel()
        self.label.setFixedSize(640, 640)

        layout.addWidget(self.label)

        # Set the layout
        self.setLayout(layout)
        self.setWindowTitle("First GUI with QT")
        self.setFixedSize(800, 800)

    def start(self):
        camera.openCamera()
        self.timer.start(1000. / 24)

    def nextFrameSlot(self):
        rval, frame = camera.vc.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(image)
        self.label.setPixmap(pixmap)


    # https://stackoverflow.com/questions/1414781/prompt-on-exit-in-pyqt-application

class MovieThread(QThread):
    def __init__(self, camera):
        super().__init__()
        self.camera = camera

    def run(self):
        self.camera.acquire_movie(200)

#if __name__ == '__main__':
#    app = QApplication([])
#    window = UI_Window()
#    window.show()

#start.py

from PyQt5.QtWidgets import QApplication

#from models import Camera
#from views import UI_Window

camera = Camera(0)
#camera.initialize()
print('test')
app = QApplication([])
start_window = UI_Window(camera)
start_window.show()
app.exit(app.exec_())

编辑: 代码在单独的文件中测试。

我还添加了read_gray()和负色read(negative=True)read_gray(negative=True)

顺便说一句: 我在很多地方检查frame 是否不为空 - 但不能使用if not frameif frame is not None 因为framenumpy.array 和@987654339 @ 可能会给出错误的结果。

我将所有 GUI 小部件从 model 移至 view

models.py

import cv2

class Camera:

    def __init__(self, camera):
        self.camera = camera
        self.vp = None

    def open(self, width=640, height=480, fps=30):
        self.vc = cv2.VideoCapture(self.camera)

        self.width = width
        self.height = height
        self.fps = fps
        # vc.set(5, fps)  #set FPS
        self.vc.set(3, width)   # set width
        self.vc.set(4, height)  # set height

        return self.vc.isOpened()

    def read(self, negative=False):
        rval, frame = self.vc.read()
        if frame is not None:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) 
            if negative:
                frame = cv2.bitwise_not(frame)
            return frame

    def read_gray(self, negative=False):            
        rval, frame = self.vc.read()
        if frame is not None:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB) 
            if negative:
                frame = cv2.bitwise_not(frame)
            return frame

views.py

from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QLabel, QWidget, QPushButton, QVBoxLayout, QApplication, QHBoxLayout, QMessageBox
from PyQt5.QtGui import QPixmap, QImage
from models import Camera

class UI_Window(QWidget):

    def __init__(self, camera = None):
        super().__init__()
        self.camera = camera
        print('UI')
        # Create a timer.
        self.timer = QTimer()
        self.timer.timeout.connect(self.nextFrameSlot)

        # Create a layout.
        layout = QVBoxLayout()

        # Add a button
        button_layout = QHBoxLayout()

        btnCamera = QPushButton("Open camera")
        btnCamera.clicked.connect(self.start)
        button_layout.addWidget(btnCamera)
        layout.addLayout(button_layout)

        # Add a label
        self.label = QLabel()
        self.label.setFixedSize(640, 640)

        layout.addWidget(self.label)

        # Set the layout
        self.setLayout(layout)
        self.setWindowTitle("First GUI with QT")
        #self.setFixedSize(800, 800)

    def start(self):
        if not self.camera.open():
            print('failure')
            msgBox = QMessageBox()
            msgBox.setText("Failed to open camera.")
            msgBox.exec_()
            return

        self.timer.start(1000. / 24)

    def nextFrameSlot(self):
        frame = self.camera.read()
        #frame = self.camera.read_gray()
        if frame is not None:
            image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
            pixmap = QPixmap.fromImage(image)
            self.label.setPixmap(pixmap)


class MovieThread(QThread):
    def __init__(self, camera):
        super().__init__()
        self.camera = camera

    def run(self):
        self.camera.acquire_movie(200)

if __name__ == '__main__':
    app = QApplication([])
    window = UI_Window()
    window.show()

ma​​in.py

from PyQt5.QtWidgets import QApplication
from views import UI_Window
from models import Camera

if __name__ == '__main__':

    camera = Camera(0)

    app = QApplication([])
    start_window = UI_Window(camera)
    start_window.show()
    app.exit(app.exec_())

【讨论】:

  • 它在一个文件中工作。但是当我将它拆分为 3 个文件时。我在views.py 中看到错误。它说“未解决对相机的引用”。 nextFrameSlotstart 中的 cameras 未划线
  • 您必须使用self. 才能访问self.camera
  • 我添加了在单独文件中测试的代码。我还添加了read_gray() 并将颜色转换为负值。在相机/模型中,我还会添加其他过滤器和图像的修改 - 即。翻转、识别轮廓和其他 cv2 的功能
【解决方案2】:

如果您只是在寻找问题的准系统答案,这里是@furas 答案的简洁替代方案:

“如何使用 PyQt 和 OpenCV 读取帧?”

import sys
from PyQt5.QtCore import Qt, QSize, QTimer, QThread
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QLabel
from PyQt5.QtGui import QPixmap, QImage
import cv2

def main():    
    app = QApplication([])
    
    window = QWidget()
    window.setLayout(QGridLayout(window))    
    window.setMinimumSize(QSize(640, 480))

    label = QLabel()
    label.setFixedSize(640, 640)    
    window.layout().addWidget(label, 0, 0)

    window.show()

    vc = cv2.VideoCapture(0)
    vc.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    vc.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    timer = QTimer()
    timer.timeout.connect(lambda: nextFrameSlot(vc, label))
    timer.start(1000. / 24)

    return app.exit(app.exec_())

def nextFrameSlot(vc: cv2.VideoCapture, label: QLabel):
    rval, frame = vc.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    image = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
    pixmap = QPixmap.fromImage(image)
    label.setPixmap(pixmap)
    
if __name__ == '__main__':
    exit_code = main()
    sys.exit(exit_code)

Python 3.7.9测试

requirements.txt:

numpy==1.19.4
opencv-python==4.4.0.46
PyQt5==5.15.1
PyQt5-sip==12.8.1

【讨论】:

    猜你喜欢
    • 2019-04-25
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2016-02-12
    • 2013-11-08
    • 2021-04-25
    相关资源
    最近更新 更多