如何通过ZeroMQ套接字发送OpenCV视频素材?

时间:2017-05-06 05:44:22

标签: python opencv video streaming zeromq

我有一个简单的网络摄像头,我使用OpenCV读出来,现在我正在尝试使用ZeroMQ将此视频素材发送到另一个(Python)程序。所以我有以下简单的脚本来读取网络摄像头并使用ZeroMQ套接字发送它:

import cv2
import os
import zmq
import base64

context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')

# init the camera
camera = cv2.VideoCapture(0)

while True:
    try:
        (grabbed, frame) = camera.read()            # grab the current frame
        frame = cv2.resize(frame, (640, 480))       # resize the frame
        footage_socket.send_string(base64.b64encode(frame))

        # Show the video in a window
        cv2.imshow("Frame", frame)                  # show the frame to our screen
        cv2.waitKey(1)                              # Display it at least one ms
        #                                           # before going to the next frame

    except KeyboardInterrupt:
        camera.release()
        cv2.destroyAllWindows()
        print "\n\nBye bye\n"
        break

这很有效,因为它显示视频并且不会出现任何错误。

我注释掉显示图像的两行(cv2.imshow()cv2.waitKey(1))。然后,我在paralel中启动了下面的脚本。第二个脚本应该接收视频片段并显示它。

import cv2
import zmq
import base64
import numpy as np

context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://*:5555')
footage_socket.setsockopt_string(zmq.SUBSCRIBE, unicode(''))

# camera = cv2.VideoCapture("output.avi")

while True:
    try:
        frame = footage_socket.recv_string()
        frame = np.fromstring(base64.b64decode(frame), dtype=np.uint8)
        cv2.imshow("Frame", frame)                  # show the frame to our screen
        cv2.waitKey(1)                              # Display it at least one ms
        #                                           # before going to the next frame
    except KeyboardInterrupt:
        cv2.destroyAllWindows()
        break

print "\n\nBye bye\n"

不幸的是,这冻结了cv2.waitKey(1)

有人知道我在这里做错了什么吗?我需要以不同方式解码素材吗?欢迎所有提示!

3 个答案:

答案 0 :(得分:3)

第0步:风险清单

鉴于目标很明确,您对分布式应用程序基础架构的快速原型设计取决于几个风险点。

0) OpenCV cv2 模块大量使用底层的基于C的组件,如果尝试使用cv2.imshow()设施,python美常常挂起cv2(外部FSA)窗口管理&帧显示服务。

1) ZeroMQ框架可以为您提供更多帮助,而不仅仅是为了使用(string)来强制将数据强制转换为.send_string() / .recv_string() - 这可能会在这里变得更好在一些更智能的BLOB映射对象中移动已知图像( pxW * pxH )几何* RGB深度(下面将在架构展望中更多地谈论这方面)。

2)给定点0& 1,ZeroMQ基础设施(在原型设计中越多)应该对未处理的异常变得强大(在zmq.Context() .Socket() cv2.imshow()个实例及其关联的try: except: finally:个孩子将自己挂在已分配的资源上崩溃,因为在快速原型循环中经常这样做。

因此是一个彻底的&在 .setsockopt( zmq.LINGER, 0 ) 处理程序&处理器内部自律编写代码框架在套接字实例化后立即显式初始 .close() + {{1>} + context.term() 处理程序部分是公平的必须。

步骤1:通过发送finally:的{​​{1}} - s

来验证ZeroMQ部分

问题隔离的最佳第一级是设置流量,只是为了提供不受控制的SEQ整数,从{strong> int 方面进行SEQ广播

.send( )

除非你的接收方证明了它对int-s流的强大处理,否则继续下去是没有意义的。

PUB

第2步:将... # FOR STREAMING, ALWAYS PREFER DESIGNS USING A NONBLOCKING MODE SEQ += 1 footage_socket.send( SEQ, zmq.NOBLOCK ) # PUB.send( SEQ ) -> *SUB* ... ... aMsgIN = footage_socket.recv( zmq.NOBLOCK ) # FOR STREAMING, ALWAYS PREFER DESIGNS USING A NONBLOCKING MODE print "{0:}".format( aMsgIN if len( aMsgIN ) > 0 else "." ), # sleep(...) # backthrottle the loop a bit ... 数据和&事件 - 环

如果您的数据泵根据需要工作,远程显示器作为下一个目标开始变得有意义。

ZeroMQ提供完整的消息(BLOB),或者什么都不提供。这是事实。接下来,ZeroMQ不保证任何人都能提供无错误的服务。这些是事实,你的设计必须与之共存。

采集是更简单的部分,只需抓取数据(也许一些颜色空间转换可能会集中在这里,但除此之外,任务是(对于子4K / sub 30fps图像处理)这一侧通常没有错误。

如上所述,

.imshow() 是一个 .recv() 实例。在发送硬编码的二进制映射BLOB时,将获得最佳性能,而不需要任何" smart"转换,显然,frame只是一大堆(虽然有一些更高级的美食可能与ZeroMQ的零拷贝机制,但这些在发送方这个阶段没有直接的好处)

numpy.ndarray

较难的部分在接收器侧。如果试图使用隐藏在frame内的# struct.unpack() / .pack() seems "just"-enough for fast & smart custom Payload protocol designs DecodeWireMSG_DATA( aMSG_DATA = struct.unpack( "!" + ( aPayloadHEADER + ( ( pxW * pxH ) * RGB_DEPTH * CELL_SIZE ) ) * "I", aMsgIN ) ) 内置事件循环引擎,此循环将与您通过cv2读取更新流的外部逻辑冲突.imshow()方面。

作为一种合理的妥协,人们可能会忽略所有"延迟" .recv() - s,没有让进程与PUB - 侧获取/广播接收同步,只显示最近的进程...使用 frame PUB将丢弃应该处理的zmq.CONFLATE个实例,因此应该为这样的1:1文档目的添加一些其他架构,最好与" just-visual"数据/处理流程的分支。)

完成此操作后,zmq.CONFLATE(可能是frame + .recv()循环的组合)将为Poller()方提供适当的数据泵,即独立于.recv()工具及其SUB(隐藏)-FSA事件循环。

建筑与建筑表现提示:

  • 对于更高的fps + FullHD / 2K / 4K项目,使用zmq.Stopwatch()实例系统地分析cv2处理的累积处理延迟/延迟。 .imshow()方法。

  • 拥有硬数据,您可以及时发现,当其他需求似乎可以解决一些更难实时的约束时,请考虑一下:

  • 主要避免任何陷入任何无法控制的黑洞蟒蛇垃圾收集的风险 - 始终控制 cv2围绕您的关键路径部分并启动明确的{{ 1}}你的设计知道它是可行的。

  • 避免新的内存分配延迟 - 可能预先分配所有必需的{ .start(), .stop() }数组,稍后只需强制{ gc.disable() | gc.enable();gc.collect() }用于那些就地数据模式修改,从而避免任何进一步的ad-hoc内存管理相关的等待状态

  • 设计流量以增加错误免疫力,通过单独的,多流的,独立更新整个大/(颜色) - 深度图像的部分(条纹)(记住零保修 - 获得完整的"胖" -message或 gc.collect()

  • 使用 numpy 调整 numpy 的效果,以映射不同类别的I / O流量优先考虑隔离的None I / O线程。

  • 在PUB方面微调 .Context() + zmq.AFFINITY ,如果需要多用户且 {{未使用1}}

  • 最后但并非最不重要的是,可以利用 zmq.Context( N ) LLVM预编译的关键路径函数的可重用代码加速(通常是重zmq.SNDBUF处理),其中额外的微秒剃掉会给你的视频处理管道带来最有益的影响,同时仍然保持在纯粹的python中(当然,仍有一些zmq.SNDHWM警告)。

更多提示&在原型设计阶段使用zmq.CONFLATE的技巧:

可能会对基于numba.jit()的图像处理this感到满意。

可能会thisnumpy方法进行简单的GUI交互式参数调整。

可能会cv2cv2处理管道分析进行处理,并将 cv2 详细信息降至cv2

答案 1 :(得分:1)

框架对象包含服务器状态的内存状态,当它发送到客户端时,它会冻结,因为它接收的帧是浅拷贝。 尝试查找如何为框架制作深层副本。

答案 2 :(得分:1)

最后,我采取了中间步骤来解决问题。我首先将单个图像写入磁盘,然后再次读出这些图像。这导致我需要将帧编码为图像(我选择了jpg),并使用魔术方法cv2.imencode('.jpg', frame)cv2.imdecode(npimg, 1)我可以使其工作。我粘贴了下面的完整工作代码。

第一个脚本读出网络摄像头并通过zeromq套接字发送镜头:

import cv2
import zmq
import base64

context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')

camera = cv2.VideoCapture(0)  # init the camera

while True:
    try:
        (grabbed, frame) = camera.read()  # grab the current frame
        frame = cv2.resize(frame, (640, 480))  # resize the frame
        encoded, buffer = cv2.imencode('.jpg', frame)
        footage_socket.send_string(base64.b64encode(buffer))

    except KeyboardInterrupt:
        camera.release()
        cv2.destroyAllWindows()
        print "\n\nBye bye\n"
        break

并且第二个脚本接收帧图像并显示它们:

import cv2
import zmq
import base64
import numpy as np

context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://*:5555')
footage_socket.setsockopt_string(zmq.SUBSCRIBE, unicode(''))

while True:
    try:
        frame = footage_socket.recv_string()
        img = base64.b64decode(frame)
        npimg = np.fromstring(img, dtype=np.uint8)
        source = cv2.imdecode(npimg, 1)
        cv2.imshow("image", source)
        cv2.waitKey(1)

    except KeyboardInterrupt:
        cv2.destroyAllWindows()
        print "\n\nBye bye\n"
        break

无论如何,我祝你生日快乐!