处理大量RTSP摄像头,不受实时限制

2022-04-08 00:00:00 python image-processing cv2 rtsp real-time

问题描述

问题定义

假设我有大量的RTSP摄像头(>;100),我想对其执行一些操作,如图像特征提取。

重要提示:我对实时性能不感兴趣,我可以立即进行4次特征提取。显然,越多越好!

像现在一样,瓶颈是图像获取。帧的获取方式为cv2 有关我尝试的内容,请阅读下面的部分。

伪码(当前解决方案)

while True:
    for every rstp_cameras:
        open_connection
        read_current_frame(no batch - the real time frame)
        process_frame
        close

我尝试的内容

在Stackoverflow上,你可以找到很多关于实时读取RTSP摄像头的答案,但都受到摄像头数量的限制,或者都有一些缺点。 我尝试过(使用python):

  1. 每个摄像头都有一个线程[带ffmpeg的CV2]
    • 打开线程中每个摄像机的连接,然后获取每个摄像机可用的最后一帧。
    • 此解决方案有效,但仅适用于少量摄像头。如果我们增加这个数字,高端CPU的使用率将达到100%(因为后台的线程总是在读取最后一帧,如果我不是在请求最后一帧,则会将其丢弃)
  2. [当前解决方案,无线程,带CV2的ffmpeg]每次迭代打开连接,读框,关闭连接。此解决方案允许我有最后一帧可用,但主要缺点是在打开过程中损失了时间(打开所有帧需要大约70秒)
  3. 带有GStreamer的CV2,无线程
    • 基于此answer。是我找到的最好的解决方案如果您有少量的摄像头。对于20个或更多的摄像头,我在线程解决方案中也遇到了同样的问题。

问题总结(&A)

现在,我很清楚,在一个工作站上处理所有这些摄像头是很困难的,因为我找到的所有解决方案都是为了返回最后一个可用帧(实时帧),在后台连续读取该帧。

目前,我还没有找到一种解决方案,可以让我一次打开连接,读取低CPU使用率的实时帧,以便我可以在大量摄像头下使用它。

阅读并行化是解决问题的唯一途径吗?我的意思是,将摄像机分成几批,在不同的工作站分配批次,然后以某种方式合并图像?

谢谢。


解决方案

您可以尝试将-discard nokey参数与here参数一起使用。
我不确定这是否会奏效...

据我所知-discard nokey只解码关键帧,跳过所有其他视频帧。
仅对关键帧进行解码应该会大大减少CPU使用量(取决于关键帧的频率)。


以下是一个代码示例(针对一个RTSP流):

import numpy as np
import subprocess as sp


# Use public RTSP Streaming for testing:
in_stream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"

# Use OpenCV for getting the video resolution.
cap = cv2.VideoCapture(in_stream)

# Get resolution of input video
width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# Release VideoCapture - it was used just for getting video resolution
cap.release()


# http://zulko.github.io/blog/2013/09/27/read-and-write-video-frames-in-python-using-ffmpeg/
FFMPEG_BIN = "ffmpeg" # on Linux ans Mac OS (also works on Windows when ffmpeg.exe is in the path)

ffmpeg_cmd = [FFMPEG_BIN,
              '-discard', 'nokey',
              '-rtsp_transport', 'tcp',
              '-max_delay', '30000000',  # 30 seconds
              '-i', in_stream,
              '-f', 'rawvideo',
              '-pix_fmt', 'bgr24',
              '-vcodec', 'rawvideo', '-an', 'pipe:']

# Open sub-process that gets in_stream as input and uses stdout as an output PIPE.
process = sp.Popen(ffmpeg_cmd, stdout=sp.PIPE)


while True:
    raw_frame = process.stdout.read(width*height*3)

    if len(raw_frame) != (width*height*3):
        print('Error reading frame!')  # Break the loop in case of an error (too few bytes were read).
        break

    # Transform the byte read into a numpy array, and reshape it to video frame dimensions
    frame = np.frombuffer(raw_frame, np.uint8).reshape((height, width, 3))

    # Show frame for testing
    cv2.imshow('frame', frame)
    cv2.waitKey(1)
  
process.stdout.close()
process.wait()
cv2.destroyAllWindows()

尝试如上打开100个FFmpeg子进程...

相关文章