简体   繁体   中英

Getting Webcam Footage from OpenCV to PyQt

I'm attempting to get webcam data from a camera using opencv and then display that in a PyQt gui. I have done this before with Tkinter by gaining access to Tkinter main window loop with the .after function. However, PyQt doesn't seem to have the same usability and in order to have another loop running with an application you need to use a separate thread. So this is what I have come up with:

import sys
import cv2
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtGui import QImage
import time

class VideoCapture(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget().__init__()
        self.camera = None
        self.camera = cv2.VideoCapture(0)
        b, self.frame = self.camera.read()
        self.label = QtGui.QLabel()
        self.workThread = WorkThread(self)
        self.connect(self.workThread, QtCore.SIGNAL('update_Camera'), self.draw)
        self.workThread.start()

    def closeEvent(self, event):
        self.workThread.stop()

    def draw(self):
        print "I should Redraw"
        height, width, channel = self.frame.shape
        bpl = 3 * width
        self.qImg = QImage(self.frame.data, width, height, bpl, QImage.Format_RGB888)
        pix = QtGui.QPixmap(self.qImg)
        self.label.setPixmap(pix)
        self.label.show()



class WorkThread(QtCore.QThread):
    def __init__(self, parent):
        QtCore.QThread.__init__(self)
        self.parent = parent

    def __del__(self):
        self.wait()

    def run(self):
        while True:
            self.emit(QtCore.SIGNAL('update_Camera'), "_")
        self.terminate()


app = QtGui.QApplication(sys.argv)
test = VideoCapture()
test.draw()

sys.exit(app.exec_())

My idea was simple: I'll just create a thread with a loop which emits a signal telling the main application to update. (Obviously I don't I want a thread with a while True loop but I just used it for convenience and planned on replacing it once I could guarantee this idea would work). However, the signal doesn't appear to be registering because the draw() function is never called. Any idea what i'm doing wrong?

I don't know anything about OpenCV, so I can only guess at the problems.

My guess is that you are only reading the video data once. If it is a video stream then you have to continually read and interpret the data.

import sys
import cv2
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtGui import QImage
import time

class VideoCapture(QtGui.QWidget):

    update_video = QtCore.pyqtSignal()

    def __init__(self, parent = None):
        QtGui.QWidget().__init__()
        self.camera = cv2.VideoCapture(0)
        self.label = QtGui.QLabel()
        layout = QtGui.QHBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.label)

        # Create the worker Thread
        self.workThread = WorkThread(self.readVideo)
        self.update_video.connect(self.draw)

    def start(self):
        self.workerThread.start()

    def stop(self):
        self.workThread.alive = False
        self.workThread.stop()

    def readVideo(self):
        """Note this method is executed in a thread. No drawing can happen in a thread. Emit a signal to draw items."""
        b, self.frame = self.camera.read()
        self.update_video.emit() # Signals are slow this may happen too fast

    def closeEvent(self, event):
        self.stop()
        return QtGui.QWidget.closeEvent(self, event)
        #self.workThread.alive = False
        #self.workThread.stop()

    def draw(self):
        print "I should Redraw"
        height, width, channel = self.frame.shape
        bpl = 3 * width
        qImg = QImage(self.frame.data, width, height, bpl, QImage.Format_RGB888)
        pix = QtGui.QPixmap(qImg)
        self.label.setPixmap(pix)
        # self.label.show() # The label is now a part of the widget layout



class WorkThread(QtCore.QThread):
    def __init__(self, target=None, args=(), kwargs={}):
        QtCore.QThread.__init__(self)
        # I don't know how Qt's threads work, so I am treating it like a python thread
        self.target = target
        self.args = args
        self.kwargs = kwargs
        self.alive = True

    def run(self):
        while self.alive:
            self.target(*self.args, **self.kwargs)


app = QtGui.QApplication(sys.argv)
test = VideoCapture()
test.start()

sys.exit(app.exec_())

Since you are only updating so many times per second you could probably use a timer for this instead of a thread. The timer is probably easier and safer to use.

import sys
import cv2
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtGui import QImage
import time

class VideoCapture(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget().__init__()
        self.camera = cv2.VideoCapture(0)
        self.label = QtGui.QLabel()
        layout = QtGui.QHBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.label)

        # Create the worker Thread
        self.timer= QtCore.QTimer()
        self.timer.setInterval(300)
        self.timer.timeout.connect(self.draw_camera)

    def start(self):
        self.timer.start()

    def stop(self):
        self.timer.stop()

    def draw_camera(self):
        """You can draw in a timer, so just read the data and draw however fast you want."""
        print "I should Redraw"
        b, frame = self.camera.read()
        height, width, channel = frame.shape
        bpl = 3 * width
        qImg = QImage(frame.data, width, height, bpl, QImage.Format_RGB888)
        pix = QtGui.QPixmap(qImg)
        self.label.setPixmap(pix)

    def closeEvent(self, event):
        self.stop()
        return QtGui.QWidget.closeEvent(self, event)

app = QtGui.QApplication(sys.argv)
test = VideoCapture()
test.start()

sys.exit(app.exec_())

I've been working on things very similar to your problem. I modified your code and tested it on my Windows PC.

The key point here is that you have to put the cv2 camera object in the WorkThread , read each frame in the main while loop in the run() method, and finally emit the image to the QWidget object to display it. In this way you get a continuous iteration of image capturing and display.

import sys
import cv2
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtGui import QImage
import time

class VideoCapture(QtGui.QWidget):
    def __init__(self, parent = None):
        # Use super() to call __init__() methods in the parent classes
        super(VideoCapture, self).__init__()

        # The instantiated QLabel object should belong to the 'self' QWidget object
        self.label = QtGui.QLabel(self) # <- So put 'self' in the parenthesis

        # Set the QLabel geometry to fit the image dimension (640, 480)
        # The top left corner (0, 0) is the position within the QWidget main window
        self.label.setGeometry(0,0,640,480)

        # Instantiate a QThread object. No need to pass in the parent QWidget object.
        self.workThread = WorkThread()

        # Connect signal from self.workThread to the slot self.draw
        self.connect(self.workThread, QtCore.SIGNAL('update_Camera'), self.draw)

        self.workThread.start()

    def closeEvent(self, event):
        self.workThread.stop()
        event.accept()

    def draw(self, img):
        print "I should Redraw"
        height, width, channel = img.shape
        bpl = 3 * width
        self.qImg = QImage(img, width, height, bpl, QImage.Format_RGB888)
        pix = QtGui.QPixmap(self.qImg)
        self.label.setPixmap(pix)
        self.label.show()


class WorkThread(QtCore.QThread):
    def __init__(self):
        # Use super() to call __init__() methods in the parent classes
        super(WorkThread, self).__init__()

        # Place the camera object in the WorkThread
        self.camera = cv2.VideoCapture(0)

        # The boolean variable to break the while loop in self.run() method
        self.running = True

    def run(self):
        while self.running:

            # Read one frame
            b, self.frame = self.camera.read()

            # Emit self.frame to the QWidget object
            self.emit(QtCore.SIGNAL('update_Camera'), self.frame)

    def stop(self):
        # Terminate the while loop in self.run() method
        self.running = False


app = QtGui.QApplication(sys.argv)
video_capture_widget = VideoCapture()
video_capture_widget.show()
sys.exit(app.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM