人脸关键点检测的思考

目录

行业标准:

官方数据集:

方法选型:

(Coordinate VS Heatmap , hrnet VS pfld):

我的感受:

如何区分关键点的性能:

一点思考,感触:

人脸检测和人脸关键点检测的适配问题:

训练过程可视化效果:

做到极致:

模块化:

All in one:

另辟蹊径:

 高斯滤波方差的思考:

 人脸关键点+关键点遮挡情况:

五官badcase针对性解决:

人脸姿态估计:

应用场合:

基本思路:

我的尝试:

euler尺度不变性,稳定性思考:

如何防抖:

灵机一动:

人脸跟踪:

论端到端和累积误差:

何谓境界:

 cv全栈:

路在何方:

must 有用的参考资料:


 凡我不能创造,我就不能理解。 ——What I cannot create,I don't understand.            --费曼

行业标准:

face++,商汤,字节,这些业界标准差异?(人脸106关键点方向)

(1)face++的鼻子中间的关键点更偏高,商汤,字节的更偏低 。

(2)face++的轮廓起点是耳朵前,商汤,字节是耳朵根

(3)唇峰都相对更靠上一些

(4)关键点错误都错的雷同

          face++sdk                                商汤sdk                              抖音sdk                           face++服务                        我们的 

官方数据集:

 

wflw(第一个图): 数量:官方98点,9560张

lapa(第二个图): 数量:官方106点,22175张 

感悟,兵不在多而在精,将不在勇而在谋,三千越甲可吞吴,精兵7万

方法选型:

(Coordinate VS Heatmap , hrnet VS pfld):

关键点回归的Ground Truth 的构建问题,主要有两种思路, Coordinate 和 Heatmap, Coordinate 即直接将关键点坐标作为最后网络需要回归的目标,这种情况下可以直接得到每个坐标点的直接位置信息; Heatmap 即将每一类坐标用一个概率图来表示,对图片中的每个像素位置都给一个概率,表示该点属于对应类别关键 点的概率, 比较自然的是,距离关键点位置越近的像素点的概率越接近 1,距离关键点越远的像素点的概率越接近 0, 具体可以通过相应函数进行模拟,如Gaussian 等。

Coordinate 的优点是参数量少,缺点是效果较差,容易训练学习。

Heatmap 的优点是容易训练,准确性高,缺点是参数量大,随着要检测的点的个数呈爆炸性增长。

我的感受:

(1)heatmap思维,解决不了视觉角度误差 。

(2)人脸检测,半人脸,图像边缘人脸,非端到端的featmap解决不了,端到端的featmap可以解决,需要精心设计 网络结构,centernet思路可以,centerface思路不行 coordinate网络合理设计,有可能解决 ,prelu or leaky relu(fc) , not sigmoid(fc)

(3)泛化性,heatmap >> coordinate

如何区分关键点的性能:

(1)贴合度高

好的关键点能够完美地贴合面部五官,即使在大角度或大动作的视频流中抖动,依然能保持稳定。相反若关键点的贴合能力不够则会大大影响产品的体验和应用效果,比如在美妆应用中唇膏和眼线等美妆特效会贴到真实的五官之外让用户一秒变“如花”。

(2)稳定性强

好的关键点不会抖动而是稳稳地贴在面部。如果关键点的稳定性不强,用户在特效应用中为基于人脸的照片或视频加贴纸的时候,就会造成贴纸抖动。

(3)跟踪速度快

好的关键点能实时跟踪每一帧画面,不会造成画面的卡顿、滞后,且人脸快速转头移动时也不会跟丢,相反性能不够好的 SDK 在进行人脸跟踪的时候比较滞后,会引起画面卡顿、占用大量CPU计算资源等情况。

(4)鲁棒性强

好的关键点能在各种极端场景下保持稳定的质量,包括遮挡、大侧脸、运动、恶劣光照等,而鲁棒性差的关键点在大角度的侧脸、遮挡或较差的光照条件下就会丢失或错位。

一点思考,感触:

what's the difference?

小小的眼睛,大大的疑惑

失之毫厘,谬以千里

勿以恶小而为之,勿以善小而不为

一个「像素」引发的血案 小动作能够引发大流行

问题的本质是什么?

动量守恒定律: 物理规律如果具有空间坐标平移不变性则对应地存在一个守恒定律

泛化能力(generalization ability),平移不变性(translation invariant)

Why do deep convolutional networks generalize so poorly to small image transformations?

(1)CNN丢失平移不变性是下采样(Pooling 和 Stride = 2的Conv)引起的。

(2)Max_Pooling其实对于平移不变性有一定的保持作用。

如何解决:

(1)更多的训练数据

(2)更佳的数据增强方法(Random crop)

(3)增大网络输入分辨率,减少累积误差。

人脸检测和人脸关键点检测的适配问题:

先有鸡,先有蛋?

端到端test精度比eval精度低

训练阶段:

(1)基于106关键点groundtruth,生成最小外接矩人脸框(中心点,宽,高)

(2)人脸矩形框中心点x,y,进行10个像素的随机扰动,边长取宽高中最大的边,随机进行(1.05,1.3)的扩大

(3)边界人脸像素补黑边

(4)缩放(112*112)

        randshiftx = np.array([random.randint(-10, 10),random.randint(-10, 10)])
        randshifty = np.array([random.randint(-10, 10),random.randint(-10, 10)])
        xy = np.min(self.landmark, axis=0).astype(np.int32) + randshiftx
        zz = np.max(self.landmark, axis=0).astype(np.int32) + randshifty
        wh = zz - xy + 1

        center = (xy + wh/2).astype(np.int32)
        img = cv2.imread(self.path)
        #print(self.path)
        randnum = random.uniform(1.05, 1.3)
        boxsize = int(np.max(wh)* randnum)
        xy = center - boxsize//2
        x1, y1 = xy
        x2, y2 = xy + boxsize
        height, width, _ = img.shape
        dx = max(0, -x1)
        dy = max(0, -y1)
        x1 = max(0, x1)
        y1 = max(0, y1)

        edx = max(0, x2 - width)
        edy = max(0, y2 - height)
        x2 = min(width, x2)
        y2 = min(height, y2)

        imgT = img[y1:y2, x1:x2]
        if (dx > 0 or dy > 0 or edx > 0 or edy > 0):
            imgT = cv2.copyMakeBorder(imgT, dy, edy, dx, edx, cv2.BORDER_CONSTANT, 0)
        if imgT.shape[0] == 0 or imgT.shape[1] == 0:
            imgTT = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
            for x, y in (self.landmark+0.5).astype(np.int32):
                cv2.circle(imgTT, (x, y), 1, (0, 0, 255))
            cv2.imshow('0', imgTT)
            if cv2.waitKey(0) == 27:
                exit()
        imgT = cv2.resize(imgT, (self.image_size, self.image_size))

测试阶段:

(1)人脸框上边的边-->下移0.25倍高度

(2)人脸框下边的边-->下移0.05倍高度

(3)取最大宽,高的1.25倍

(4)边界像素判断,不够补黑边

(5)缩放(112*112)

            x1, y1, x2, y2 = (box[:4]).astype(np.int32)

            tt = y2-y1
            y1 = int(y1+tt*0.25)
            y2 = int(y2 + tt*0.05)

            w = x2 - x1 + 1
            h = y2 - y1 + 1

            size = int(max([w, h]) * 1.25)
            cx = x1 + w // 2
            cy = y1 + h // 2
            x1 = cx - size // 2
            x2 = x1 + size
            y1 = cy - size // 2
            y2 = y1 + size

            dx = max(0, -x1)
            dy = max(0, -y1)
            x1 = max(0, x1)
            y1 = max(0, y1)

            edx = max(0, x2 - width)
            edy = max(0, y2 - height)
            x2 = min(width, x2)
            y2 = min(height, y2)

            cropped = img[y1:y2, x1:x2]
            if (dx > 0 or dy > 0 or edx > 0 or edy > 0):
                cropped = cv2.copyMakeBorder(cropped, dy, edy, dx, edx, cv2.BORDER_CONSTANT, 0)


            crop_resized = cv2.resize(cropped, (112, 112))

训练过程可视化效果:

做到极致:

内外分明,内外兼修 轮廓(外),眉毛(内),眼睛(内),鼻子(内),嘴巴(内)

模块化:

course to fine,增加更多的关键点,增加额头轮廓线,增加眼睛,嘴巴 更细节的关键点(直接训练,或者,贝塞尔插值)

All in one:

关键点,人脸解析,人脸属性,人脸识别,一个网络多任务学习完成。

机器多,卡多,人多,实验多,大力出奇迹

另辟蹊径:

基于3dmm方法,进行训练数据制造

训练更多的关键点,训练3d关键点

 高斯滤波方差的思考:

 人脸关键点+关键点遮挡情况:

可以得到眼睛,嘴巴是否遮挡

五官badcase针对性解决:

(1)眉毛,短粗,形状不明显,双眉峰,侧脸形变,眉毛头不明显 

(2)眼睛,眯眼,内双,斜眼笑

(3)嘴巴遮挡,张开,闭合,抿嘴,嘟嘴,上嘴唇肥大,薄,下嘴唇肥大,薄,香肠嘴

(4)轮廓,披肩发遮挡,垫下巴,双下巴,侧面脸,遮挡

人脸姿态估计:

  

应用场合:

比如在人脸识别系统的中,姿态估计可以辅助进行输入样本的筛选(一般人脸要相对正脸才进行召回和识别) 疲劳驾驶产品中驾驶员的左顾右盼检测

基本思路:

(1)landmark-based,通过2D标定信息来估计3D姿态信息的算法,如先计算人脸的关键点,然后选取一个参考系 (平均正脸的关键点),计算关键点和参考系的变换矩阵,然后通过迭代优化的算法来估计人脸的姿态 (可参考Opencv中的SolvePnP算法) https://github.com/zuoqing1988/ZQCNN/blob/master/SamplesZQCNN/SampleLnet106/ZQ_HeadPoseEstimation.h

(2)landmark-free,通过数据驱动的方式训练一个回归器,由该回归器对输入人脸的块进行一个直接的预测。 (cnn,gcn)

算法

优点

缺点

landmark-based

1,基于已有的大量关键点标注数据进行训练

2,可获取人脸各部位坐标信息

1,平均正脸的选取本身会引入计算误差

2,各类情况的landmark训练样本严重不平衡,长尾效应。

3,2d->3d的优化算法,也会引入计算误差。选取的landmark少,计算误差大,选取的landmark多,计算速度慢

landmark-free

1,直接回归欧拉角,简洁方便

2,模型设计简单,不需要后处理或者二次算法优化

1,需要提供大量含有姿态信息的训练数据,模型训练难收敛。

2,数据标注困难,大多数情况采用2d->3d标注,少数情况可以使用合成数据

我的尝试:

(1)Dlib68个点中的6个

 landmarks_3D = np.float32([
            [0.0, 0.0, 0.0],             # Nose tip
            [0.0, -330.0, -65.0],        # Chin
            [-225.0, 170.0, -135.0],     # Left eye left corner
            [225.0, 170.0, -135.0],      # Right eye right corner
            [-150.0, -150.0, -125.0],    # Mouth left corner
            [150.0, -150.0, -125.0],      # Mouth right corner
        ]) / 4.5

1000张和face++误差: yaw mae:3.4285 roll mae:1.6264 pitch ame:178.1665

(2)Dlib68个点中的68个

       landmarks_3D = np.float32([
[[-73.393524 -29.801432 -47.667534]
 [-72.77502  -10.949766 -45.909405]
 [-70.53364    7.929818 -44.84258 ]
 [-66.85006   26.07428  -43.141113]
 [-59.790188  42.56439  -38.6353  ]
 [-48.368973  56.48108  -30.750622]
 [-34.1211    67.246994 -18.456453]
 [-17.87541   75.05689   -3.609035]
 [  0.098749  77.06129    0.881698]
 [ 17.477032  74.758446  -5.181201]
 [ 32.648968  66.92902  -19.176563]
 [ 46.372356  56.31139  -30.77057 ]
 [ 57.34348   42.419125 -37.628628]
 [ 64.38848   25.45588  -40.88631 ]
 [ 68.212036   6.990805 -42.28145 ]
 [ 70.486404 -11.666193 -44.142567]
 [ 71.375824 -30.36519  -47.140427]
 [-61.119408 -49.361603 -14.254422]
 [-51.287586 -58.769794  -7.268147]
 [-37.8048   -61.996155  -0.442051]
 [-24.022755 -61.033398   6.606501]
 [-11.635713 -56.68676   11.967398]
 [ 12.056636 -57.391033  12.051204]
 [ 25.106256 -61.902187   7.315098]
 [ 38.33859  -62.777714   1.022953]
 [ 51.191006 -59.302345  -5.349435]
 [ 60.053852 -50.190254 -11.615746]
 [  0.65394  -42.19379   13.380835]
 [  0.804809 -30.993721  21.150852]
 [  0.992204 -19.944595  29.284037]
 [  1.226783  -8.414541  36.94806 ]
 [-14.772472   2.598255  20.132004]
 [ -7.180239   4.751589  23.536684]
 [  0.55592    6.5629    25.944448]
 [  8.272499   4.661005  23.695742]
 [ 15.214351   2.643046  20.858156]
 [-46.04729  -37.471413  -7.037989]
 [-37.674686 -42.73051   -3.021217]
 [-27.883856 -42.711517  -1.353629]
 [-19.648268 -36.75474    0.111088]
 [-28.272964 -35.134495   0.147273]
 [-38.082417 -34.919044  -1.476612]
 [ 19.265867 -37.032307   0.665746]
 [ 27.894192 -43.342445  -0.24766 ]
 [ 37.43753  -43.11082   -1.696435]
 [ 45.170807 -38.086514  -4.894163]
 [ 38.196453 -35.532024  -0.282961]
 [ 28.76499  -35.484287   1.172675]
 [-28.916267  28.612717   2.24031 ]
 [-17.533194  22.172188  15.934335]
 [ -6.68459   19.02905   22.611355]
 [  0.381001  20.721119  23.748438]
 [  8.375443  19.03546   22.721994]
 [ 18.876617  22.39411   15.610679]
 [ 28.794413  28.079924   3.217393]
 [ 19.057573  36.29825   14.987997]
 [  8.956375  39.634575  22.554245]
 [  0.381549  40.395645  23.591625]
 [ -7.428895  39.836407  22.406107]
 [-18.160633  36.6779    15.121907]
 [-24.37749   28.67777    4.785684]
 [ -6.897633  25.475977  20.893742]
 [  0.340663  26.014269  22.220478]
 [  8.444722  25.326199  21.02552 ]
 [ 24.474474  28.323008   5.712776]
 [  8.449166  30.596216  20.67149 ]
 [  0.205322  31.408737  21.90367 ]
 [ -7.198266  30.844875  20.328022]]
        ]) 

1000张和face++误差: yaw mae:7.1058 roll mae:150.4720 pitch ame:20.0818

原因:106点的定义和68点的定义有区别

(3)106点中的14个点,最终方案,效果最佳

landmarks_3D = np.float32([
            [6.825897, 6.760612, 4.402142],  # LEFT_EYEBROW_LEFT, 
            [1.330353, 7.122144, 6.903745],  # LEFT_EYEBROW_RIGHT, 
            [-1.330353, 7.122144, 6.903745],  # RIGHT_EYEBROW_LEFT,
            [-6.825897, 6.760612, 4.402142],  # RIGHT_EYEBROW_RIGHT,
            [5.311432, 5.485328, 3.987654],  # LEFT_EYE_LEFT,
            [1.789930, 5.393625, 4.413414],  # LEFT_EYE_RIGHT,
            [-1.789930, 5.393625, 4.413414],  # RIGHT_EYE_LEFT,
            [-5.311432, 5.485328, 3.987654],  # RIGHT_EYE_RIGHT,
            [2.005628, 1.409845, 6.165652],  # NOSE_LEFT,
            [-2.005628, 1.409845, 6.165652],  # NOSE_RIGHT,
            [2.774015, -2.080775, 5.048531],  # MOUTH_LEFT,
            [-2.774015, -2.080775, 5.048531],  # MOUTH_RIGHT,
            [0.000000, -3.116408, 6.097667],  # LOWER_LIP,
            [0.000000, -7.415691, 4.070434],  # CHIN
        ])

euler尺度不变性,稳定性思考:

 

问题:计算欧拉角是选取的关键点数目越多越好吗?

结论:左博的代码是质量最高,最值得借鉴的 

如何防抖:

1.尽量多的数据

2.添加追踪机制,小范围移动内不做检测

3.对点和检测框都做ROI

灵机一动:

各自为伍+形状约束,思考端到端,全图出结果的好处 假设训练图片112*112,那么最大精度就是1个像素,小数点后其余的值,叫做尺度因子。 groudtruth = (原使crop图的106个x,y)/crop图的长宽 网络结构需要双分支,双106*2,一个负责112*112,一个负责尺度因子。 x,y回归值制作,取整((0-1)的label*112)/122,可以L1,L2,KL,soft cross_entrop x,y尺度因子制作,取余((0-1)的label*112)/122,L1,L2

人脸跟踪:

基于sort,deepsort,kalman滤波,匈牙利算法。

论端到端和累积误差:

人脸检测的准确率 = 人脸检测的准确率 = 0.99

人脸关键点的准确率 = 人脸检测的准确率*人脸关键点的准确率 = 0.99*0.99 = 0.9801

人脸欧拉角的准确率 = 人脸检测的准确率*人脸关键点的准确率*人脸欧拉角的准确率  = 0.99*0.99*0.99 = 0.9702

人脸属性的准确率 = 人脸检测的准确率*人脸关键点的准确率*人脸属性的准确率  = 0.99*0.99*0.99 = 0.9702

何谓境界:

古今之成大事业、大学问者,必经过三种之境界: “昨夜西风凋碧树,独上高楼,望尽天涯路。”此第一境也;(git clone)

“衣带渐宽终不悔,为伊消得人憔悴”,此第二境也;(选择性clone(star,fork),加入自己思想,完善)

“众里寻她千百度,蓦然回首,那人正在灯火阑珊处”,此第三境也。(返璞归真,大智若愚,工业标准,ssd vs mtcnn,关键点的速度限度,书本上没有,靠经验堆)

王国维《人间词话》

 cv全栈:

(1)紧跟前沿 掌握足够多的输入。

(2)注重学习 & 不断实践 有属于自己的思考和严谨的产出。

(3)重视基础知识 & 多做总结 理解清楚,事半功倍。

(4)体系实践 三人行必有我师,向身边的人学习。

路在何方:

must 有用的参考资料:

https://github.com/hpc203/10kinds-light-face-detector-align-recognition https://github.com/zuoqing1988/ZQCNN https://github.com/ShiqiYu/libfacedetection https://github.com/OAID/TengineKit https://github.com/olucurious/Awesome-ARKit https://github.com/google/mediapipe https://github.com/yinguobing/head-pose-estimation Head Pose Estimation using OpenCV and Dlib | LearnOpenCV # https://github.com/yuenshome/yuenshome.github.io/issues/9 https://github.com/qaz734913414/Ncnn_FaceTrack/blob/master/LandmarkTracking.h https://github.com/xiangdeyizhang/FaceTrack_ncnn_HyperFT https://github.com/deepinsight/insightface https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/ https://github.com/AI-performance/embedded-ai.bench https://zhuanlan.zhihu.com/p/48347016 http://www.pris.net.cn/introduction/teacher/dengweihong https://github.com/gjain307/Skin-Disease-Detection.git https://github.com/microsoft/onnxruntime/tree/v1.0.0/onnxruntime/python/tools/quantization

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python人脸关键点检测是指使用Python编程语言进行人脸关键点的定位和检测。人脸关键点通常包括眼睛、鼻子、嘴巴等重要的面部特征点,通过检测这些关键点可以实现人脸识别、表情识别、姿态估计等应用。 在Python中,有多个库可以用于人脸关键点检测,其中比较常用的是dlib和OpenCV。下面是一个使用dlib库进行人脸关键点检测的示例代码: ```python import dlib import cv2 # 加载dlib的人脸检测器和关键点检测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # 读取图像 image = cv2.imread("face.jpg") # 转换为灰度图像 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 使用人脸检测器检测人脸 faces = detector(gray) # 遍历每个检测到的人脸 for face in faces: # 使用关键点检测器检测关键点 landmarks = predictor(gray, face) # 遍历每个关键点,并在图像上绘制出来 for n in range(0, 68): x = landmarks.part(n).x y = landmarks.part(n).y cv2.circle(image, (x, y), 2, (0, 255, 0), -1) # 显示结果图像 cv2.imshow("Face Landmarks", image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 上述代码中,首先使用dlib库加载了人脸检测器和关键点检测器。然后读取待检测的图像,并将其转换为灰度图像。接下来使用人脸检测器检测图像中的人脸,并使用关键点检测器检测每个人脸的关键点。最后,遍历每个关键点,并在图像上绘制出来。 需要注意的是,上述代码中使用的shape_predictor_68_face_landmarks.dat文件是一个已经训练好的模型,可以从dlib官方网站下载。此外,还可以根据具体需求对关键点进行进一步处理和应用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值