我有一个简单的网络摄像头,我使用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)
。
有人知道我在这里做错了什么吗?我需要以不同方式解码素材吗?欢迎所有提示!
答案 0 :(得分:3)
鉴于目标很明确,您对分布式应用程序基础架构的快速原型设计取决于几个风险点。
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()
处理程序部分是公平的必须。
finally:
的{{1}} - s 问题隔离的最佳第一级是设置流量,只是为了提供不受控制的SEQ
整数,从{strong> int
方面进行SEQ
广播
.send( )
除非你的接收方证明了它对int-s流的强大处理,否则继续下去是没有意义的。
PUB
... # 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感到满意。
可能会this对numpy
方法进行简单的GUI交互式参数调整。
可能会cv2
对cv2
处理管道分析进行处理,并将 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
无论如何,我祝你生日快乐!