在Python中,如何将实时数据读入循环与处理密集型操作分开?
所以我有一个OpenCV网络摄像头提要,我想尽快读取其中的帧。由于Python GIL,我的脚本在帧中读取的最快速度似乎如下所示:在Python中,如何将实时数据读入循环与处理密集型操作分开?,python,opencv,ipc,python-multiprocessing,Python,Opencv,Ipc,Python Multiprocessing,所以我有一个OpenCV网络摄像头提要,我想尽快读取其中的帧。由于Python GIL,我的脚本在帧中读取的最快速度似乎如下所示: #Parent or maybe client(?) script #initilize the video capture object cam = cv2.VideoCapture(0) while True: ret, frame = cam.read() # Some code to pass this numpy frame arr
#Parent or maybe client(?) script
#initilize the video capture object
cam = cv2.VideoCapture(0)
while True:
ret, frame = cam.read()
# Some code to pass this numpy frame array to another python script
# (which has a queue) that is not under time constraint and also
# is doing some more computationally intensive post-processing...
if exit_condition is True:
break
我希望将这些帧(Numpy数组)添加到第二个Python脚本(或者可能是一个多处理实例?)中的某种处理队列中,然后该脚本将执行一些不受时间限制的后处理,如cam.read()循环
因此,基本想法如下所示:
实时(或尽可能快)数据采集(摄像头读取)脚本
---->
分析脚本(该脚本将进行后处理、写入结果,并生成稍微滞后于数据采集的matplotlib图)
我做了一些研究,看起来管道、套接字、pyzmq和python多处理都可以实现我想要的。问题是我对以上任何一项都没有经验
所以我的问题是,什么样的方法才能最好地实现我所寻找的目标,谁能提供一个简短的例子,甚至一些想法/想法来为我指明正确的方向
非常感谢
编辑:非常感谢史蒂夫让我走上正轨。这是我所想的工作要点。。。代码按原样运行,但如果添加更多的后处理步骤,那么队列大小的增长速度可能会快于主进程的处理速度。限制帧速率的建议很可能是我最终使用的策略。
import time
import cv2
import multiprocessing as mp
def control_expt(connection_obj, q_obj, expt_dur):
def elapsed_time(start_time):
return time.clock()-start_time
#Wait for the signal from the parent process to begin grabbing frames
while True:
msg = connection_obj.recv()
if msg == 'Start!':
break
#initilize the video capture object
cam = cv2.VideoCapture(cv2.CAP_DSHOW + 0)
#start the clock!!
expt_start_time = time.clock()
while True:
ret, frame = cam.read()
q_obj.put_nowait((elapsed_time(expt_start_time), frame))
if elapsed_time(expt_start_time) >= expt_dur:
q_obj.put_nowait((elapsed_time(expt_start_time),'stop'))
connection_obj.close()
q_obj.close()
cam.release()
break
class test_class(object):
def __init__(self, expt_dur):
self.parent_conn, self.child_conn = mp.Pipe()
self.q = mp.Queue()
self.control_expt_process = mp.Process(target=control_expt, args=(self.child_conn, self.q, expt_dur))
self.control_expt_process.start()
def frame_processor(self):
self.parent_conn.send('Start!')
prev_time_stamp = 0
while True:
time_stamp, frame = self.q.get()
#print (time_stamp, stim_bool)
fps = 1/(time_stamp-prev_time_stamp)
prev_time_stamp = time_stamp
#Do post processing of frame here but need to be careful that q.qsize doesn't end up growing too quickly...
print (int(self.q.qsize()), fps)
if frame == 'stop':
print 'destroy all frames!'
cv2.destroyAllWindows()
break
else:
cv2.imshow('test', frame)
cv2.waitKey(30)
self.control_expt_process.terminate()
if __name__ == '__main__':
x = test_class(expt_dur = 60)
x.frame_processor()
多处理文档是一个很好的起点。 我建议你读这篇文章,即使你现在可能还不明白 在您提到的其他技术之上使用管道将允许您保持性能并保持代码简单 下面是一些我还没有测试过的代码,它们应该为您提供一个良好的起点
from multiprocessing import Process, Pipe
def read_frames(connection_obj):
#initilize the video capture object
cam = cv2.VideoCapture(0)
while True:
ret, frame = cam.read()
connection_obj.send(frame) # is this what you want to send?
if exit_condition is True:
connection_obj.close()
break
def launch_read_frames(connection_obj):
"""
Starts the read_frames function in a separate process.
param connection_obj: pipe to send frames into.
Returns a pipe object
"""
read_frames_process = Process(target=read_frames, args=(connection_obj,)) # this trailing comma is intentional
read_frames_process.start()
read_frames_process.join()
return parent_conn
def act_on_frames(connection_obj):
while True:
frame = connection_obj.recv()
# Do some stuff with each frame here
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
launch_read_frames(child_conn)
# You could also call this function as a separate process, though in
# this instance I see no performance benefit.
act_on_frames(parent_conn)
感谢您的快速响应!我将阅读你今晚发送的链接,试用你明天早上发布的示例代码,看看是否符合我的期望。一个简单的问题。。如果连接_obj.send(frames)的速率远高于连接_obj.recv()的速率,是否会出现诸如帧未被处理或管道过度填充之类的问题?管道确实具有系统定义的缓冲区大小。更多信息请参见此答案。如果您觉得数据发送的速度比正在处理的速度快得多,那么您应该考虑实现一个工作线程池,一旦数据可以从管道读取,就可以启动工作线程池。这当然增加了复杂性,因为现在必须聚合每个辅助线程的结果。签出multiprocessing.Pool类。请注意,您只需要一个进程在管道上调用recv(),以避免冲突/错误。我明白了,在阅读多处理页面后,将
frame=connection\u obj.recv()
的结果卸载到SimpleQueue(queue=multiprocessing.queues.SimpleQueue()queue.put(frame)
)这似乎有一个限制32767(见:)?如果你使用一个工人池,我肯定会排队。然而,对于单个工作,我将使用管道,因为多处理队列构建在管道之上。对于多个工作进程,队列是最好的选择,因为一次从多个进程调用queue.get()很好,而一次从不同的进程调用pipe.recv()可能会导致错误。我建议测试其中任何一个,看看bufsize是否会变大并出现问题。你打算处理这个流多长时间?嗯。。。因此,我计划在非常长的时间内(最多36小时,可能超过36小时)处理流,我希望按顺序处理帧,因为帧@t-1将影响在帧@t上执行的计算。正因为如此,我相信我只限于一个工人。但我突然想到,也许我可以将连接的结果\u obj.recv()
附加到一个collections.deque对象中,而不是一个队列,该对象可以无限扩展。。。然后,我的分析/后处理函数的while循环可能只是弹出dequeEdit建议的元素:第二个代码块的缩进问题。如果粘贴副本,则不起作用