查看: 177|回复: 0

[教程] 【树莓派+Movidius计算棒】实现一个效果尚可的人脸识别系...

[复制链接]

主题

好友

650

积分

举人

  • TA的每日心情

    2018-11-20 13:41
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2019-2-18 16:24:13 |显示全部楼层
    GIF.gif


    【1】Camera
    1)多线程
    异步的摄像头接口,可以充分利用系统资源,提高识别效率。
    capture_processing 图像捕获线程
    camera 配置:数据源,FPS, Color Space, Frame Size。
    frame 预处理:frame_preprocessor(frame) 可以 resize,flip
    inference 预处理:inference_processor(frame) 可以 resize, tensor, color space, whiten, data type ...
    图像缓存区(自动丢帧):建立图像缓冲区,后边AI应用算法处理不过来时,自动丢帧处理。

    inference_processing AI 推断线程
    默认是一个推断线程。也可以指定起多个推断线程。要处理好多个推断结果的时序问题。
    整个项目总的AI推断接口 inference(img, frame, timestamp)
    返回一个用于展示的推断结果的集合:result_list
    推断结果缓存区(每个推断线程,自有一个推断结果缓存区)

    show_processing 图像结果输出线程
    展示推断结果 draw(result_list, frame, tmsp)
    根据需求也可以有其他的结果数据输出,例如发送到某服务器。send_report(result_list, tmsp)

    2)自动跳针
    图像缓存区,是一个先进先出的队列。如果排队比较长,可以隔帧丢弃,实现跳针。
    排在队列前边的(时间越早)的,有更多机会被丢弃。
    最大程度保证输出图像的时间连续。

    1. if len(frame_list) > 10:
    2.     frame_list = frame_list[::2]
    复制代码

    【2】FaceDetector

    1)人脸识别
    可设置Filter,控制检出人脸的位置,范围,大小,概率等

    接口
    inference(frame)
    返回值
    归一化的 box_list   例如:[{'x':0.1, 'y':0.1, 'w':0.2, 'h':0.2}, ...]

    • OpenCV 的 cascade。常用的有模型:lbpcascade 或 haarcascade 系列。加大最小检出人脸的尺寸,可以提高检出效率。
    OpenCV 模型:

    lbpcascade_frontalface_improved.xml
    haarcascade_frontalface_alt2.xml
    haarcascade_fullbody.xml
    1. # Demo
    2. def opencv_face_detect(img, scale=1.2):
    3.   img_w = image.shape[1]
    4.   img_h = image.shape[0]
    5.   detector = cv2.CascadeClassifier('~/models/haarcascade_frontalface_alt2.xml')
    6.   scaleFactor = 1.1
    7.   minSize = (10, 10)
    8.   rects = detector.detectMultiScale(image, scaleFactor=scaleFactor, minSize=minSize,   minNeighbors=4)
    9.   dets = []
    10.   for left, top, width, height in rects:
    11.     cx = (left + width / 2) / img_w
    12.     cy = (top + height / 2) / img_h
    13.     w = width / img_w * scale
    14.     h = height / img_h * scale
    15.     dets.append([cx,cy,w,h])      
    16.   return dets
    复制代码

    1. $ wget http://dlib.net/files/dlib-19.16.tar.bz2
    2. $ tar xvf dlib-19.16.tar.bz2
    3. $ cd dlib-19.16
    4. $ python setup.py install  --yes DLIB_USE_CUDA
    复制代码
    1. # coding:utf-8
    2. '''
    3. 脸部68个特征点检测
    4. '''
    5. import sys
    6. import dlib
    7. from skimage import io
    8. import cv2

    9. # 加载并初始化检测器
    10. # 非 CNN 模型
    11. detector1 = dlib.get_frontal_face_detector() #和opencv类似
    12. # CNN 模型下载地址http://dlib.net/files/mmod_human_face_detector.dat.bz2
    13. detector2 = dlib.cnn_face_detection_model_v1('../models/dlib/mmod_human_face_detector.dat')
    14. detector = detector2

    15. # 模型下载地址http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
    16. predictor = dlib.shape_predictor('../models/dlib/shape_predictor_68_face_landmarks.dat')

    17. camera = cv2.VideoCapture(0)
    18. if not camera.isOpened():
    19.     print("cannot open camear")
    20.     exit(0)

    21. while True:
    22.     ret,frame = camera.read()
    23.    
    24.     if not ret:
    25.         continue
    26.     frame_new = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
    27.     # 检测脸部
    28.     dets = detector(frame_new, 1)
    29.     print("Number of faces detected: {}".format(len(dets)))
    30.     # 查找脸部位置
    31.     for i, face in enumerate(dets):
    32.         print("Detection {}: Left: {} Top: {} Right: {} Bottom: {} ".format(
    33.             i, face.left(), face.top(), face.right(), face.bottom()))
    34.         # 绘制脸部位置
    35.         cv2.rectangle(frame, (face.left(), face.top()), (face.right(), face.bottom()), (0, 255, 0), 1)
    36.         shape = predictor(frame_new, face)
    37.         # print(shape.part(0),shape.part(1))
    38.         # 绘制特征点
    39.         for i in range(68):
    40.             cv2.circle(frame,(shape.part(i).x,shape.part(i).y),3,(0,0,255),2)
    41.             cv2.putText(frame,str(i),(shape.part(i).x,shape.part(i).y),cv2.FONT_HERSHEY_COMPLEX,0.5,(255,0,0),1)
    42.     cv2.imshow("Camera",frame)

    43.     key = cv2.waitKey(1)
    44.     if key == 27:
    45.         break

    46. cv2.destroyAllWindows()
    复制代码
    1.png

    脸部68个特征点检测


    2.png



    • 其他神经网络:R-CNN、Faster R-CNN、R-FCN 都可以做人脸识别,但需要考虑其效率和效果再选择。


    2)人脸特征点对齐
    检测出的人脸角度、朝向不统一,这会影响到后期的人脸匹配。
    dlib 人脸特征点对齐 参考,进一步做图像处理,统一检出人脸的朝向。
    3.png
    1. # coding: utf-8
    2. import cv2
    3. import dlib
    4. import sys
    5. import numpy as np
    6. import os

    7. # 获取当前路径
    8. current_path = os.getcwd()
    9. # 指定你存放的模型的路径,我使用的是检测68个特征点的那个模型,
    10. # predicter_path = current_path + '/model/shape_predictor_5_face_landmarks.dat'# 检测人脸特征点的模型放在当前文件夹中
    11. predicter_path = current_path + '/model/shape_predictor_68_face_landmarks.dat'
    12. face_file_path = current_path + '/faces/inesta.jpg'# 要使用的图片,图片放在当前文件夹中
    13. print predicter_path
    14. print face_file_path

    15. # 导入人脸检测模型
    16. detector = dlib.get_frontal_face_detector()
    17. # 导入检测人脸特征点的模型
    18. sp = dlib.shape_predictor(predicter_path)

    19. # 读入图片
    20. bgr_img = cv2.imread(face_file_path)
    21. if bgr_img is None:
    22.     print("Sorry, we could not load '{}' as an image".format(face_file_path))
    23.     exit()

    24. # opencv的颜色空间是BGR,需要转为RGB才能用在dlib中
    25. rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB)
    26. # 检测图片中的人脸
    27. dets = detector(rgb_img, 1)
    28. # 检测到的人脸数量
    29. num_faces = len(dets)
    30. if num_faces == 0:
    31.     print("Sorry, there were no faces found in '{}'".format(face_file_path))
    32.     exit()

    33. # 识别人脸特征点,并保存下来
    34. faces = dlib.full_object_detections()
    35. for det in dets:
    36.     faces.append(sp(rgb_img, det))

    37. # 人脸对齐
    38. images = dlib.get_face_chips(rgb_img, faces, size=320)
    39. # 显示计数,按照这个计数创建窗口
    40. image_cnt = 0
    41. # 显示对齐结果
    42. for image in images:
    43.     image_cnt += 1
    44.     cv_rgb_image = np.array(image).astype(np.uint8)# 先转换为numpy数组
    45.     cv_bgr_image = cv2.cvtColor(cv_rgb_image, cv2.COLOR_RGB2BGR)# opencv下颜色空间为bgr,所以从rgb转换为bgr
    46.     cv2.imshow('%s'%(image_cnt), cv_bgr_image)

    47. cv2.waitKey(0)
    48. cv2.destroyAllWindows()
    复制代码

    3)活体检测a.双目活体检测:
    同时实时采集近红外和可见光两种图像,检测是否为真人

    4.png


    通过红外散点和双目图像,还原出图像的“深度”,获得脸部的立体3D模型。

    5.png

    真人
    6.png

    照片

    b.连续性检测,即通过视频流连续检测,判断目标人物"是活的"眨眼、张嘴 做活体识别 7.png

    dlib 识别人脸特征点



    8.png
    人眼纵横比

    摇头检测
    弱:判断人脸是否在摇头。
    强:根据特征点相对位置关系的变化,可判断出是否是一张2D人脸照片。

    4)人脸匹配
    计算128/512维向量的欧式距离,判定是否是一个人。
    9.png

    FaceNet 人脸ID
    FaceNet 参考文章
    FaceNet的好处是,可以在 CUDA 或 Movidius计算棒上运行。
    [FaceNet Github]
    10.png
    新的FaceNet模型是512维的人脸descriptor

    dlib 人脸ID
    dlib 参考文章
    dlib 有CPU版本,和 CUDA 版本。但没办法在Movidius上跑。
    shape_predictor_68_face_landmarks.dat:
    http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
    dlib_face_recognition_resnet_model_v1.dat:
    http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2

    批量欧式距离计算
    欧式距离

    2范数

    需要匹配的人脸较多的时候,矩阵运算(而不是循环)计算欧式距离,可大幅提高效率。
    1.     def find_descriptor(face_db, descriptor):
    2.         temp = face_db.descriptors - descriptor
    3.         e = np.square(temp)
    4.         e = np.sum(e, axis=1)
    5.         # e = np.linalg.norm(temp, ord=2, axis=1, keepdims=True)
    6.         min_diff = e.min()
    7.         # min_diff *= min_diff
    8.         index = np.argmin(e)
    9.         return face_db.infos[index], min_diff
    复制代码
    注意:
    diff 通常是小于1的,如果开 , diff 会变大。训练的时候是有好处的,增加收敛速度。推断的时候就没必要了,反而不好看。所以我在推断的时候,就直接


    5)视频中的人脸截取
    我们在视频中,对一个人,连续截取人脸,与人脸库进行比对。但如何挑选质量最好的图像记录下来呢?人脸挑优问题,通常选清晰和正面朝向的人脸,认为这样会好。要计算图像清晰度,脸部3D朝向等。实现复杂,且实测效果不佳。
    连续比对中,我选去 distance 最小的那个人脸图片,作为挑优的结果。(这么做有个前提,人脸库必须都是清晰且正向的人脸图片)。

    11.png


    6)人脸库比对动态阀值
    如果一个人,我们并不知道他是不是我们人脸库里的人,此时就需要一个阀值来比较他与人脸库的 min_distance。


    12_副本.jpg


    同一个人,站在不同位置,随着远近,截取的人脸图像尺寸有着近大远小。
    大的照片清晰,小的照片不清晰。它们与人脸库的 min_distance 也随着距离增大而变大。

    13_副本.jpg



    如图所示,实测 min_distance 随着与头像尺寸关系,用一次线性关系来表示。
    FACE_THRESHOLD = lambda shape: (-43 / 40000) * min(shape[0], 160) + 0.453
    特别注意,不同规格摄像头,成像质量不一致,镜头焦距不一致。同台阀值参数的设定,需要根据实测值重新计算。
    14.png


    7) 人脸画像:年龄、性别、表情、种族等
    [人脸属性分析--性别、年龄和表情识别] 这里有很多开源的实现
    我们采用的是:https://github.com/BoyuanJiang/Age-Gender-Estimate-TF

    8)人脸聚类
    将人脸映射到128 或 512维的数据空间中,计算彼此之间的距离。距离近的视作一个人,距离远的视作不是一个人,而判定的标准阈值由自己选定,通常是0.6
    人脸聚类

    【3】Face Tracking
    通过检出人脸的时空属性,跟踪人脸位置,确定连续的两次检出是同一个人。
    当 检出率>90% 和 FPS > 10时,才能有比较好的追踪效果。
    1) 时空连续时间关联衰减函数 attenuator( diff_time, k=1)
    15.png


    空间关联函数 :IoU_IoMin(box1, box2)
    16.png



    交并比


    交比最小   
    一些特殊情况,可能会用到 交比最小。

    时空关联函数:relateFace2Face(face1,face2)

    当r小于一个阀值的时候,我们认为在时空关系上是连续的。
    人脸检出率和FPS高时,可以设一个较高的r(如0.4)
    人脸检出率和FPS较低时,r需要设一个较小值(如0.15)否则很容易跟丢。
    这个办法的好处是,计算量非常小,非常的快。缺点是检出率和FPS比较低的时候,时空关联会非常小。容易跟丢了。
    注意:用dlib的单目标追踪技术可以达到更好的追踪效果,计算成本会高些。
    [dlib 目标追踪参考]

    2)Face Profile
    • 用face描述一张检测到的人脸,用 profile 来描述一个人。
    • 根据不同帧,face的时空连续属性,来判定这个脸(face)属于哪个人(profile)

    face

    1. □[face]
    2.   ├─□[box]
    3.   │ ├─ ☞[h]: 0.40
    4.   │ ├─ ☞[w]: 0.30
    5.   │ ├─ ☞[x]: 0.10
    6.   │ └─ ☞[y]: 0.10
    7.   ├─ ☞[category]: 1 # 0:finding... 1:staff  2:visitor
    8.   ├─ ☞[feature]: np.ndarray:(1, 128)
    9.   ├─ ☞[diff]: 0.24
    10.   ├─ ☞[img]: 'cv_img'
    11.   ├─ ☞[name]: '张三'
    12.   └─ ☞[tmsp]: 1534604717.47
    复制代码
    Profile
    1. □[profile]
    2. ├─ ☞[cap_flag]: 1
    3. ├─ ☞[category]: 1
    4. ├─ ☞[color]: (255,0,0)
    5. ├─ ☞[display_name]: '张三'
    6. ├─□[faces]: list(1)
    7. │ └─□[0]
    8. │   ├─□[box]
    9. │   │ ├─ ☞[h]: 0.40
    10. │   │ ├─ ☞[w]: 0.30
    11. │   │ ├─ ☞[x]: 0.10
    12. │   │ └─ ☞[y]: 0.10
    13. │   ├─ ☞[category]: 1
    14. │   ├─ ☞[diff]: 0.24
    15. │   ├─ ☞[feature]: np.ndarray:(1, 128)
    16. │   ├─ ☞[img]: 'cv_img'
    17. │   ├─ ☞[name]: '张三'
    18. │   └─ ☞[tmsp]: 1534605778.75
    19. ├─□[latest_face]
    20. │ ├─□[box]
    21. │ │ ├─ ☞[h]: 0.40
    22. │ │ ├─ ☞[w]: 0.30
    23. │ │ ├─ ☞[x]: 0.10
    24. │ │ └─ ☞[y]: 0.10
    25. │ ├─ ☞[name]: '张三'
    26. │ └─ ☞[tmsp]: 1534605778.75
    27. └─ ☞[uudi]: '96a17322-a2fa-11e8-973a-8c85907905a8'
    复制代码
    3)Tracking Update
    step1:删除 超时 profiles接口:del_timeout_profiles(cur_tmsp)
    说明:删除连续 x 秒(例如:2秒)没有更新跟踪状态的人脸profile。识别率 和 FPS越高,阀值x可以设置的越小,效果越好。
    step2:根据时空连续性,匹配人脸。匹配上了,更新profile:update_profile(profile, face)
    没匹配上,创建profile:create_profile(face)


    作者:_49_
    來源:简书


    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /5 下一条

    手机版|爱板网

    GMT+8, 2019-3-20 18:35 , Processed in 0.154431 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-5   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001056号

    Powered by Discuz!

    返回顶部