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[1], cvRGBImg.shape[0], 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.

    Also, subclassing 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 QThread() when ready.

    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[1], image.shape[0], 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 picamera.

    Hope this helps :o)



  • @jazzycamel said in Picamera significant delay and low FPS, but low CPU and memory usage:

    Thanks this worked i also changed the camera parameters so it runs even faster.

    self._camera=PiCamera(sensor_mode=4,resolution='640x480',framerate=40).
    

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

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.