简体   繁体   English

将网络摄像头素材从OpenCV获取到PyQt

[英]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. 我正在尝试使用opencv从摄像机获取网络摄像机数据,然后在PyQt gui中显示该数据。 I have done this before with Tkinter by gaining access to Tkinter main window loop with the .after function. 在Tkinter之前,我已经通过使用.after函数访问Tkinter主窗口循环来做到这一点。 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. 但是,PyQt似乎没有相同的可用性,并且要使另一个循环与应用程序一起运行,您需要使用单独的线程。 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). (显然,我不想要一个带有一会儿True循环的线程,但是我只是为了方便起见使用它,并计划在可以保证这个想法可行的情况下替换它)。 However, the signal doesn't appear to be registering because the draw() function is never called. 但是,信号似乎没有被注册,因为从来没有调用过draw()函数。 Any idea what i'm doing wrong? 知道我在做什么错吗?

I don't know anything about OpenCV, so I can only guess at the problems. 我对OpenCV一无所知,所以我只能猜测问题所在。

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. 我修改了您的代码,并在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. 这里的关键点是必须将cv2相机对象放入WorkThread ,在run()方法中读取主while循环中的每一帧,最后将图像发送给QWidget对象以显示它。 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_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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