系列编排日期为 2026 年 8 月;正文依据 2026-10-12 核对的实现整理。历史实测保留真实日期,教学数字不代表实测;本次未重新执行板端推理。
返回系列目录 · 本篇为 14 章+4 份附录中的一篇。
为什么连续三帧不等于持续两秒
设备的推理速度随模型加载、多人数量、World 触发及传输变化。用固定视频帧率乘以帧数会错误估算持续时间。当前实现同时要求单调时钟持续时间和最少观测次数,两个条件缺一不可。
时序处理依附于短时头框轨迹,不能把不同人的异常接在一起。轨迹 ID 只是会话内的短时关联标识,不是生物身份,也不是跨进程永久编号。
IoU 关联的具体规则
FaceTracks::assign() 比较当前头框与上一轮轨迹框。时间间隔必须非负且不超过 track_gap,IoU 至少为 0.3 才形成候选。
只有当前框恰好匹配一个旧轨迹,且该旧轨迹没有同时被其他当前框匹配时,才继承 ID 和状态统计。多对一、一对多、没有候选都会新建轨迹。
关联结束后,旧轨迹列表被当前匹配结果替换。因此 track_gap=10 不表示会保留一个漏检的人十秒:若中间已经处理了一帧并没有这个头,该轨迹就已被移除。track_gap 限制的是相邻被处理观测的时间跨度。
状态计数与方向变化
每次 observe() 先读取单帧主状态。如果它不同于轨迹上一次记录的状态,就把起始时间设为当前观察时间,计数归零,然后计入当前观测。
|
因此从 MOUTH_OCCLUSION_SUSPECT 变成 NOSE+MOUTH_OCCLUSION_SUSPECT 也会重计,并非只要都属于“异常”就连续累计。左右侧向方向变化同样重置。
侧向持续后,报告中的 state 显示为 SIDE_*_SUSPECTED;内部轨迹仍按输入候选状态比较,下一次同方向候选不会因显示名称升级而错误重计。
confirmed 的条件
默认异常候选包括 NO_FACE 与 *_OCCLUSION_SUSPECT。SIDE_*_CANDIDATE、POSE 只有在 pose_action=abnormal 时加入异常候选。满足持续条件后 persistence.confirmed=true。
UNCERTAIN、ROI_INVALID、INFERENCE_ERROR 不因停留足够久自动成为遮挡确认。它们需要另外的诊断和业务处理,当前算法不会把不确定升级成肯定。
默认 pose_action=observe 下,侧向形态仍可升级成 SIDE_*_SUSPECTED,side_sustained=true,但 confirmed=false。两个标记必须分别显示。
时间算例
同一头框在单调时刻 0.0、1.2、2.4 秒连续输出遮嘴候选,第三次观测时持续 2.4 秒、次数三,满足默认 hold_seconds=2。
若观测发生在 0.0、0.2、0.4 秒,虽然次数三,持续时间仍不够。若发生在 0.0、3.0 秒,持续时间够而次数只有二,也不能确认。
PC 预览默认 track_gap=2,当前板端默认 10 秒。若两轮间隔为 2.6 秒,PC 默认会重建轨迹,板端默认可能继承,前提是 IoU 唯一匹配且中间没有漏检。板端值较大用于容纳较慢级联,但也会增加相隔较久时误关联的机会。
调参、断流与独立图片
上位机分析直播在同一会话复用处理器,保留时序;参数版本改变时重建或重置相关级联状态,不能让旧阈值下累计的时间继承给新阈值。固定图片重测是独立测试,单张图片不能证明持续异常。
帧级错误会清空轨迹,漏检会移除对应轨迹。断流时应停止输出新的观察并提示旧帧,不能用重复播放的最后一帧不断累加观测次数。
“15 fps 预览、2 秒确认”不代表每次确认有三十次独立推理;实际可能只有三次。应在结果里同时检查 observations 和 duration_seconds。
这还不是完整业务报警状态机
当前代码产生候选、确认标记与标注颜色,没有在此处发送外部报警。若后续产品增加报警,还需要明确定义触发、保持、恢复、冷却、重复上报和故障处理。那些属于待设计的业务层,不能作为当前已经实现的能力写进流程。
测试重点:多人交叉、框抖动、状态切换、左右切换、漏检、超过间隔、时间倒退、热调参以及确认后恢复可见。源码为 FaceOcclusionPolicy.cpp::FaceTracks。
