- 下载数据-19.3 MB
- 下载模型-43.5 MB
- 下载结果-36.66 MB
这是七篇系列文章中的最后一篇。到目前为止,我们已经有了用于人员检测的DNN模型和用于在Raspberry Pi设备上启动该模型的Python代码。我们使用MobileNet DNN构建的AI算法在Pi 3B board上平均约为1.25 FPS。正如我们在前一篇文章中所展示的,这种速度足以检测人在实时摄像机的视频流中的出现。但是我们的算法不是常规意义上的实时齿轮。
视频监视系统中的实时摄像机通常以15-30 FPS的速度运行。我们的算法以1.25 FPS的速率处理帧,太慢了。是什么让它变慢?让我们看一下我们的代码以获取线索。
这是我们在前一篇文章中开发的一部分代码:
fps.start()
obj_data = self.ssd.detect(frame)
persons = self.ssd.get_objects(frame, obj_data, class_num, min_confidence)
fps.stop()
如您所见,我们仅测量人员检测操作(在框架上运行SSD模型)的速度。因此,此操作需要每帧0.8秒才能执行。该算法的其余部分足够快,可以以实时视频系统的速度运行。
考虑到这一点,我们是否可以重新设计算法以使其适应处理实时视频流所需的更高速度?我们试试吧。
在该算法的当前版本中,我们运行SSD模型处理(每帧大约需要0.8秒),其余代码可以按顺序进行实时操作。因此,较慢的部分定义了整体速度。使算法适应实时模式的最简单方法是将其分为多个部分,然后并行运行这些部分。“快”部分将以其实时速度运行,而“慢”部分将以1.25 FPS运行。但是,由于这些部分将异步运行,因此我们可以处理(慢速)SSD模型的部分,并且仅处理那些值得考虑的部分。
看下面的代码。这是一个新类,实现了异步处理的思想:
# Real-Time Video detector
import sys
from multiprocessing import Process
from multiprocessing import Queue
def detect_in_process(proto, model, ssd_proc, frame_queue, person_queue, class_num, min_confidence):
ssd_net = CaffeModelLoader.load(proto, model)
ssd = SSD(ssd_proc, ssd_net)
while True:
if not frame_queue.empty():
frame = frame_queue.get()
obj_data = ssd.detect(frame)
persons = ssd.get_objects(frame, obj_data, class_num, min_confidence)
person_queue.put(persons)
class RealtimeVideoSSD:
def __init__(self, proto, model, ssd_proc):
self.ssd_proc = ssd_proc
self.proto = proto
self.model = model
def detect(self, video, class_num, min_confidence):
detection_num = 0
capture = cv2.VideoCapture(video)
frame_queue = Queue(maxsize=1)
person_queue = Queue(maxsize=1)
detect_proc = Process(target=detect_in_process, args=(self.proto, self.model, self.ssd_proc, frame_queue, person_queue, class_num, min_confidence))
detect_proc.daemon = True
detect_proc.start()
frame_num = 0
persons = None
# Capture all frames
while(True):
t1 = time.time()
(ret, frame) = capture.read()
if frame is None:
break
if frame_queue.empty():
print ("Put into frame queue ..."+str(frame_num))
frame_queue.put(frame)
t2 = time.time()
dt = t2-t1
if dt0):
detection_num += len(persons)
Utils.draw_objects(persons, "PERSON", (0, 0, 255), frame)
# Display the resulting frame
cv2.imshow('Person detection',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
frame_num += 1
capture.release()
cv2.destroyAllWindows()
return (frame_num, detection_num)
如您所见,检测算法被提取到一个单独的detect_in_process函数中。此函数有两个特殊参数:frame_queue和person_queue。这些队列旨在支持帧捕获过程和人员检测过程之间的交互。
让我们看一下用于实时人员检测的主类RealtimeVideoSSD。它的构造函数接收三个参数:SSD结构的原型,SSD模型和帧处理器。请注意,前两个参数是模型数据的路径,而不是加载的模型本身的路径。模型被加载到执行过程中。这使我们避免了流程之间大量数据的传输;我们仅传输轻量字符串数据。
detect类的方法接收视频文件的路径,人员类的编号和置信度阈值。首先,它创建视频流的捕获以及帧和人物检测的队列。然后,它使用指定的参数初始化Process类的实例。其中之一是detect_in_process函数,它将随流程一起运行。检测过程在后台(daemon = True)运行。
进一步,我们遍历所有接收到的帧。当我们得到一个新的帧时,我们查看帧队列,如果它是空的,我们将帧放入队列中。后台进程查看队列并开始处理框架。完成后,该过程会将结果放入人员队列。
帧处理循环查看人员队列,如果检测到,将其绘制在当前帧上。因此,如果在一个帧中检测到一个人,则该人将显示在检测到的帧中,而不是原始帧中。
这里的另一招是在帧处理循环中。注意,我们测量capture.read方法的时间。然后,我们将帧处理设置为睡眠模式以强制其减慢速度。在这种情况下,我们将其速度降低到大约25 FPS(0.040秒= 1/25 FPS)——测试视频的实际帧速率。
现在,我们可以使用以下Python代码以实时模式测试人员检测:
# testing SSD in real-time mode
if __name__ == '__main__':
proto_file = r"/home/pi/Desktop/PI_RPD/mobilenet.prototxt"
model_file = r"/home/pi/Desktop/PI_RPD/mobilenet.caffemodel"
proc_frame_size = 300
person_class = 15
# frame processor for MobileNet
frame_proc = FrameProcessor(proc_frame_size, 1.0/127.5, 127.5)
video_file = r"/home/pi/Desktop/PI_RPD/video/persons_4.mp4"
video_ssd = RealtimeVideoSSD(proto_file, model_file, frame_proc)
(frames, detections) = video_ssd.detect(video_file, person_class, 0.5)
print ("Frames count: "+str(frames))
print ("Detection count: "+str(detections))
这是在Pi 3B设备上运行的测试的屏幕视频:
youtube
您可以看到我们的视频以FPS = 25的实时模式运行。人员检测滞后——检测矩形与人员位置不匹配,如下图所示:
这种滞后是我们检测算法的一个明显缺点:检测到出现后0.8秒,我们就会显示出检测结果。当人员移动时,检测会显示人员过去的位置。不过,该算法是实时的:它确实以FPS = 25(视频本身的帧频)处理视频。该算法执行其预期的任务:它检测视频流中的人物。考虑到一个人在摄像机视图中行走了几秒钟,发现该人的外观的可能性非常高。
在本系列文章中,我们使用现代的AI算法在计算资源有限的边缘设备上进行人员检测。我们考虑了三种用于对象检测的DNN方法,并选择了最佳模型——适用于Raspberry Pi设备上的图像处理。我们使用预训练的DNN模型开发了用于人员检测的Python代码,并展示了如何在Raspberry Pi设备上启动该代码。最后,我们调整了代码以进行实时处理,并在视频上对其进行了测试,证明它可以检测实时视频流中的人。