玩转人脸识别

2,828 阅读6分钟

一、人脸识别发展

细数人脸识别发展史,我们大致可将其分为四个阶段:

  1. 1964~1990年,第一阶段,研究人脸识别面部特征,没有实现自动识别
  2. 1991~1997年,第二阶段,研究人工算法识别
  3. 1998~2014年,第三阶段,主要研究鲁棒性,如光照、姿态
  4. 2015年至今,第四阶段,互联网应用:技术成熟,大面积推广应用

这一技术已经让生活发生翻天覆地变化!

试想,周一早上,你通过人脸打卡机进入办公室,打开美团选好一杯热咖啡,人脸无感支付。然后你想起周末和朋友的快乐时光,习惯性打开facebook,人脸验证通过后,编辑、上传图片一气呵成,facebook也瞬间帮你@到照片中的好友。


(图源:人脸识别原理)

二、基于face_recognition实现人脸识别

如此酷炫的功能,我们也可以借助face_recognition轻松实现!

face_recognition:是世界上最简洁的人脸识别库,你可以使用Python和命令行工具提取、识别、操作人脸。 face_recognition 提供丰富的api(详见)供调用,我们借助python-flask框架,封装接口,搭建一个小型人脸识别服务器。

核心代码如下:

def faceMatch(name):
    if name is None:
        abort(404)
    name = './uploadimg/'+name
    unknown_image = face_recognition.load_image_file(name)
    res = []
    if unknown_image is None:
        res.append(" position fail!")
    else:
        try:
            unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
            WSI_MASK_PATH = './orisample/'  #原始库
            wsi_mask_paths = glob.glob(os.path.join(WSI_MASK_PATH, '*.jpg'))
            for pic in wsi_mask_paths:
                known_image = face_recognition.load_image_file(pic)
                #已知人脸编码
                known_encoding = face_recognition.face_encodings(known_image)[0]
                #待识别图像,与源库比对
                results = face_recognition.compare_faces([known_encoding], unknown_encoding, tolerance = 0.4)
                if results[0] == True:
                    res.append(pic.split('/')[2]+" It's a picture of me!")
                else:
                    res.append(pic.split('/')[2]+" not")
        except:
            res.append(" position fail!!")
    return render_template('facematch.html', url=res, name=res)

代码(源码地址)解读:

  • 导入face_recognition后,调用face_encodings接口获得图片编码
  • 再通过compare_faces接口,依靠算法比对两者相似度,进行预判

效果展示:

以上示例,使用python-flask简单封装了face_recognition接口 完整代码

三、人脸识别原理

注:只对人脸识别应用感兴趣同学,可以跳过此章节

人脸识别,一般可分为四个步骤:

  • 定位人脸
  • 提取脸部特征
  • 脸部特征参数化
  • 比对源库作人脸辨识
3.1 定位人脸

人脸定位应用HOG算法,通过渐变替换像素,获取图片人脸主要特征。

我们首先将图片置为黑白,剔除色彩影响,然后根据像素点周围渐变趋势,用箭头标记:

(图源:人脸识别原理)

重复如此操作,最后得到箭头标记后的图像( HOG算法实现代码 ):

HOG算法中转图像的目的,是忽略图像色彩、明亮度等影响,准确定位到图片中人脸位置,为下一步提取脸部特征做好准备。

3.2 提取脸部特征

定位到人脸位置后,我们会面对一个新问题:即不同视角下(正脸、侧脸)呈现不同?在同一纬度,待识别图和源图皆为正面,那么预测会更加精准。

于是,进行简易的中转,得到待识别图的近似正脸图。

接着,我们需要提取识别图中人脸的特征。就像美术生在素描时,通常会做构图前点位设置。

(图源:CMU的Brandon Amos)

3.3 脸部特征参数化

接下来就是本文重点了!!

每一张待人脸识别的图像,会经由机器转为128位参数化标识。

那么,机器按怎样的规则做图像参数化呢?通过训练一个深度卷积神经网络,进行深度学习和训练,确定图像转128位规则。

训练方式:

  • 导入已知人脸图像,得到#1
  • 加载已知人的另外一张图片,得到#2
  • 加载非已知人的图片,得到#3

我们不断调整算法和参数,使得#1与#2越来越接近、#1与#3越来越疏远,经过百万千万次运算,最终神经网络便能学会最优的图像参数化规则,达到人脸识别目的。

3.4 比对源库作人脸辨识

我们把得到的128位数值,与数据源库比对,找到最相似的,即完成人脸识别。通过一个简单SVM分类器,可以让比对过程,缩短到几毫秒。

四、进阶:视频打码

前有霍某,后有迪某,这可忙坏了热门综艺《披荆斩棘的GG》后期同学,这里也帮后期同学们减压,让程式自动打码吧!

我们选取一小段《披荆斩棘的GG》原视频:

打码后的视频如下:

主要借助ffmpeg、opencv实现,(完整代码来源:Jack Cui)就不用重复造轮子了:

def mask_video(input_video, output_video, mask_path='mask.jpg'):
    # 打码图片
    mask = cv2.imread(mask_path)
    # 读取视频
    cap = cv2.VideoCapture(input_video)
    # 读取视频参数,fps、width、heigth
    CV_CAP_PROP_FPS = 5
    CV_CAP_PROP_FRAME_WIDTH = 3
    CV_CAP_PROP_FRAME_HEIGHT = 4
    v_fps = cap.get(CV_CAP_PROP_FPS)
    v_width = cap.get(CV_CAP_PROP_FRAME_WIDTH)
    v_height = cap.get(CV_CAP_PROP_FRAME_HEIGHT)
    # 设置写视频参数,格式为 mp4
    size = (int(v_width), int(v_height))
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter(output_video, fourcc, v_fps, size)

    # 已知人脸
    known_image = face_recognition.load_image_file("lyd2.png")
    # print(known_image)
    biden_encoding = face_recognition.face_encodings(known_image)[0]
    print(biden_encoding)
    # 读取视频
    cap = cv2.VideoCapture(input_video)
    while(cap.isOpened()):
        ret, frame = cap.read()
        if ret:
            # 检测人脸
            face_locations = face_recognition.face_locations(frame)
            # 检测每一个人脸
            for (top_right_y, top_right_x, left_bottom_y, left_bottom_x) in face_locations:
                unknown_image = frame[top_right_y-50:left_bottom_y +
                                      50, left_bottom_x-50:top_right_x+50]
                if unknown_image is None:
                    break
                try:
                    unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
                except:
                    break
                # 对比结果
                results = face_recognition.compare_faces(
                    [biden_encoding], unknown_encoding)
                # 是李云迪,就打码
                if results[0] == True:
                    mask = cv2.resize(mask, (top_right_x-left_bottom_x, left_bottom_y-top_right_y))
                    frame[top_right_y:left_bottom_y,left_bottom_x:top_right_x] = mask
            # 写入视频
            out.write(frame)
        else:
            break

代码解读:

  • 借助opencv,读取视频流cap = cv2.VideoCapture(input_video),获取每一帧while(cap.isOpened())
  • 针对每一帧做人脸识别face_recognition.compare_faces(),若识别到目标人脸,则对每帧图像做打码cv2.resize()
  • 图像流保存,逐帧写入视频out.write(frame)
  • 新视频是无声的,可以通过ffmpeg转音频,再整合到一起

五、问题与方案

5.1 face_recognition对小孩和亚洲人脸辨识度较低

官方说明是缺少数据源,解决办法是扩大对应的训练库。另外,在特定人脸定位不佳,也可以针对性进行深度学习,以提升识别准确度。

5.2 视频人脸动态变化,导致打码完整度低

视频过长,对应人脸变动较大,在演示中明显发现远视角和低补光下,识别不佳,对应我们逐帧定位人脸大小,自适应打码。若想达到更好效果,人工辅助和矫正也是必不可少的。

5.3 人脸识别商业化

安防、交通、金融、楼宇是较为常见领域,但是其天然互联网属性,可以孕育出更多创造性产品,比如前一阵很火的换脸视频、宠物情绪探测器等等。随着物联网普及,相信人脸识别也会有很多有趣应用落地。

六、总结

本文通过face_recognition为大家演示了人脸识别搭建,同时介绍了背后人脸定位、特征提取、深度学习原理。最后,借助opencv通过逐帧处理,实现视频打码,让《披荆斩棘的GG》中迪某,”码“上消失。

参考资料:

face_recognition开源项目 传送门

Modern Face Recognition with Deep Learning Adam Geitgey

Pytorch 深度学习实战教程 Jack Cui