使用Face_recognition截取人脸与人像比对

Published: 2018-07-07

Tags: Python

本文总阅读量

前一段时间发现了一个开源的Python库Face_recognition(Github),它基于dlib库构建,看介绍蛮不错的,更多的介绍看Github就好

因为需要做一个从图像中裁剪人脸的功能,用未经优化的OpenCV代码检测人脸,受光线角度等因素影响,会经常出现找不到人脸的情况,所以想到用Face_recognition试一试

贝克汉姆

Face_recognition的Python示例很多(https://github.com/ageitgey/face_recognition/tree/master/examples) 比如从摄像头获取图像标注人脸,从已知的人脸数据库中寻找出扫描的人脸,很灵活,Face_recognition的安装教程很多,这里简要记录一下打包及杂七杂八的东西

引入包

import face_recognition
from PIL import Image
import numpy as np
import base64
import time
import json
import cv2

然后是人像裁剪

def base64_to_cv2_img(b64_image):
    '''
    将 Base64 编码的图片转换为 OpenCV 识别的图片
    '''
    encoded_data = b64_image
    nparr = np.fromstring(encoded_data.decode('base64'), np.uint8)
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    return img

def detect_it():
    '''
    使用 face_recognition 截取头像
    '''
    img = base64_to_cv2_img(b64_image)
    cv2.imwrite("origin.png", img)

    image = face_recognition.load_image_file("origin.png")

    face_locations = face_recognition.face_locations(image)

    print("I found {} face(s) in this photograph.".format(len(face_locations)))

    if len(face_locations) == 0:
        return ""

    face_size = 0
    face = (0, 0, 0, 0)
    for face_location in face_locations:

        # 人像在图片的位置
        top, right, bottom, left = face_location
        print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))

        # 如果图像有多张人脸,那么选择截取面积最大的
        if (bottom - top) * (right - left) > face_size:
            face = (top, right, bottom, left)
            face_size = (bottom - top) * (right - left)

    top, right, bottom, left = face
    face_image = image[top-60:bottom+60, left-40:right+40]
    pil_image = Image.fromarray(face_image)
    pil_image.save('detected.png')

    with open("detected.png", "rb") as image_file:
        encoded_string = base64.b64encode(image_file.read())
        return encoded_string

网络传递过来的图像,为了便于调试,先存放在本地,然后再加载,返回的encoded_string就是Base64字符串

这几乎就是全部的代码了

人像比对,更加容易

def compare_face(image01, image02):

    known_image = face_recognition.load_image_file(image01)
    unknown_image = face_recognition.load_image_file(image02)

    known_encoding = face_recognition.face_encodings(known_image)[0]
    unknown_encoding = face_recognition.face_encodings(unknown_image)[0]

    distance = face_recognition.face_distance([known_encoding], unknown_encoding)
    return distance

image01与image02为图像本地路径

如果需要做成api,直接用Flask包裹一下就可以了

from flask import Flask, request, jsonify, make_response, current_app
from gevent.pywsgi import WSGIServer
app = Flask(__name__)

@app.route('/detect', methods=['GET', 'POST', 'OPTIONS'])
@crossdomain(origin='*')
def detect():
    if request.method == 'POST':
        resp_data = request.get_json()
        photo = detect_it(resp_data['b64_image'])
        data = { "b64_image": photo, "message": "success" }
        resp = make_response(jsonify(data))
        return resp

    else:
        return '''请使用Post方式传递数据'''

各种判断都去掉了,方便查看,值得注意的是@crossdomain(origin='*') 这个东西是用来控制跨域访问的,如果前端Ajax直接请求接口,没有这些是不行的。

def after_request(response): 
    response.headers['Access-Control-Allow-Origin'] = '*' 
    response.headers['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS' 
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization' 
    return response

app.after_request(after_request)


def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            else:
                h['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

Windows下打包:

直接使用Pyinstaller打包即可,但是需要包含模型数据。建议不用加-F参数打成独立的exe程序,因为运行的时候还需要解压出来运行,直接文件夹的形式,启动的效率还会高些

安装了Face_cecognition后C:\Python27\Lib\site-packages目录下会有一个face_recognition_models文件夹

把它复制出来放在打包好的目录里就可以了

Centos下运行:

(PIP记得换源,否则模型100M大小安装很慢)

yum install cmake gcc gcc-c++
yum install python-devel opencv opencv-python

pip install face_recognition
pip install --upgrade numpy==1.14.1 --user python 

最后的numpy指定版本安装是因为在人像比对的过程中发现系统自带的版本太低,无法使用,而且系统也不让升级numpy,使用--user python指定用户后可以安装,安装最新版的包后,运行比对还会报错,一个函数参数新版本修改了,所以用了1.14.1版本

2018-07-07,我使用的face_recognition版本为

dlib                             19.13.1  
face-recognition                 1.2.2    
face-recognition-models          0.3.0

待解决Centos下的打包运行(Pyinstaller打包后放到别的机器无法运行)