无法并行化进程相互依赖的Python进程

时间:2019-04-13 07:07:29

标签: python multithreading opencv concurrency raspberry-pi3

我在Raspberry Pi 3B +上有一个深度学习应用程序。我有一个循环,该循环首先从摄像机抓取一帧,然后将其传递到神经网络进行预测,然后在屏幕上显示带有预测的帧:

while True:
    frame = cam.get_frame()

    preds = model.predict(frame)

    label, score, c, pts = get_best_classes(preds, model)

    print("{} ({}): {:.2f}".format(label, c, score))

    screen.draw(frame, pts)

抓取帧并显示非常快(实时),但预测约为0.7秒。当我运行它时,大约有4秒钟的延迟,这意味着当我移动相机时,屏幕上的输出将仅在4秒钟后移动。我对此进行了研究,这是因为框架在模型无法预测之前就堆积了。 The solution是要在不同的线程上获取帧和进行预测,但是我没有线程或多处理的经验。

我在Google上搜索了许多教程,但它们都是并行打印内容的示例。我找不到入门教程,其中一个过程(在我的情况下为预测)取决于另一个过程的输出(从相机中抓取一帧)。

所以我的问题分为三个部分:

  1. 能否请您指出可能的解决方案?
  2. 您能否提供一些教程的链接,其中进程共享数据,而一个进程仅在另一进程完成任务时才开始发挥作用
  3. 如何确保进程无限循环运行?

2 个答案:

答案 0 :(得分:0)

我发现了延迟的原因。事实证明,cam.get_frame()(是我写的cv2.VideoCapture().read()的薄包装)有大约5-7个预缓冲帧。因此,每次调用它时,它不会返回当前帧,而是返回缓冲区中的下一帧。我发现this解决方案很有帮助。

修改代码后,延迟消失了:

# buffered frames
n = 5
while True:
    # ignore buffered frames
    for _ in range(n):
        frame = cam.get_frame()

    preds = model.predict(frame)

    label, score, c, pts = get_best_classes(preds, model)

    print("{} ({}): {:.2f}".format(label, c, score))

    screen.draw(frame, pts)

答案 1 :(得分:0)

如果选择采用多处理路线,则可以使用多处理Queue or a Pipe在进程之间共享数据。队列既是线程安全的,又是进程安全的。使用管道时,您必须更加小心,因为如果两个进程(或线程)尝试同时从管道的同一端读取或写入管道,则管道中的数据可能会损坏。当然,同时使用管道的不同末端的过程不会造成损坏的风险。

对于您的应用程序,建议使用多线程路由。这个想法是要有两个线程来避免顺序获取和处理帧。

  • 线程#1-专用于仅读取摄像机流中的帧。
  • 线程2-用于处理帧(预测)。

因为cv2.VideoCapture.read()是一项阻塞操作,所以我们将读取框架与处理分开。因此,我们在其自己的独立线程中读取帧以通过减少I / O操作引起的延迟来“改善” FPS。此外,通过将帧捕获隔离到其自己的线程,将始终有准备好处理的帧,而不必等待I / O操作完成并返回新的帧。在专用于处理的主线程中,我们现在可以自由地进行预测,而不必等待相机抓取下一帧。

这是一个小部件,专用于仅读取摄像机流中的帧。在主程序中,您可以使用最新框架自由进行处理/预测。

from threading import Thread
import cv2

class VideoStreamWidget(object):
    def __init__(self, src=0):
        # Create a VideoCapture object
        self.capture = cv2.VideoCapture(src)

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow('frame', self.frame)

        # Press Q on keyboard to stop stream 
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)

    def grab_latest_frame(self):
        return self.frame

if __name__ == '__main__':
    video_stream_widget = VideoStreamWidget(0)
    while True:
        try:
            video_stream_widget.show_frame()
            latest_frame = video_stream_widget.grab_latest_frame()
            # Do processing here with the latest frame
            # ...
            # ...
        except AttributeError:
            pass