智绘鸿蒙 · 如 7 而至端侧 AI 能力实战手记

一张大合影里有几张脸

智绘鸿蒙·如 7 而至

前言

需求表述为"统计合影人数",但这句话里有两个未定义项:"一张脸"的判定条件是什么,以及**"人数"是否等于检测框数量**。工程上必须先固定这两项,否则任何准确率数字都没有意义。

本文用 Core Vision Kit 的 faceDetector 做一组可控实验,把这组问题量化。实验对象是一张合成合影——人脸数量、位置、尺寸全部由构造过程给定,因此真值(ground truth)是先验确定的,不存在人工标注带来的误差。

主要结论有三条:其一,返回框描述的是面部区域而非头部区域,宽度约为贴入人脸图块边长的 0.40 倍;其二,存在一个尺寸阈值,落在检出框宽 44~52 像素之间;其三,姿态角 rollAngle 不能用于判定人脸是否倒置。

1 实验对象与符号定义

图像尺寸 1600×1000 像素。以 3 张底片(记为 a、b、c)裁取正方形人脸图块后贴入,共 13 个图块。每个图块记作 T_i=(x_i, y_i, s_i),其中 s_i 为贴入边长(像素)。同一底片重复使用,因为本实验只考察检测而非识别,重复底片不影响计数真值。

13 个图块按条件分为三组:

组别 成员 考察变量
基准组 normal-a/b/c/a2,边长 230~270 无扰动
边界组 edge-cut(右侧越界 30 像素)、second-row(第二排) 出画与排布
姿态组 inverted(贴入前旋转 180°) 倒置
阶梯组 size-180/150/130/110/90/70 尺寸单调下降

检测记作 \hat{F} = {(\text{rect}_j, \text{pose}_j, \text{prob}_j)},其中 \text{rect}_j=(l,t,w,h)。

2 方法与判据

调用形式:

await faceDetector.init();
const faces: faceDetector.Face[] = await faceDetector.detect({ pixelMap: pm });

Face 的字段是 rectposeprobabilitypointsblock?。注意没有 confidenceThreshold 一类可调入参,也没有返回置信度阈值相关信息,因此本实验无法通过调参改变灵敏度。

匹配规则:对每个 T_i 取其中心 c_i,在未被占用的检测结果中选中心曼哈顿距离最小者配对,一对一占用。距离无上限约束——这意味着"错误配对"是可能的,因此下表同时报告中心偏差,偏差过大的命中应视为可疑。

三项指标:命中数(配到检测框的图块数)、漏检数、多余检测数(未被任何图块占用的检测框数)。

3 结果

单次运行,MatePad Pro(HarmonyOS 7 / API 26):

贴入 13 · 检出 10 · 多余 0 · 267ms
图块 贴入边长 判定 检出框 (l,t,w,h) 中心偏差 pose (P/R/Y)
normal-a 260 命中 176,232,108,160 0, 2 11 / 2 / 4
normal-b 240 命中 492,247,88,125 −4, −10 13 / 2 / 2
normal-c 270 命中 816,222,112,162 −3, −2 11 / 4 / 1
normal-a2 230 命中 1124,257,96,135 −3, 0 10 / 3 / 2
edge-cut 250 命中 1456,242,88,127 10, −9 13 / 2 / 0
second-row 200 命中 452,657,88,122 −4, −2 11 / 3 / 2
inverted 180 命中 948,672,80,100 0, −6 10 / 17 / −1
size-180 180 命中 −4, −8 12 / 3 / 2
size-150 150 命中 −3, 0 11 / 6 / −1
size-130 130 命中 464,837,52,70 −5, −3 15 / 7 / −6
size-110 110 漏检
size-90 90 漏检
size-70 70 漏检

4 讨论

4.1 检出框描述的是面部,不是头部

比值 w/s 在各图块上高度一致:108/260=0.415、88/240=0.367、112/270=0.415、96/230=0.417、88/200=0.440、52/130=0.400。均值约 0.41,且 h/w \approx 1.45,明显是一个纵向拉伸的矩形。

工程含义:若把 rect 当作"人头所在区域"去做裁剪或打码,会漏掉头发、耳朵和下巴。做隐私打码时建议按 0.41 反推头部尺度,例如横向扩到贴入图块的 0.75 倍、纵向上移 0.35 倍再画框。本实验未直接验证打码覆盖率,这一步需要真实照片复核。

4.2 尺寸阈值

单调性成立:130 及以上全部命中,110 及以下全部漏检,中间没有抖动。

按 4.1 的比例关系,s=130 对应检出框宽 52 像素,s=110 对应约 44 像素。因此以检出框宽度计,阈值落在 44~52 像素之间。需要强调这是推算:s=110 时模型根本没有返回框,无法直接观察其应有宽度。若要精确定位,应在 110~130 之间以 5 像素步长补测。

换算到真实场景:一张 4000×3000 的合影,人物面部通常占 150~400 像素,全部在阈值之上;但如果先把图缩到 1080 宽再送检,面部可能落到 40~100 像素,接近或低于阈值。这解释了"原图能数对、缩图就少人"的常见现象,也意味着:宁可多花一次检测时间,也不要先降分辨率。

4.3 姿态角的解释边界

倒置图块(贴入前旋转 180°)被正常检出,其 pose 为 pitch=10、roll=17、yaw=−1,而正立图块的 roll 在 2~7 之间。

也就是说:模型确实感知到了异常(roll 从个位数升到 17),但没有把 180° 报成 180°。若按"roll 接近 180 即为倒置"写判据,会完全失效。合理解释是该角度定义在有界区间内(各正立样本 roll 均为小正数,无法据此区分是 [−90,90] 还是 [0,180]),但本实验样本不足以确定其定义域,只能得出一个可执行的结论:rollAngle 可用作"姿态异常"的弱信号,不可用作朝向的度量。

4.4 计数场景可以直接用框数

本次多余检测数为 0,即没有出现把背景纹理、肩膀、重复底片误判为额外人脸的情况。对"数人数"这个具体需求,faces.length 在尺寸达标时是可靠统计量,不需要额外的非极大值抑制或去重逻辑。

5 内部效度

必须交代本实验不能外推的部分:

  1. 图像是合成的,人脸来自 3 张底片的重复贴入,背景是程序生成的斜纹,不存在真实合影中的相互遮挡、侧脸、逆光与景深虚化。真实照片的漏检率只会更高。
  2. 底片是正脸证件照,姿态极小;阶梯组因此测到的是"干净正脸"的阈值,不是普通场景的阈值。
  3. 每个条件只测一次,未评估同一张图的重复稳定性。
  4. 阈值区间由比例推算得到,不是直接观测。

真机数据

项目 实测值
设备 MatePad Pro(HarmonyOS 7 / API 26,序列号略)
图像 1600×1000,贴入 13 个人脸图块
检出 / 漏检 / 误检 10 / 3 / 0
检测耗时 267ms(含 init 的首次运行另计)
检出框宽 / 贴入边长 0.367 ~ 0.440,均值约 0.41
中心偏差
尺寸阈值 贴入 130 命中、110 漏检;按框宽计约 44~52 像素

逐图块判定结果,命中行给出检出框与中心偏差

尺寸阶梯:130 命中,110 起连续三档漏检

总结

这套能力适合"人数下限估计 + 人脸定位"类需求:合影自动裁剪、会议签到人数核对、相册按人脸聚类的前置定位、直播连麦人数提示。它给的是框和姿态,不给身份,因此凡是涉及"是谁"的需求,还要叠加 faceComparator

不适合精确计数。真实合影里的相互遮挡会直接压低召回,而接口不提供任何灵敏度旋钮,你无法用调参换回漏掉的人。人数敏感的场景(比如按人头计费)应当把检测结果当作下限,再让人工确认。

💡 一句话faceDetector 的框只覆盖面部(约为头部图块宽度的 0.41 倍),且贴入边长低于约 110 像素就会整张漏检——数人之前,先别缩图。

参考文献

  1. 人脸检测(开发指南):https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/core-vision-face-detector
  2. faceDetector(人脸检测)API 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/core-vision-face-detector-api
  3. Core Vision Kit 简介:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/core-vision-introduction
  4. Core Vision Kit 错误码:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/errorcode-core-vision