Picamera significant delay and low FPS, but low CPU and memory usage
I am trying to integrate a camera view inside pyqt GUI, only this is running very slow with a 2-4sec delay and very low fps. So far i have used Qtimer but the speed was terrible, so i tried Qthreading as recommended by some one else but this did not improve at all. Does any one know what i am doing wrong in my code ?
class cameraThread(QThread): def __init__(self, guiobject): QThread.__init__(self) self.camera = PiCamera() self.guiobject = guiobject def __del__(self): self.wait() def run(self): while True: self.camera rawCapture = PiRGBArray(self.camera) self.camera.capture(rawCapture, format="rgb") image = rawCapture.array cvRGBImg = image qimg = QtGui.QImage(cvRGBImg.data, cvRGBImg.shape, cvRGBImg.shape, QtGui.QImage.Format_RGB888) qpm = QtGui.QPixmap.fromImage(qimg) self.guiobject.labelcamera.setPixmap(qpm) class ExampleApp(QtWidgets.QMainWindow, design.Ui_MainWindow): def __init__(self, parent=None): super(ExampleApp, self).__init__(parent) self.setupUi(self) self.button.clicked.connect(self.camerarun) self.threading = getPostsThread(self) def camerarun(self): self.threading.start()
There are a few issues I can identify with your code, the most important one is that you should not be passing a reference to the GUI object into another thread (I'm quite surprised this runs actually!), but as a result of this you may not actually be spawning a thread, which is probably why you are not seeing a performance increase.
QThread()is not the ideal or recommended way to implement threading with Qt, despite the documentation implying this. It's actually better to construct a
QObject()and then move it into
Finally, when communicating between threads (i.e. the GUI thread and the camera thread) you should not be directly referencing objects, but instead indirectly connect them via Qt's Signal/Slot mechanism which is guaranteed to be thread-safe.
The following example (demonstrating all of the above) should do what you want and hopefully be a bit quicker:
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QThread from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import qApp, QWidget, QVBoxLayout, QPushButton, QLabel from picamera import PiCamera, PiRGBArray class Camera(QObject): frameCaptured=pyqtSignal(QPixmap) def __init__(self): super().__init__() self._looping=False self._camera=PiCamera() @pyqtSlot() def loop(self): self._looping=True while self._looping: rawCapture=PiRGBArray(self._camera) self._camera.capture(rawCapture, format="rgb") image=rawCapture.array qImage=QImage(image.data, image.shape, image.shape, QImage.Format_RGB888) pixmap=QPixmap.fromImage(qImage) self.frameCaptured.emit(pixmap) @pyqtSlot() def stop(self): self._looping=False class Gui(QWidget): def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self._thread=QThread() qApp.aboutToQuit.connect(self._thread.quit) self._camera=Camera() self._camera.moveToThread(self._thread) self._thread.start() l=QVBoxLayout(self) l.addWidget(QPushButton("Start", self, clicked=self._camera.loop)) self._cameraLabel=QLabel(self) self._camera.frameCaptured.connect(self._cameraLabel.setPixmap) l.addWidget(self._cameraLabel) if __name__=="__main__": from sys import argv, exit from PyQt5.QtWidgets import QApplication a=QApplication(argv) g=Gui() g.show() exit(a.exec_())
A quick disclaimer: I don't have a Pi with a camera handy, so I can't test that bit, but the threading part definitely works with a stub for
Hope this helps :o)
Thanks this worked i also changed the camera parameters so it runs even faster.
Now the only problem is when i try to capture using the worker thread is not responding to the signal ,this is also happening with the stop function its not responding to a button click or custom emit signal to stop the camera it just keeps running
@pyqtSlot() def snapshot(self): self._looping=False self._camera.capture('image.jpg') self._looping=True stopcamera = pyqtSignal() self._thread = QThread() self.stopcamera.connect(self._camera.stop) self._thread.start() self.CameraSnap.clicked.connect(self._camera.snapshot) self.CameraBack.clicked.connect(self.functionstopcamera) def functionstopcamera(self): self.stopcamera.emit()