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
"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.