【问题标题】:How to capture multiple camera streams with OpenCV?如何使用 OpenCV 捕获多个摄像机流?
【发布时间】:2020-02-23 19:11:32
【问题描述】:

我必须拼接从多 (9) 台摄像机捕获的图像。最初,我尝试以 15 FPS 的速率从 2 个摄像机捕获帧。然后,我连接了 4 个摄像头(我还使用了外部供电的 USB 集线器来提供足够的电力),但我只能看到一个流。

为了测试,我使用了以下脚本:

import numpy as np
import cv2
import imutils

index = 0
arr = []
while True:
    cap = cv2.VideoCapture(index)

    if not cap.read()[0]:
        break
    else:
        arr.append(index)
    cap.release()
    index += 1

video_captures = [cv2.VideoCapture(idx) for idx in arr]

while True:
    # Capture frame-by-frame
    frames = []
    frames_preview = []

    for i in arr:
        # skip webcam capture
        if i == 1: continue
        ret, frame = video_captures[i].read()
        if ret:
            frames.append(frame)
            small = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
            frames_preview.append(small)

    for i, frame in enumerate(frames_preview):
        cv2.imshow('Cam {}'.format(i), frame)


    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything is done, release the capture
for video_capture in video_captures:
    video_capture.release()
cv2.destroyAllWindows()

摄像头数量有限制吗?有谁知道从多个摄像头捕获帧的正确方法是什么?

【问题讨论】:

    标签: python multithreading performance opencv video-streaming


    【解决方案1】:

    要使用 OpenCV 捕获多个流,我建议使用线程,它可以通过将繁重的 I/O 操作减轻到单独的线程来提高性能。由于使用cv2.VideoCapture().read() 访问网络摄像头/IP/RTSP 流是一个阻塞操作,所以我们的主程序一直卡住,直到从摄像头设备读取帧为止。如果你有多个流,这个延迟肯定是可见的。为了解决这个问题,我们可以使用线程生成另一个线程来处理使用双端队列并行检索帧,而不是依赖单个线程按顺序获取帧。线程允许在不影响主程序性能的情况下连续读取帧。使用线程和 OpenCV 捕获单个流的想法来自Python OpenCV multithreading streaming from camera 中的先前答案。

    但如果你想捕获多个流,仅靠 OpenCV 是不够的。您可以将 OpenCV 与 GUI 框架结合使用,将每个图像拼接到一个漂亮的显示器上。我将使用PyQt4 作为框架,qdarkstyle 用于 GUI CSS,imutils 用于 OpenCV 便利功能。


    这是我目前使用的相机 GUI 的一个非常精简的版本,没有占位符图像、凭据管理登录页面和相机切换功能。我保留了自动相机重新连接功能,以防互联网中断或相机连接丢失。如上图所示,我只有 8 个摄像头,但添加另一个摄像头非常简单,不会影响性能。此相机 GUI 目前以大约 ~60 FPS 的速度运行,因此它是实时的。您可以使用 PyQt 布局轻松地重新排列布局,因此请随意修改代码!记得更改直播链接!

    from PyQt4 import QtCore, QtGui
    import qdarkstyle
    from threading import Thread
    from collections import deque
    from datetime import datetime
    import time
    import sys
    import cv2
    import imutils
    
    class CameraWidget(QtGui.QWidget):
        """Independent camera feed
        Uses threading to grab IP camera frames in the background
    
        @param width - Width of the video frame
        @param height - Height of the video frame
        @param stream_link - IP/RTSP/Webcam link
        @param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
        """
    
        def __init__(self, width, height, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
            super(CameraWidget, self).__init__(parent)
    
            # Initialize deque used to store frames read from the stream
            self.deque = deque(maxlen=deque_size)
    
            # Slight offset is needed since PyQt layouts have a built in padding
            # So add offset to counter the padding 
            self.offset = 16
            self.screen_width = width - self.offset
            self.screen_height = height - self.offset
            self.maintain_aspect_ratio = aspect_ratio
    
            self.camera_stream_link = stream_link
    
            # Flag to check if camera is valid/working
            self.online = False
            self.capture = None
            self.video_frame = QtGui.QLabel()
    
            self.load_network_stream()
    
            # Start background frame grabbing
            self.get_frame_thread = Thread(target=self.get_frame, args=())
            self.get_frame_thread.daemon = True
            self.get_frame_thread.start()
    
            # Periodically set video frame to display
            self.timer = QtCore.QTimer()
            self.timer.timeout.connect(self.set_frame)
            self.timer.start(.5)
    
            print('Started camera: {}'.format(self.camera_stream_link))
    
        def load_network_stream(self):
            """Verifies stream link and open new stream if valid"""
    
            def load_network_stream_thread():
                if self.verify_network_stream(self.camera_stream_link):
                    self.capture = cv2.VideoCapture(self.camera_stream_link)
                    self.online = True
            self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
            self.load_stream_thread.daemon = True
            self.load_stream_thread.start()
    
        def verify_network_stream(self, link):
            """Attempts to receive a frame from given link"""
    
            cap = cv2.VideoCapture(link)
            if not cap.isOpened():
                return False
            cap.release()
            return True
    
        def get_frame(self):
            """Reads frame, resizes, and converts image to pixmap"""
    
            while True:
                try:
                    if self.capture.isOpened() and self.online:
                        # Read next frame from stream and insert into deque
                        status, frame = self.capture.read()
                        if status:
                            self.deque.append(frame)
                        else:
                            self.capture.release()
                            self.online = False
                    else:
                        # Attempt to reconnect
                        print('attempting to reconnect', self.camera_stream_link)
                        self.load_network_stream()
                        self.spin(2)
                    self.spin(.001)
                except AttributeError:
                    pass
    
        def spin(self, seconds):
            """Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
    
            time_end = time.time() + seconds
            while time.time() < time_end:
                QtGui.QApplication.processEvents()
    
        def set_frame(self):
            """Sets pixmap image to video frame"""
    
            if not self.online:
                self.spin(1)
                return
    
            if self.deque and self.online:
                # Grab latest frame
                frame = self.deque[-1]
    
                # Keep frame aspect ratio
                if self.maintain_aspect_ratio:
                    self.frame = imutils.resize(frame, width=self.screen_width)
                # Force resize
                else:
                    self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
    
                # Add timestamp to cameras
                cv2.rectangle(self.frame, (self.screen_width-190,0), (self.screen_width,50), color=(0,0,0), thickness=-1)
                cv2.putText(self.frame, datetime.now().strftime('%H:%M:%S'), (self.screen_width-185,37), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,255), lineType=cv2.LINE_AA)
    
                # Convert to pixmap and set to video frame
                self.img = QtGui.QImage(self.frame, self.frame.shape[1], self.frame.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
                self.pix = QtGui.QPixmap.fromImage(self.img)
                self.video_frame.setPixmap(self.pix)
    
        def get_video_frame(self):
            return self.video_frame
    
    def exit_application():
        """Exit program event handler"""
    
        sys.exit(1)
    
    if __name__ == '__main__':
    
        # Create main application window
        app = QtGui.QApplication([])
        app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt())
        app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
        mw = QtGui.QMainWindow()
        mw.setWindowTitle('Camera GUI')
        mw.setWindowFlags(QtCore.Qt.FramelessWindowHint)
    
        cw = QtGui.QWidget()
        ml = QtGui.QGridLayout()
        cw.setLayout(ml)
        mw.setCentralWidget(cw)
        mw.showMaximized()
    
        # Dynamically determine screen width/height
        screen_width = QtGui.QApplication.desktop().screenGeometry().width()
        screen_height = QtGui.QApplication.desktop().screenGeometry().height()
    
        # Create Camera Widgets 
        username = 'Your camera username!'
        password = 'Your camera password!'
    
        # Stream links
        camera0 = 'rtsp://{}:{}@192.168.1.43:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera1 = 'rtsp://{}:{}@192.168.1.45/axis-media/media.amp'.format(username, password)
        camera2 = 'rtsp://{}:{}@192.168.1.47:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera3 = 'rtsp://{}:{}@192.168.1.40:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera4 = 'rtsp://{}:{}@192.168.1.44:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera5 = 'rtsp://{}:{}@192.168.1.42:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera6 = 'rtsp://{}:{}@192.168.1.46:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
        camera7 = 'rtsp://{}:{}@192.168.1.41:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
    
        # Create camera widgets
        print('Creating Camera Widgets...')
        zero = CameraWidget(screen_width//3, screen_height//3, camera0)
        one = CameraWidget(screen_width//3, screen_height//3, camera1)
        two = CameraWidget(screen_width//3, screen_height//3, camera2)
        three = CameraWidget(screen_width//3, screen_height//3, camera3)
        four = CameraWidget(screen_width//3, screen_height//3, camera4)
        five = CameraWidget(screen_width//3, screen_height//3, camera5)
        six = CameraWidget(screen_width//3, screen_height//3, camera6)
        seven = CameraWidget(screen_width//3, screen_height//3, camera7)
    
        # Add widgets to layout
        print('Adding widgets to layout...')
        ml.addWidget(zero.get_video_frame(),0,0,1,1)
        ml.addWidget(one.get_video_frame(),0,1,1,1)
        ml.addWidget(two.get_video_frame(),0,2,1,1)
        ml.addWidget(three.get_video_frame(),1,0,1,1)
        ml.addWidget(four.get_video_frame(),1,1,1,1)
        ml.addWidget(five.get_video_frame(),1,2,1,1)
        ml.addWidget(six.get_video_frame(),2,0,1,1)
        ml.addWidget(seven.get_video_frame(),2,1,1,1)
    
        print('Verifying camera credentials...')
    
        mw.show()
    
        QtGui.QShortcut(QtGui.QKeySequence('Ctrl+Q'), mw, exit_application)
    
        if(sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()
    

    【讨论】:

    猜你喜欢
    • 2017-11-10
    • 1970-01-01
    • 2018-07-26
    • 2020-12-24
    • 2012-06-16
    • 2014-09-07
    • 1970-01-01
    • 2012-04-04
    相关资源
    最近更新 更多