我在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上搜索了许多教程,但它们都是并行打印内容的示例。我找不到入门教程,其中一个过程(在我的情况下为预测)取决于另一个过程的输出(从相机中抓取一帧)。
所以我的问题分为三个部分:
答案 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在进程之间共享数据。队列既是线程安全的,又是进程安全的。使用管道时,您必须更加小心,因为如果两个进程(或线程)尝试同时从管道的同一端读取或写入管道,则管道中的数据可能会损坏。当然,同时使用管道的不同末端的过程不会造成损坏的风险。
对于您的应用程序,建议使用多线程路由。这个想法是要有两个线程来避免顺序获取和处理帧。
因为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