前一段时间发现了一个开源的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打包后放到别的机器无法运行)