You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PyQt6+摄像头+pyqtgraph多进程遇pickle错误,求解决方案

问题描述

正尝试用PyQt6开发GUI,实现摄像头与pyqtgraph Viewer的交互。创建了三个类:QT界面类(CharacViewer)、摄像头类(cameraClass)、pyqtgraph Viewer类(imageViewer),希望将摄像头和Viewer作为独立进程运行:摄像头采集帧后通过队列发送给Viewer进程显示。但启动进程时遇到pickle错误,无法序列化cv2.VideoCapture对象。不想使用Pathos的Pool,仅需两个独立进程,求解决方法。

错误信息

TypeError: cannot pickle 'cv2.VideoCapture' object

问题原因

  1. 摄像头对象序列化问题:当前代码在主进程中调用charac_viewer.initialiseCamera(),会在主进程内创建cv2.VideoCapture实例。启动子进程时,multiprocessing需要序列化整个camera对象,而cv2.VideoCapture是C语言实现的资源句柄,无法被Python的pickle机制序列化。
  2. GUI控件跨进程问题:主进程中调用charac_viewer.initialiseViewer()创建了pyqtgraph的ImageView,GUI控件只能在所属的Qt事件循环进程中运行,跨进程操作会导致异常,且ImageView对象同样无法被序列化。

解决方案

核心思路:所有进程专属的资源(摄像头、GUI控件)都在各自的子进程内部初始化,避免在主进程创建后跨进程传递。

修改cameraClass.py

将摄像头的初始化逻辑移到子进程的run()方法中,同时把宽高参数通过类初始化传入:

import cv2
import multiprocessing
import numpy as np

class camera(multiprocessing.Process):
    def __init__(self, queue, width, height):
        multiprocessing.Process.__init__(self)
        self.queue = queue
        self.width = width
        self.height = height
        print('Camera class initialized!')

    def run(self):
        # 在子进程内部初始化摄像头
        self.vid = cv2.VideoCapture(0)
        self.vid.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
        self.vid.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
        print('Camera initialized!')
        
        while True:
            # 限制队列最大缓存5帧,避免内存占用过高
            if self.queue.qsize() < 5:
                ret, frame = self.vid.read()
                if ret:
                    arr = np.array(frame)
                    # 打包帧形状与扁平化数据
                    self.queue.put((arr.shape[0], arr.shape[1], arr.shape[2], arr.flatten()))
        # 退出时释放摄像头资源
        self.vid.release()

修改imageViewer.py

在子进程内部初始化ImageView,并启动独立的Qt事件循环(子进程无主进程的QApplication支持):

import multiprocessing
import pyqtgraph as pg
import numpy as np
from PyQt6.QtWidgets import QApplication

class imageViewer(multiprocessing.Process):
    def __init__(self, queue):
        multiprocessing.Process.__init__(self)
        self.queue = queue
        self.counter = 0
        print('Image Viewer Class initialized!')

    def run(self):
        # 子进程必须创建独立的QApplication
        app = QApplication([])
        # 在子进程内部初始化Viewer
        self.imv = pg.ImageView()
        self.imv.show()
        self.imv.resize(800, 600)
        print('Image Viewer initialized!')

        # 使用QTimer轮询队列,避免死循环阻塞事件循环
        timer = pg.QtCore.QTimer()
        def update_frame():
            while not self.queue.empty():
                width, height, colors, data_flat = self.queue.get()
                frame = np.reshape(data_flat, (width, height, colors))
                self.imv.setImage(frame)
        timer.timeout.connect(update_frame)
        timer.start(30)  # 每30ms更新一次,对应约30fps

        # 启动子进程的Qt事件循环
        app.exec()

修改CharacViewer.py

移除主进程中对摄像头和Viewer的初始化调用,仅保留进程启动逻辑:

from PyQt6 import QtWidgets, uic

class CharacViewer(QtWidgets.QMainWindow):
    def __init__(self):
        super(CharacViewer, self).__init__()
        self.ui = uic.loadUi("gui_interface.ui", self)
        self.ui.pushButton_RunCamera.clicked.connect(self.startImagingProcess)
        self.width = 720
        self.height = 1280
        self.counter = 0

    def setImageViewer(self, imv):
        self.imv = imv

    def setCamera(self, cam):
        self.cam = cam

    def startImagingProcess(self):
        print('Start Imaging...')
        self.cam.start()
        self.imv.start()

修改main.py

调整摄像头实例的创建,传入宽高参数,不再调用主进程内的初始化方法:

import sys
from PyQt6.QtWidgets import QApplication
from CharacViewer import CharacViewer
from cameraClass import camera
from imageViewer import imageViewer
import multiprocessing as mp

if __name__ == '__main__':
    app = QApplication([])
    app.setStyle('Fusion')
    queue = mp.Queue()

    # 创建摄像头实例时传入宽高参数
    camera_proc = camera(queue, 720, 1280)
    imageViewer_proc = imageViewer(queue)
    charac_viewer = CharacViewer()
    charac_viewer.move(900,0)
    charac_viewer.resize(650,830)
    charac_viewer.show()
    charac_viewer.setImageViewer(imageViewer_proc)
    charac_viewer.setCamera(camera_proc)

    print('All set!')
    sys.exit(app.exec())

关键说明

  1. 摄像头初始化移到子进程:确保cv2.VideoCapture在子进程内创建,避免跨进程序列化资源句柄。
  2. Viewer进程独立事件循环:pyqtgraph依赖Qt事件循环,子进程必须创建自己的QApplication并启动循环,否则GUI控件无法正常显示。
  3. 队列数据优化:将帧形状与数据分开传递,同时限制队列缓存数量,避免内存占用过高。

内容的提问来源于stack exchange,提问作者Paul J. M.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:04:51