如何将实时数据读入循环与Python中更多处理密集型操作分开?

时间:2015-05-19 01:42:04

标签: 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 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 Arrays)添加到第二个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()

1 个答案:

答案 0 :(得分:1)

多处理文档是一个很好的起点。 https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes 即使你现在可能不理解,我建议你阅读。

使用管道覆盖您提到的其他技术将使您能够保持性能并保持代码简单。

下面是一些我没有测试过的代码应该给你一个好的起点。

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)