• 利用目标跟踪来提高实时人脸识别处理速度


    简介

    利用 Python 开发,借助 Dlib 库捕获摄像头中的人脸,提取人脸特征,通过计算特征值之间的欧氏距离,来和预存的人脸特征进行对比,判断是否匹配,达到人脸识别的目的;

    最终的的追踪识别效果如图(Face_1 和 Face_2 是检测出来的人脸,Person_1 和 Person_2 是通过目标追踪得到的标识 ID):

    原始人脸实时检测识别逻辑

    在之前的博客里面介绍到如何利用 Dlib 进行实时的人脸识别(https://www.cnblogs.com/AdaminXie/p/9010298.html),但是会遇到 FPS 很低 (FPS 差不多在 5 左右)的问题;

    对于视频流中的某一帧 N,需要进行以下步骤来进行识别:

    1. 对于当前帧进行人脸检测(~0.03s);
    2. 对于检测出的人脸,提取特征描述子(~0.158s);
    3. 对于当前帧中的所有人脸,都要和已知人脸数据库进行遍历比对(~0.003s);
    4. 对于当前帧中的人脸 X,如果比对结果出来发现和已知人脸 Y 的特征描述子的欧氏距离小于 0.4,则认为当前帧中的人脸 X 就是我们认识的 Y;

     

    从耗时可以看出来,步骤一中的人脸检测和步骤三四中的比对都不会太影响到程序性能,但是如果要实时计算特征描述子,就会很吃资源,需要差不多 0.16s 的时间来计算某一张人脸的特征描述子;

    与之对比进行检测(只需要 0.03s)和数据库比对(只需要 0.003s);

    在这个程序(https://github.com/coneypo/Dlib_face_recognition_from_camera/blob/master/face_descriptor_from_camera.py)里面,对捕获到的图像进行实时的特征描述子计算,发现最终出来的界面的 FPS 输出在 5 FPS 左右,会有明显的卡顿;

    原程序中进行人脸识别的伪代码如下:

    while stream.isOpened():
        flag, img_rd = stream.read()
        
        # 1. 对于当前帧进行人脸检测
        faces = detector(img_rd, 0)
        
        # 2. 如果当前帧中出现人脸
        if len(faces) !=0:
            # 3. 对于这些人脸,提取特征描述子,存入一个 list
            for i in range(len(faces)):
                features_camera_list.append(face_reco_model.compute_face_decriptor(img_rd, shape)
                
            # 4. 对于这些人脸,遍历已知人脸数据库
            for i in range(len(faces)):
                # 4.1 比如对于 person_X,和所有已知的人脸进行欧式距离对比
                for j in range(len(features_known_list)):
                    return_e_distance(当前帧中的 person_X 的特征描述子,已知人脸的特征描述子[j])
                # 4.2 对于 person_x,得到一个和已知人脸比较的最小欧式距离,如果 <0.4 则认为 person_x 就是这个已知人脸
                if e_distance_min < 0.4:
                    当前帧中的 person_X 的名字 = 欧式距离 <0.4 的已知人脸的名字

    比如在第 N 帧中有两个人,经过我们的遍历对比,我们知道是 Jack 和 Lucky;

    那么对于视频流中的后续帧(N+1,N+2 帧等等),如果还是有两个人,我们就可以大概率判定他们还是 Jack 和 Lucky,只是在帧中的位置发生了变化(这也是目标追踪能够适用的前提);

    那我们只需要确定帧 N 的两个人,和帧 N+1 中这两个人的对应关系,而不需要对于帧 N+1 再进行一次特征描述子提取,然后再进行遍历比对(因为这样很占用资源);

    这就是目标追踪要做的事情,具体 OT 的介绍可以参考我之前的博文(https://www.cnblogs.com/AdaminXie/p/13560758.html);

    通过目标跟踪来提高 FPS

    OT 的实现逻辑

    所以我们希望能避免掉这个 耗时 0.16s 的特征子提取 的处理工作,事实上通过目标追踪,我们确实不需要再对每一帧都要做做检测+识别;

    只需要对于视频流第一帧/初始帧进行检测+识别,识别出该帧的人脸名字;

    对于后续帧,首先是判断当前帧和上一帧的目标数变化,如果目标数不变,比如上一帧有两个人,那么这一帧也有两个人,我们就可以判定这两个人就是上一帧出现的两个人,所以不需要再进行特征描述子的提取和识别工作;

    取而代之的是需要判断当前帧这两个目标,和上一帧两个目标的关系(通过 https://www.cnblogs.com/AdaminXie/p/13560758.html 介绍的 质心追踪算法 来确定);

    原始方式:

    • 初始帧:检测+识别
    • 后续帧:检测+识别

    引入 OT:

    • 初始帧:检测+识别
    • 后续帧:检测+质心追踪

    目标人脸数不超过一张

    考虑最简单的情况,窗口中出现的人脸至多一张,所以目标数 = 1 或者 0;

    对于这种特殊的情况,如果人脸数发生改变:

    • 0->1,没有人脸到出现人脸,对于这一帧中新出现的人脸进行识别
    • 1->0,人脸消失了,注销人脸,清空储存人脸的 list

    如果人脸数不发生改变,那么初始帧认出来他是 person_X,他就一直是 person_X,后续帧只需要做检测就好了(这里其实质心比对都不需要做了);

    人脸数 <=1 的情况下的实现可以参考我这里的代码:https://github.com/coneypo/Dlib_face_recognition_from_camera/blob/master/face_reco_from_camera_ot_single_person.py

    可以看到 FPS 可以达到 28.31,比之前的 FPS 提高了很多(因为只对人脸出现的第一帧进行检测+识别,后续帧只做了检测);

    目标人脸数多于一张 

    出现的人脸数一旦要大于1,就要利用质心追踪算法来判断前后帧的目标对应关系:

    • last_frame_centroid_list   存储的是上一帧的质心坐标;
    • curret_frame_centroid_list  存储的是当前帧的质心坐标;
    • e_distance_current_frame_face_x_list:对于当前帧中检测出的 face_X,和上一帧中的 face_1, face_2 .. face_n 计算质心欧式距离,得到一个长度为 n 的列表;

    比如对于第 65 帧,已知:

    上一帧中(第 64 帧):

    • last_frame_centroid_list = [[566.5, 163.5], [129.0, 186.5]];
    • last_frame_face_names_list = ['person_1', 'person_2'];

    当前帧中(第 65 帧):

    • current_frame_centroid_list = [[566.5, 163.5], [117.0, 184.0]];

    想得到当前帧(第 65 帧):

    • current_frame_face_names_list = ?

     

    所以对于当前帧中出现的所有人脸(face_1 和 face_2),计算质心得到 current_frame_centroid_list = [[566.5, 163.5], [117.0, 184.0]],

    对于当前帧中的 face_1,和上一帧中的 last_frame_centroid_list[0] = [566.5, 163.5] / last_frame_centroid_list[1] = [129.0, 186.5] 计算欧式距离;

    经过计算得知 face_1 和 last_frame_centroid_list[0] 的欧式距离更小,得到 last_frame_num = 0;

    也就是说:

    • self.current_frame_face_names_list[0] = self.last_frame_face_names_list[1],即 face_1 是 person_1
    • self.current_frame_face_names_list[1] = self.last_frame_face_names_list[0],即 face_2 是 person_2

    得到当前帧的人脸名称 current_frame_names_list = ["person_1", "person_2"];

    def centroid_tracker(self):
            for i in range(len(self.current_frame_centroid_list)):
                e_distance_current_frame_face_x_list = []
                # for face 1 in current_frame, compute e-distance with face 1/2/3/4/... in last frame
                for j in range(len(self.last_frame_centroid_list)):
                    self.last_current_frame_centroid_e_distance = self.return_euclidean_distance(
                        self.current_frame_centroid_list[i], self.last_frame_centroid_list[j])
    
                    e_distance_current_frame_person_x_list.append(
                        self.last_current_frame_centroid_e_distance)
    
                last_frame_num = e_distance_current_frame_person_x_list.index(
                    min(e_distance_current_frame_face_x_list))
                self.current_frame_face_names_list[i] = self.last_frame_face_names_list[last_frame_num]

    考虑如下情况,分别是第 57/58 帧:

    第 57 帧中,检测出 face_1 和 face_2,face_1 在右边,face_2 在左边,face_1 是 person_1, face_2 是 person_2;

    current_frame_centroid_list = [[597.0, 155.0], [169.0, 169.0]];

    current_frame_face_names_list = ['person_1', 'person_2'];

    即右边的 [597.0, 155.0] 的 face_1 是 person_1;

    左边的 [169.0, 169.0] 的 face_2 是 person_2;

      

    在第 58 帧的时候,先进行人脸检测,检测出来两个人脸 face_1 和 face_2,不过 face_1 在左边,face_2 在右边;

    (第 57 帧)last_frame_centroid_list = [[597.0, 155.0], [169.0, 169.0]];

    (第 58 帧)current_frame_centroid_list = [[169.0, 169.0], [589.5, 163.5]];

    这时候对于当前帧的 face_1 ([169.0, 169.0]),和上一帧的两个人脸进行质心的欧氏距离对比,得到和 last_frame_centroid_list[1] 更近一点,也就是说:

    • self.current_frame_face_names_list[0] = self.last_frame_face_names_list[1],即 face_1 是 person_2
    • self.current_frame_face_names_list[1] = self.last_frame_face_names_list[0],即 face_2 是 person_1

    得到当前帧的人脸名单:

    current_frame_face_names_list = ['person_1', 'person_2'];

     所以最终的结果可以看到识别的结果,FPS 在 26 左右,比之前的方法提高了很多:

     

    完整实现的代码在 https://github.com/coneypo/Dlib_face_recognition_from_camera/blob/master/face_reco_from_camera_ot_multi_people.py

    # 请尊重他人劳动成果,转载或者使用源码请注明出处:http://www.cnblogs.com/AdaminXie

    # 代码已上传到了我的 GitHub,如果对您有帮助欢迎 Star 支持我下:https://github.com/coneypo/Dlib_face_recognition_from_camera

    # 如有问题请留言或者联系邮箱: coneypo@foxmail.com

  • 相关阅读:
    为什么 ObjectiveC 很难
    PHP开发Windows桌面应用程序实例
    windows下借助InstantRails环境搭建redmine
    php,python,ruby,perl的优缺点?
    Ruby在windows上的eclipse开发环境搭建
    Python的web框架很多,比如Django,webpy等,但是哪一种综合实力最强呢?
    使用JRockit作为Eclipse的Java VM
    Fat Jar Eclipse PlugIn Tutorial
    ECLIPSE插件集合
    Java反编译利器Jad, Jode, Java Decompiler等及其IDE插件
  • 原文地址:https://www.cnblogs.com/AdaminXie/p/13566269.html
Copyright © 2020-2023  润新知