原图像中的68个关键点是怎么获取_手机端人脸关键点如何快准稳

本文详述了移动端人脸关键点检测的优化策略,包括数据集的调优、大模型标注数据集、自动标注技术以及算法设计与迭代。强调了人脸对齐在提升回归效果中的作用,提出了多人脸关键点处理方法,并探讨了模型量化技术,如ristretto方法,以实现模型轻量化。此外,还讨论了人脸关键点应用的难点,如点的抖动性、嘴部关键点不准和遮挡问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 问题定义

问题的定义,决定了后续场景的应用和数据标注。针对手机端的应用,一般会把场景限定为:应用于亚洲人手机前置摄像头自拍视频流中,根据人脸关键点做后续的人脸美颜、贴纸以及人脸识别等。这里对人脸关键点的衡量指标就是快、准、稳达到商用级别。

这个问题的应用:Face alignment is an important component of many computer vision applications, such as face verification(人脸比对) , facial emotion recognition(人脸情感识别) , human computer interaction (人机交互)and facial motion capture(人脸动作捕捉)。

2 人脸数据集调优

2.1 数据集非常重要。

根据实用场景采集相应的原始图片,覆盖不同年龄层、自拍各种角度(侧脸角度、俯仰角、方位角)、不同表情(睁闭眼、张嘴、闭嘴、大笑、抿嘴)等。采集后,需要对人脸数据标注人脸关键点。这里涉及到对人脸关键点的定义和各种不正常人脸的定义。比如遮挡人脸、大侧脸,可以通过标注人员脑补的方式给出标注位以及点的可见型标注。

2.2 看需求标数据。

如果是要模型适应非洲、美洲人脸,就需要采集这类数据。当然数据增强,也未尝不可,但可能会给效果带来偏差。看对最终模型的要求如何。越是强需求模型,最好用采集数据去训练。

2.3 用大模型标注数据集。

这个方法出自facebook研究院文章Data Distillation Towards Omni-Supervised Learning。虽然在标注过程中,一般会加入多轮质检,但质检同学对人脸关键点的点位接受培训地程度不一样,每个人的理解不可能严格一致,那么通过多人质检出来的人脸关键点必然偏差较大,这对点的稳定性必然产生影响。这该如何解决呢?方法就是用离线蒸馏。借助大模型的能力先学习这些人工标注的数据,然后用移动端的小模型去学习大模型的预测结果。这样类似于用大模型去对人工标注结果做了一个平滑,降低了相同点位的抖动行。同时,还可以通过这种方式过滤掉人工标注错误或者很奇葩的人脸数据,使得移动端小模型更容易学习。另外,人工标注的数据毕竟很有限,通过这种机器打标的方式,可以获取更多标注数据。数据多了肯定效果只好不坏的。

2.4 自动标注人脸关键点。

在发现了2-3这样的结论之后,在数据集层面,就带来了一个很大的技巧。如果我们能拿到竞品试用版本的人脸关键点sdk,我们可以用sdk直接刷采集数据,得到预测结果,然后设计移动端模型,学习这个预测结果即可。这个方法的缺点就是竞品sdk所使用的数据质量可能是自己数据集质量的上限,但可以在这个数据集基础上精修,比从头标注要省很多成本。

2.5 通过训练数据的成分优化模型。

1)简单数据启动模型,复杂数据微调模型。一般都是用相对简单一些的训练数据训练模型,然后在加入难样本微调,能让模型更多侧重于大多数正常样本的基础上兼顾下难样本。

2)通过标注各种成分的训练数据,通过多任务学习提升模型效果。这就是Tasks-Constrained DCN的思想了。这也不会给上线带来额外开销。代价就是:标注各种角度姿态、夸张表情、性别、遮挡不可见点等。另外,对于不可见点的loss不回传也是一种优化。

3 人脸关键点移动端算法策略的设计和迭代调优

回归人脸关键点的方法很多。要根据自己的需求去设计特定的策略。针对视频流,一般的做法是先进行人脸检测,再启动人脸关键点网络跟踪策略。人脸关键点网络跟踪策略就是根据上一帧的关键点结果作为当前帧可能存在的人脸区域。同时会设计一个人脸判断二分类模型,来判断人脸区域是否有人脸,一旦因为用户动作幅度太大,导致人脸丢失,就启动人脸检测模型。针对人脸关键点回归网络的优化,这里有几个共性的优化点:

3.1 人脸对齐,对回归效果作用很大。

1)这里先回答一个问题:做人脸关键点就是为了通过人脸关键点做人脸对齐,以便优化后续的人脸识别,现在怎么变成了通过人脸对齐来优化人脸关键点了呢?(鸡生蛋、蛋生鸡的问题)。这里有篇论文证实了是可以做到的。2017年的Deep Alignment Networks,针对一幅图像,采用级联网络的方式,利用前一级网络输出的关键点通过放射变换转正输入图像中的人脸,再输入给下一级回归网络,实验证明这样会提升回归的效果。

2)接下来回答为什么这样做?我们知道标注数据中一般会有很多旋转的人脸,虽然说只要标注数据中有这些旋转人脸,cnn肯定能够回归出来效果。但不可否认的是,人脸旋转角度过大,增加了问题的难度,不利用训练出稳定性很高的模型。而这些旋转的人脸数据是可以通过仿射变换,把这些人脸矫正对齐的。这样可以降低训练集难度,能较大地提升回归模型的效果。实验证明,通过对人脸进行矫正对齐后,再做小角度(比如10度)的扰动,回归出的点更加稳定。(这里补充:人脸对齐在只解决了训练数据集中方位角旋转的问题,就能获得很大的效果提升,那么类似地,在俯仰角、侧滚角以及夸张表情等问题上有很大的提升空间)

3)那么,怎么做?Deep Alignment Networks给出了一种实现方法,但这种级连网络的方法,肯定不满足移动端小模型的需求。所以如何获取当前帧的一个差不多的人脸关键点结果是一个问题。其实很简单的。因为我们是视频流,实际场景中视频流的帧率很高,所以完全可以拿前一帧的关键点结果作为当前帧的粗略结果(类似于DAN第一级网络的输出结果)。这样,就可以拿前一帧关键点对当前输入帧进行人脸对齐,而且基本不会增加任何时间消耗,只有一步放射变换耗点时间。

4)但问题又来了,如果用户的人脸滑动很快的情况下,当前帧的人脸就会滑出前一帧关键点的对应位置,此时怎么办呢?这就要从算法策略上去考虑。可以给关键点网络加上人脸判别的二分类分支,共享关键点网络特征,不会有太大时间消耗。一旦判别不是人脸,就启动人脸检测网络

5)用什么方式进行放射变换?这里有不少方法。常见的是利用三点对齐,比如人眼、鼻子三点,对齐到平均图上的三点,得到放射矩阵,然后对整个人脸进行对齐。除此之外,还有二点对齐,即利用两眼中心点和两嘴角中心点,进行对齐。这里最好的对齐方式,是通过多点对齐策略,多点对齐参照普氏分析法,就是利用最小二乘法得到形状A到形状B的仿射变换。

6)普氏分析法得到放射变换效果好的原因:在DAN中没有不采用仿射变换矩阵,是为了防止局部畸变;放射变换,如果精度不准,就会导致脸部对齐不准;所以点数越多,脸部特征对齐的效果越好。

3.2 移动端人脸关键点回归网络的设计调优

1) 更多的数据。

轻量网络的特征提取能力、泛化能力都不如深度网络,所以相对于深度网络,轻量化小型网络更加依赖数据,数据驱动效果的作用更明显。所以,如果为了获取强需求的应用模型,需要充足的数据,不要让数据成为瓶劲。而大量数据获取的技巧在上文有论述。

2)网络设计中比较精细的技巧在shufflenetv2中有相关论述。

(1)卷积层的输入和输出channels相同时,MAC(内存访问)最少。这一般就是不得不降采样的时候,才会不同。

(2)过多的分组卷积,会增加MAC。mobilenet类网络使用了大量的depthwise分组卷积。

(3)网络的碎片化(如shotcut/bottleneck/FPN/goolenet的多路径结构)影响并行度, 主要是运算快的分支模块需要等待运算慢的分支模型执行完成才能继续执行。

(4)逐像素级别的操作如eltwise,会消耗很多时间,慎用。

(5)用直接指标(例如速度)替换间接指标(例如 FLOPs)。

3)网络设计中比较粗略的技巧:

(1)遵照第5条。不看flops,只结合自己的inference引擎,看真机测试时间,给多少时间预算就设计多大网络。面向实用级别的设计,才能不踩坑。

(2)选择一个参考网络作为前提来设计,并根据(1)的时间预算来裁剪。裁剪的原则,就是要保证整个参考网络的均衡,比如mobilenetv1的通道裁剪一半,比如网络输入图像分辨率降低一半的时候,网络深度要降低。遵照大分辨率大主干网络、小分辨率小主干网络的思想。比如,关键点网络结构可以参照DAN,并把最新的网络子模块全部加入。

(3)设计完了之后,根据shufflenetv2中所说,来对网络设计进行微调。以便网络最高效。

(4)在网络不够深的小网络中,没有必要使用shotcut。shotcut就是因为网络太深,如resnet,导致回传梯度弥散给设计的技巧。shotcut这种多路径结构影响并行度。

(5)在网络浅层,不能下采样的太快,导致输入信息迅速衰减,这样会导致后续网络怎么设计都不会有好的效果。要根据时间预算,尽量让浅层网络把输入图像的信息充分传递到深层网络。

(6)分组卷积。评测出网络耗时的卷积,用depthwise卷积代替。

(7)分解卷积。用13和31来代替3*3,节省参数量。大量提升速度,节省时间。

4)网络设计中结合人脸问题本身的技巧:

(1)根据face++ DCN的文章,设计了级连cnn回归。就是根据点的位置和稠密程度,将任务进行拆解。拆解方式有脸的内部点和外部点分别训练或者将loss_weight调整权重训练;根据一级点,扣出眼睛、鼻子、嘴巴,在较大分辨率上进行稠密点的回归定位。这种方法缺点是脸部块之间失去约束,导致脸部块整体抖动。所以另一个思路,是根据一级点的结果,将内部part合二为一,而且将眼睛、鼻子、嘴巴等部件更集中于感兴趣区,用一个大点的网络去回归,效果更好。尤其是抖动性方面,主要是结合了上下文信息,提升了点与点之间的约束。

(2)上文说到把人脸对齐到平均脸,同时也可以把关键点去中心化,只回归与平均脸的距离。

(3)对于遮挡、大姿态等问题。加入多任务学习,提升效果。遮挡通过指导标注,脑补,让模型学习这种脑补;大姿态中的人脸姿态角通过对齐,基本能解决;但对于大笑、大侧脸等也是只能通过指导标注,脑补,让模型也脑补的方法了。

(4)在轻量化模型设计中,有个经验是:一份时间,一份效果,哪里点不准,就把时间(预算)加到哪里去;而挤时间的方法有:新网络depthwise结构、定点化(8bit/16bit)等来挤时间。

(5)如何支持360度旋转的问题。如果手机旋转,可以用手机系统获取的角度,恢复转正;如果是人脸自动旋转很大角度呢?360度,在前一个阶段,人脸检测的时候加入五点,根据五点转正人脸。

(6)其他方法如DAN的heatmap方法,比较耗时;LK光流法:用LK光流跟踪上一帧的点位和当前帧的预测结果做融合,提升点的稳定性,提效不大。

(7)旷世实时的8000人脸关键点技术?

"3D face reconstruction and face alignment tasks are gradually combined into one task: 3D dense face alignment."

"Heatmap regression has became one of the mainstream approaches to localize facial landmarks."

5)对浮点模型剪枝和量化

在训练好浮点模型之后,为了提升模型的轻量化程度,往往对模型进行裁剪或者量化。模型裁剪的方法有不少,如选择模型的某些层,设置裁剪率,自动剪枝;二值权重网络/三值权重网络等方法。其中比较好的一种方法是ristretto(浓caffe的意思)量化方法,restretto的最强解释

这个git里面说了很多理论,这里摘抄下其使用方法:

(1)首先安装Ristretto,它和Caffe是很兼容的。

(2)一般的网络如SqueezeNet在32位和16位动态定点上表现良好,但是我们可以进一步缩小位宽。参数压缩和网络准确性之间有一个折衷。Ristretto工具可以自动为网络的每个部分找到合适的位宽:

./build/tools/ristretto quantize        # 工具
    --model=models/SqueezeNet/train_val.prototxt    # 全精度网络模型
    --weights=models/SqueezeNet/squeezenet_v1.0.caffemodel  #全精度网络权重
    --model_quantized=models/SqueezeNet/RistrettoDemo/quantized.prototxt  # 自动生成的量化网络模型文件 
    --trimming_mode=dynamic_fixed_point  # 量化类型 动态定点
    --gpu=0 
    --iterations=2000 
    --error_margin=3

这个脚本将量化SqueezeNet模型。 你会看到飞现的信息,Ristretto以不同字宽测试量化模型。

I0626 16:56:25.035650 14319 quantization.cpp:260] Network accuracy analysis for
    I0626 16:56:25.035667 14319 quantization.cpp:261] Convolutional (CONV) and fully
    I0626 16:56:25.035681 14319 quantization.cpp:262] connected (FC) layers.
    I0626 16:56:25.035693 14319 quantization.cpp:263] Baseline 32bit float: 0.5768
    I0626 16:56:25.035715 14319 quantization.cpp:264] Dynamic fixed point CONV
    I0626 16:56:25.035728 14319 quantization.cpp:265] weights: 
    I0626 16:56:25.035740 14319 quantization.cpp:267] 16bit: 0.557159
    I0626 16:56:25.035761 14319 quantization.cpp:267] 8bit:  0.555959
    I0626 16:56:25.035781 14319 quantization.cpp:267] 4bit:  0.00568
    I0626 16:56:25.035802 14319 quantization.cpp:270] Dynamic fixed point FC
    I0626 16:56:25.035815 14319 quantization.cpp:271] weights: 
    I0626 16:56:25.035828 14319 quantization.cpp:273] 16bit: 0.5768
    I0626 16:56:25.035848 14319 quantization.cpp:273] 8bit:  0.5768
    I0626 16:56:25.035868 14319 quantization.cpp:273] 4bit:  0.5768
    I0626 16:56:25.035888 14319 quantization.cpp:273] 2bit:  0.5768
    I0626 16:56:25.035909 14319 quantization.cpp:273] 1bit:  0.5768
    I0626 16:56:25.035938 14319 quantization.cpp:275] Dynamic fixed point layer
    I0626 16:56:25.035959 14319 quantization.cpp:276] activations:
    I0626 16:56:25.035979 14319 quantization.cpp:278] 16bit: 0.57578
    I0626 16:56:25.036012 14319 quantization.cpp:278] 8bit:  0.57058
    I0626 16:56:25.036051 14319 quantization.cpp:278] 4bit:  0.0405805
    I0626 16:56:25.036073 14319 quantization.cpp:281] Dynamic fixed point net:
    I0626 16:56:25.036087 14319 quantization.cpp:282] 8bit CONV weights,
    I0626 16:56:25.036100 14319 quantization.cpp:283] 1bit FC weights,
    I0626 16:56:25.036113 14319 quantization.cpp:284] 8bit layer activations:
    I0626 16:56:25.036126 14319 quantization.cpp:285] Accuracy: 0.5516
    I0626 16:56:25.036141 14319 quantization.cpp:286] Please fine-tune.

分析表明,卷积层的激活和参数都可以降低到8位,top-1精度下降小于3%。 由于SqueezeNet不包含全连接层,因此可以忽略该层类型的量化结果。 最后,该工具同时量化所有考虑的网络部分。 结果表明,8位SqueezeNet具有55.16%的top-1精度(与57.68%的基准相比)。 为了改善这些结果,我们将在下一步中对网络进行微调。

(3)上一步将 32位浮点 SqueezeNet 量化为 8位固定点, 并生成相应的量化网络描述文件(models/SqueezeNet/RistrettoDemo/quantized.prototxt)。 现在我们可以微调浓缩的网络,尽可能多地恢复原始的准确度。 在微调期间,Ristretto会保持一组高精度的 权重。 对于每个训练batch,这些32位浮点权重 随机 四舍五入为 8位固定点。 然后将8位参数 用于前向 和 后向传播,最后将 权重更新 应用于 高精度权重。 微调程序可以用传统的caffe工具 ./build/tools/caffe train 来完成。 只需启动以下脚本:

./examples/ristretto/01_finetune_squeezenet.sh
//内容
#!/usr/bin/env sh
# finetune 微调

SOLVER="../../models/SqueezeNet/RistrettoDemo/solver_finetune.prototxt"   # 微调求解器
WEIGHTS="../../models/SqueezeNet/squeezenet_v1.0.caffemodel"              # 原始 全精度权重

./build/tools/caffe train 
    --solver=$SOLVER 
    --weights=$WEIGHTS

经过1200次微调迭代(Tesla K-40 GPU〜5小时), batch大小为32 * 32, 压缩后的SqueezeNet将具有57%左右的top-1验证精度。 微调参数位于models/SqueezeNet/RistrettoDemo/squeezenet_iter_1200.caffemodel。 总而言之,您成功地将SqueezeNet缩减为8位动态定点,精度损失低于1%。 请注意,通过改进数字格式(即对网络的不同部分 选择整数 和 分数长度),可以获得稍好的最终结果。

(4)用这个量化的caffemodel和配置文件,去测试集合上验证下效果,如果没问题,就是量化成功了。

(5)原理是什么?

(5.1)这里有一段辅助理解“在微调期间,Ristretto会保持一组高精度的 权重。 对于每个训练batch,这些32位浮点权重 随机 四舍五入为 8位固定点。 然后将8位参数 用于前向 和 后向传播,最后将 权重更新 应用于 高精度权重。”

(5.2)量化finetune或者推断中,将每层的输入、参数、输出都根据量化参数,定点化表示;利用这些定点表示的数据进行矩阵计算;如果计算中出现超过表示的最大值或者最小值,那么就截断处理,这里的最大值和最小值是根据量化参数得出,量化参数是利用本身工具在大量训练数据集上统计得到,所以基本可以保证这种截断处理不会带来太大的精度损失。(个人理解,不一定对)

(5.3)看原文中动态固定点量化的代码(见下文),可以大致了解它的基本原理:

(5.3.1)根据量化参数小数位、最大bit位(比如8位)得到当前浮点数据(输入层数据、参数数据、输出层数据)被表示的最大值和最小值,利用这个范围去截断当前浮点数据;

(5.3.2)动态固定点量化的操作步骤:将截断后的浮点数据根据小数位,放大2的小数位的次方(也就是将浮点数左移动);之后round,把左移小数位后的浮点数的小数位去掉(这些小数位对效果影响不大了);再将round后的定点数,根据小数位缩小回原来的量级。这种操作对每个层的输入数据、参数数据、输出数据都可以执行。这就实现了这种方法的训练。推断的时候,根据小数位把这个带有固定小数位数的数据,放大到整数空间,用8bit或者16bit去计算即可。那么所有的输入、参数、输出都是8bit/16bit的数据,利用硬件处理起来就非常方便。这段文字标粗是为了强调这段文字是这种量化方法思想的精华,文字标述地有点拗口,也可以通过以下代码去体会它的思想

// 动态固定点方式量化数据============================  
template <typename Dtype>
void BaseRistrettoLayer<Dtype>::Trim2FixedPoint_cpu(
      Dtype* data,          // 数据 起始指针
      const int cnt,        // 数量
      const int bit_width,  // 量化总位宽
      const int rounding,   // 取整策略
      int fl)               // 小数位位宽
{
  for (int index = 0; index < cnt; ++index) 
  {
    // Saturate data 饱和数处理
// 例如 0 1 1 0 1 1 0 1 , bit_width= 8, fl = 2
// 最高位符号位,所以余下的数为 1 1 0 1 1 0 1 = 109D (假设全部作为整数位)
// 实际小时位有2位,所以小数点需要向前移动 2位
// 所以实际表示的数为 2^0 * 109 * 2(-2) = 27.25
// 所以 总位宽 bit_width 小数位长度 fl
// 表示的最大数为  (2^(bit_width-1) - 1)/(2^fl) = (2^(bit_width - 1) - 1)*(2^(-fl))
// 最小的数为 -1 * (2^(bit_width - 1) - 1)*(2^(-fl))
    Dtype max_data = (pow(2, bit_width - 1) - 1) * pow(2, -fl);// 最大数
    Dtype min_data = -pow(2, bit_width - 1) * pow(2, -fl);// 最小数
    
    // 首先数据包和处理,比最大值小,比最小值大
    data[index] = std::max(std::min(data[index], max_data), min_data);
 
    // Round data
	 data[index] *= pow(2, fl);// 按小数位乘方系数 放大或者缩小 
    //data[index] /= pow(2, -fl);//   27.25125 * 2^2----> 109.005
    // 放大后再取整================== 109.005 ----> 109/110
    switch (rounding) 
	{
     // 最近偶数(NEAREST)
    case QuantizationParameter_Rounding_NEAREST:
      data[index] = round(data[index]);
      break;
    
    // 随机舍入(STOCHASTIC) 
    case QuantizationParameter_Rounding_STOCHASTIC:
      data[index] = floor(data[index] + RandUniform_cpu());
      break;
    default:
      break;
    }
	// 取整后再缩小 109/110 ----> 109  / 2^2  =  27.25
    //data[index] *= pow(2, -fl);
    data[index] /= pow(2, fl);// 相当于去除了超过小数位的部分============
  }
}

(6) ristretto量化经验总结和技巧

(6.1)int16量化相对比较容易成功,一般训练数据不是太难拟合的话,都是能够恢复到浮点模型的精度的。int8量化相对比较难以量化成功,即恢复到浮点的精度。

(6.2)在int8量化中,每层算出的尾数基本都大于4的情况下,量化一般都能成功;小于4的时候(对应的最大量化参数大于8),容易失败。

(6.2)压参数到合理范围:

降低训练数据的拟合难度;

精调正则化参数weight_decay,或者针对某层调整weight_decay;

分析每层浮点参数,找到参数比较大(int8量化参数超过8的话,需要特别关注)的层,再找出这些层中参数比较大的channel,然后针对这个channel要么把参数强行按比例降到8以下或者直接设置为0,对于个别channel出问题是有效的,如果对于很多channel都是这样,就得重新训练浮点模型了;

量化网络最后一层用浮点层,降低抖动性。

(6.3)对于普通卷积层一般不太会出现参数超过8的现象,超过8的现象出现在depthwise和bn联合使用的时候。往往depthwise层的参数还是合理的,但是一旦把bn吸完,就会出现大参数。出现的原因主要是:depthwise层是分组卷积,每组只有1个channel,相当于逐通道卷积,通道与通道之间没有信息交互,这样就会导致卷积后的输出数据方差很小。而紧接在其后的bn层,去计算这个输出层的均值和方差。在吸取bn参数,整个到上一层卷积的时候,会除以这个方差,导致计算得到的新参数变大(公式推导如下)。这是weight_decay这些正则方法无法触及的。所以随着网络训练,这种现象存在是很正常的。

卷积层:y_conv = w * x_conv + b

bn层:y_bn = [gama * ( x_bn - mean)] / var + beta

因为x_bn = y_conv,所以:

y_ bn = [gama * ( y_conv - mean)] / var + beta

= [gama * ( w * x_conv + b - mean)] / var + beta

= [gama * w] / var * x_conv + [gama * ( b - mean)] / var + beta

令 w_new = [gama * w] / var , b_new = [gama * ( b - mean)] / var + beta, y_bn =y_new, x_conv=x_new

则 y_new = w_new * x_new + b_new

通过这个式子,得到吸取bn后的新的卷积层。

可见,最终被量化的参数w_new = [gama * w] / var , 这是随着var变小而变大的。

所以,去掉参数较大的层的bn操作,也是一种解决思路。

另外一个思路是:将bn操作放在1*1conv之后,道理很简单,不解释了。

(6.4)为什么要用depthwise和bn,为什么吸bn,道理很简单,不解释。为什么不可以把某层bn不吸取,直接在inference的时候,带上呢?这可能是一般的infrence框架认为bn可以整合到上一层的参数中,都不会去实现bn operation,bn的实现比较耗时的原因。

(7)ristretto方法和inference框架的耦合

(7.1)轻量化网络优化得好的话,在于四个方面:数据集、轻量网络设计、量化、inference框架优化,四个方面缺一不可。ristretto量化方法是和inference框架强耦合的。它需要inference框架按照ristretto原理进行适当修改。另外,infrence方面由于一般会利用上neao指令集等优化方法,所以一般int8量化模型的inference接口和int16量化模型的inference接口是分开写的,以便端上计算资源充分利用。

(7.2)可以学习参照各大公司的公开源码。

(8)多人脸关键点问题

(8.1)一是多人脸跟踪;二是多人脸关键点回归,batch预测。设置最大人脸数目,自拍场景下人脸一般不超过5个。

(8.2)多人脸跟踪状态下,假如出现新人脸,应该怎么办呢?每隔20ms全图检测一次。这会引起抖动,避免抖动的方法是:新进来的id,才用检测位置;原跟踪的人脸仍然引用原跟踪的位置。

(9)上述ristretto方法带来的提升(定性)

(9.1)速度上:如果不进行ristretto方法量化,float32模型在中低端机器上几乎无法运行,int8量化比int16要快30%左右。比如iphone6机型上时间优化到2ms/帧

(9.2)包大小上:由4倍的压缩率。比如由浮点模型的800K到int8的200K模型。

4 人脸关键点的应用难点

人脸关键点往往应用于2D贴纸和美颜美妆。这里有几个难点。

难点之一就是点的抖动性会引起2D贴纸、眼影等的抖动。所以防抖是个很大的难点。一般思路是对训练数据的点进行平滑处理,即蒸馏;二是用后处理的方法,也就是平滑策略,但平滑策略形同鸡肋,平滑大了容易导致点的拖动、有时延;

难点之二是嘴上的关键点往往不准。主要原因是嘴形类似非刚体,变化很大,数据比较难标注;

难点之三是用户只露出不完整脸的时候,由于无法检测到脸或者脸的置信度过低被过滤掉,导致无法上妆。

难点之四,和难点之三类似,就是脸部被部分遮挡时,无法判别,导致人脸继续上装。比如当用手遮挡住一只眼睛,发现手背上有一个眼影的贴纸,给人很假的感觉。

难点之五,只有脸部关键点的情况,很难处理带有脸部之外的美妆美颜贴纸,一旦贴上,立马就觉得很假,因为随着脸转动,脖子贴纸或者衣物贴纸也会转动,而实际上脖子和衣物并没有转动。这里可以通过标注其他部位的关键点来一起解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值