簡體   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