繁体   English   中英

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

[英]Getting Webcam Footage from OpenCV to PyQt

我正在尝试使用opencv从摄像机获取网络摄像机数据,然后在PyQt gui中显示该数据。 在Tkinter之前,我已经通过使用.after函数访问Tkinter主窗口循环来做到这一点。 但是,PyQt似乎没有相同的可用性,并且要使另一个循环与应用程序一起运行,您需要使用单独的线程。 所以这是我想出的:

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

我的想法很简单:我将创建一个带有循环的线程,该线程发出一个信号,通知主应用程序进行更新。 (显然,我不想要一个带有一会儿True循环的线程,但是我只是为了方便起见使用它,并计划在可以保证这个想法可行的情况下替换它)。 但是,信号似乎没有被注册,因为从来没有调用过draw()函数。 知道我在做什么错吗?

我对OpenCV一无所知,所以我只能猜测问题所在。

我的猜测是您只读取一次视频数据。 如果是视频流,则必须不断读取和解释数据。

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

由于您每秒仅更新多次,因此您可能为此使用计时器而不是线程。 计时器可能更容易使用,也更安全。

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

我一直在研究与您的问题非常相似的事物。 我修改了您的代码,并在Windows PC上对其进行了测试。

这里的关键点是必须将cv2相机对象放入WorkThread ,在run()方法中读取主while循环中的每一帧,最后将图像发送给QWidget对象以显示它。 通过这种方式,您可以获得图像捕获和显示的连续迭代。

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