7.3. Hello BlazeFace¶
BlazeFace 是来自 Google MediaPipe 系列的人脸检测神经网络。单次推理调用会返回每张检测到的人脸的边界矩形,以及六个面部关键点——右眼、左眼、鼻子、嘴、右耳、左耳。每一台出厂带有神经网络支持的 OpenMV Cam 都在闪存中携带 blazeface_front_128.tflite 模型,因此运行一个端到端的人脸检测器只需几行 Python。
7.3.1. 完整脚本¶
import csi
import ml
from ml.postprocessing.mediapipe import BlazeFace
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
while True:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
这就是整个人脸检测器,没有别的内容了;脚本捕获一帧,把它交给模型,遍历返回的检测列表,并把每张人脸的边界矩形及其六个关键点绘制回这一帧。IDE 预览会实时显示这些边界框和关键点。
7.3.2. 每一行的作用¶
前三行导入脚本所需的模块。csi 是摄像头传感器接口;ml 是本章其余部分所讲述的机器学习模块;BlazeFace 是后处理器,它把 BlazeFace 的原始输出张量转换成脚本所遍历的边界框和关键点列表。
接下来的五行配置传感器。摄像头被复位到已知状态,设置为 RGB565 颜色,设置为 VGA 分辨率,然后 开窗(windowed) 为一个 400×400 的正方形。开窗很重要:BlazeFace 是在正方形裁剪图上训练的,给它一个正方形输入可以让网络期望的宽高比与它在捕获帧中看到的内容对齐。
加载模型这一行打开模型文件:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model 读取给定路径处的文件——/rom/ 是一个稍后会介绍的常驻闪存的文件系统——并返回一个模型对象,脚本将针对它运行推理。postprocess= 关键字注册了 BlazeFace 后处理器;如果没有它,predict 会返回网络的原始输出张量,应用就必须自己手动解码它们。有了它,predict 直接返回解码后的结果。后处理器上的 threshold=0.4 参数设置网络在保留一个检测之前必须报告的最低置信度;较低的值能以更多误检为代价捕捉到更模糊的人脸。
其余四行是主循环。每次循环都会捕获一帧并询问模型它看到了什么:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
predict() 接收一个输入列表(这里是一张捕获的图像),并返回一个检测元组列表。每个元组包含边界矩形 (x, y, w, h)、一个介于零和一之间的置信度 score,以及一个 (6, 2) 的关键点坐标 ndarray——依次为右眼、左眼、鼻子、嘴、右耳和左耳。绘制调用使用 draw_rectangle()——也就是图像章节中每个经典检测器最后所用的同一个原语——来勾勒人脸轮廓。ml.utils.draw_keypoints() 是 ml 工具库中的一个小辅助函数,它在每个关键点的 (x, y) 位置用一个十字标记它。
7.3.3. 脚本没有说明的部分¶
除去导入和传感器设置,脚本只有七行可运行的推理工作,但这七行内部发生了大量的算术运算。捕获的 400×400 RGB565 帧在到达网络之前会变成一个 128×128 的量化 8 位张量;网络针对数万个权重运行数百次运算;由此产生的置信度分数张量和边界框偏移张量,在 predict 返回之前会变成一个带有附属关键点的、互不重叠的边界框排序列表。其中每一步变换在应用需要时都是 可以 控制的,而且其中有几步对任何非默认模型都必须进行调优。
接下来的四个小节会把这些变换逐一展开。依次为:
ml 模块——一旦模型加载完毕,
ml.Model暴露了什么,以及模型文件在摄像头上实际存放在哪里。推理流水线——每次
predict()调用的四个阶段。推理引擎——运行网络算术运算的 CPU 与 NPU 路径。
解码输出——把原始输出张量转换成本脚本所遍历的检测结果的后处理器。
读完本章后,读者就能为一个并非随摄像头出厂的模型编写等效脚本,解码一个尚不存在后处理器的张量,并能推理为什么某个特定模型在一台摄像头上以 30 FPS 运行而在另一台上只有 3 FPS。