系列编排日期为 2026 年 8 月;正文依据 2026-10-12 核对的实现整理。历史实测保留真实日期,教学数字不代表实测;本次未重新执行板端推理。
返回系列目录 · 本篇为 14 章+4 份附录中的一篇。
三个运行位置
PC 预览用于浮点参考、几何观察和规则验证;板端 Stream 负责生产入口中的图像获取、五阶段推理和判定;GenerateTestTools 上位机负责发起测试、显示板端输出、调整参数和保存样本。上位机没有另写一套遮挡规则来代替板端结果。
当前业务入口仍名为 yunet_pipnet,报告中的模型字符串也保留历史命名。不能只看名称推断运行的是两个还是五个模型,应检查 decision_policy、head_model、模型路径、unet_ran 和 world_review。
一帧从输入到输出
- 输入源提供紧凑 BGR888 图像;
setFrame()保存该帧并记录单调时钟时间。 - 头部图像按比例缩放补边,调用槽位 2;解码头框并进行 NMS。
FaceTracks::assign()用头框与上一轮轨迹关联,为每个头部分配 ID。- 扩大头框,在其区域内执行槽位 0 的 YuNet;仅选择中心落在原头框内的最高分人脸。
- 人脸裁剪有效时,将同一裁剪缩放到 256×256,执行槽位 1 的 PIPNet。
- 继续执行槽位 3 的 FaceOcc。即使 PIPNet 报错,也先尝试这个必经阶段,再报告无法完成融合的错误。
- 校验张量,计算响应、分割、几何与最终单帧状态;将点位还原到原图。
- 对该头部执行
observe(),补充持续时间、观测次数和确认标记。 - 所有人头完成四阶段后,槽位 4 的 World 按触发条件进行整图复核及可选裁剪补查。
- 输出同帧标注图、JSON 和各阶段耗时;有调试回调时返回上位机,否则进入板端预览输出。
每次模型运行后,紧凑输出已经复制到可供后处理使用的缓冲区,再卸载模型。这是内存生命周期要求,不能把设备缓冲指针留到卸载之后继续读取。
输入输出约定
| 数据 | 表达方式 | 所属坐标或时间 |
|---|---|---|
| 摄像头图像 | BGR888,紧凑交错字节 | 原图像素 |
| OM 输入 | BGR planar UINT8,由 AIPP 处理颜色与归一化 | 模型输入尺寸 |
| PIPNet 解码坐标 | 98×2 浮点数 | 人脸裁剪归一化坐标,可越界,不强行截断 |
| FaceOcc logits | FP32,256×256 | 与 PIPNet 共用的裁剪坐标 |
报告 points |
98×2 | 原图像素 |
posture.coordinates |
98×2 | 去倾斜、以脸宽归一化的局部坐标 |
observed_monotonic |
单调时钟秒数 | 本进程观察时刻,不能当作 UTC 时间 |
timing.*_ms |
毫秒 | 本轮阶段计时,各阶段可能包含多个人头 |
板端 captured_ 在帧交给处理器时赋值,它不是传感器硬件曝光时间。时序能反映处理器观察到的间隔,但不能直接用来推算采集到显示的全部延迟。
执行次数与多人开销
设本帧有 H 个有效头框,其中 F 个有可分析人脸。头部网络运行一次,YuNet 通常每头一次,PIPNet/U-Net 每个有效人脸各一次。World 若没有符合触发条件的对象则运行零次;否则先共享一次整图结果,再按各头是否缺少充分证据进行裁剪补查。
因此“五阶段”不等于每帧固定五次模型调用。多人时,后四个网络的累计执行成本可能明显增长。报告里的 world_review.calls 比按人头数猜测调用次数更可靠。
完整异常路径
| 失败或缺失条件 | 当前行为 |
|---|---|
| 没检出头 | heads=[];本次关联结果为空,旧轨迹不跨这次漏检保留 |
| 有头无有效人脸 | NO_FACE,不运行 PIPNet/U-Net |
| 找到人脸但裁剪条件不满足 | ROI_INVALID,后续鼻嘴指标缺失 |
| 鼻嘴凸包无效、采样触边 | UNCERTAIN 与 INVALID_REGION,不是整个人脸裁剪错误 |
| 单头推理或张量校验失败 | 该头 INFERENCE_ERROR,释放 NPU 资源,其他头继续 |
| 头部检测等外层异常 | 帧级 status=ERROR,清空跟踪 |
| World 运行异常 | 该头 review.status=error,累计复核错误,原状态和时序不变 |
| 调试客户端断开或租约超时 | 结束独占调试,恢复正常 AI 调度 |
非法输入图像会在 onFrame() 入口提前返回,未必产生新的错误报告。显示端因此必须另有旧帧提示,不能把“没有新结果”解释为持续正常。
同一帧不仅是同一个文件名
显示图、点位、分割和 JSON 必须来自同一次分析。不要在循环中一边读取最新图像、一边读取另一轮已经更新的 JSON。调试协议通过会话、帧号、报告与图像 blob 关联结果;样本保存后还可检查原图 SHA-256。
预览为了保持播放器工作可能重复发送最后一个结果。15 fps 的结果流可以实际每一两秒才产生一次新判断。判断新鲜度应看帧号与时间戳,而不是播放器是否流畅。
本章核对入口
StreamNpuOcclusionProcessor.cpp 的 infer()、setFrame()、onFrame();StreamNpuDebug.cpp 的级联处理与会话管理;FaceOcclusionPolicy.cpp 的 assign()、observe()。资源、协议和保存细节在第十三章继续展开。
