0

I am trying to produce a GUI using PyQT6 to interface a camera with a pyqtgraph viewer. I created three classes, one for the QT interface (CharacViewer), one for the camera (cameraClass) and one for the pyqtgraph viewer (imageViewer). I want the camera and viewer to work as processes: once the camera has acquired a frame, it will send it to the pyqtgraph process through a queue. The pyqtgraph process will extract the frame from the queue and display it. I need to do it this way because I want to interact with the pyqtgraph window later during the acquisition. However, I am encountering a pickling issue because the process function can't pickle the camera and pyqtgraph objects. Does anybody have experience with this? Pathos seems to solve a similar issue, but I don't want to create a Pool, rather two distinct Processes.

Here are the different codes I have written so far:

main.py:

import sys
from PyQt6 import uic
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 = camera(queue)
    imageViewer = imageViewer(queue)
    charac_viewer = CharacViewer()
    charac_viewer.move(900,0)
    charac_viewer.resize(650,830)
    charac_viewer.show()
    charac_viewer.setImageViewer(imageViewer)
    charac_viewer.setCamera(camera)
    charac_viewer.initialiseCamera()
    charac_viewer.initialiseViewer()

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

    print('Killing GUI')

CharacViewer.py:

import cv2
from PyQt6 import QtWidgets, uic, QtCore
from PyQt6.QtWidgets import QApplication, QFileDialog
import threading

import queue
from multiprocessing import Process, Queue
from multiprocessing.shared_memory import SharedMemory
import numpy as np
import pyqtgraph as pg
from PyQt6.QtCore import QTimer, QSize

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

    def initialiseCamera(self):
        self.cam.initialiseCamera(self.width, self.height)

    def initialiseViewer(self):
        self.imv.initialiseViewer()

cameraClass.py:

import cv2
import multiprocessing
import matplotlib.pyplot as plt
import numpy as np
import queue


class camera(multiprocessing.Process):
    # def __init__(self, sync_queue, param, conn):
    def __init__(self, queue):
        multiprocessing.Process.__init__(self)
        self.queue = queue
        print('Camera class initialized!')

    def initialiseCamera(self, width, height):
        self.width = width
        self.height = height
        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!')
    def run(self):
        while 1:
            if self.queue.empty():
                ret, frame = self.vid.read()
                arr = np.array(frame)
                print(np.shape(arr))
                width = np.shape(arr)[0]
                height = np.shape(arr)[1]
                colors = np.shape(arr)[2]
                arr = arr.flatten()
                arr = np.append(arr, np.array([width, height, colors]))
                self.queue.put(arr)

imageViewer.py:

import cv2
import multiprocessing
import matplotlib.pyplot as plt
import pyqtgraph as pg
import numpy as np
import queue


class imageViewer(multiprocessing.Process):

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

    def initialiseViewer(self):
        self.imv = pg.ImageView()
        self.imv.show()
        self.imv.resize(800, 600)
        print('Image Viewer initialized!')

    def run(self):
        while 1:
            if not self.queue.empty():
                data = self.queue.get()
                dataToPrint = data[:-3]
                self.width = data[-3]
                self.height = data[-2]
                self.colors = data[-1]
                dataToPrint = np.reshape(dataToPrint, (self.width, self.height, self.colors))
                self.imv.setImage(dataToPrint)

The error I get is the following:

TypeError: cannot pickle 'cv2.VideoCapture' object

4
  • Multi-processing is a poor fit for this kind of application. Use multi-threading instead. If you search SO (and elsewhere), you will easily find many examples that successfully use the latter approach. Commented Feb 27, 2024 at 21:04
  • Could you elaborate on why multi-processing is a poor fit for this application? Using multi-threading wouldn't be ideal in my opinion, as I want a constant stream of data flowing from the camera to the displayer. Using multi-threading, it would not be simultaneous and would be blocked if I interact with the GUI. Am I wrong? Commented Feb 29, 2024 at 8:28
  • @PaulJ.M. it wouldn't be simultaneous even when using multi processing. You're assuming that moving the "worker" on another process would magically make things instantly faster, but since you'll eventually need to communicate with the GUI in order to display the output, that won't change anything, since the pyqtgraph processing must be in the same thread of the GUI. Commented Feb 29, 2024 at 11:42
  • 2
    @PaulJ.M. No, it would not be blocked. Just search for "cv2" "PyQt" "multithreading" as I suggested earlier and you will find plenty of working examples. As musicamente said, multiprocessing isn't a magic bullet in this scenario. It's only ever useful (as a last resort) with uninterruptible tasks that cannot provide periodic updates. But that is clearly not the case here, since video capture naturally decomposes into a series of independent frames. Qt's signal-slot mechanism provides an efficient, thread-safe way to send a stream of data to the main thread without blocking the GUI. Commented Feb 29, 2024 at 14:12

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.