背景
上一篇文章,小白教程-人脸识别检测,简单实现了人脸的检测识别,但是那只能作为一个demo玩玩,我们的目标是要有向外提供服务的能力,有向外提供服务的能力,才能落地,赋能业务。那我们应该怎么实现呢?
需求分析
基于公司门禁系统,场景分析下,我们的服务应该提供什么样的能力,才能往外提供服务,分析如下:
- 对目标图片进行识别,图片中是否有人?这人是谁?是我公司的人吗?提供人脸识别接口
- 对图片识别,我们首先是不是应该建立人脸基础库,有了人脸库,才能对目标图片进行识别。那就需要一个员工人脸录入信息的接口,简称:人脸注册
- 如果这个员工离职了,那再次回到公司,通过门禁系统,刷脸识别进入公司,那不是存在安全问题。那怎么处理呢?当员工离职了,我就要对应的删除人脸库这个员工的数据。人脸删除接口
通过以上分析,我们至少,要提供三个接口:
技术调研
我们需要把系统做成web应用,通过http协议提供服务,进行调研发现基于python的web框架有下面几种,可以简单了解下
Django
项目地址: https://github.com/django/django
Python 中最流行的 Web 框架,功能非常全面,像安全认证,URL Routing,模板引擎,ORM,甚至 Admin 管理后台,全部包括。
Flask
项目地址: https://github.com/pallets/flask
也是非常流行的一个 Web 框架,它的特定是轻便,灵活,可定制性强。
用户可以根据自己的需求来添加相应的功能,在保持核心功能简单的同时,实现功能的丰富与扩展,其强大的插件库可以让用户实现个性化的网站定制,开发出功能强大的网站。
Bottle
项目地址: https://github.com/bottlepy/bottle
一个简单高效的遵循 WSGI 的微型 Web 框架。说微型,是因为它只有一个文件,除 Python 标准库外,它不依赖于任何第三方模块。
这个框架使用的比较少,不过源码也很少,如果想读源码的话,从它入手倒是一个不错的选择
Tornado
项目地址: https://github.com/tornadoweb/tornado
Tornado 是一个基于 Python 的 Web 服务框架和异步网络库,通过利用非阻塞网络 I/O, 可以承载成千上万的活动连接。在需要长连接的场景下使用,效果会更好。
Sanic
项目地址: https://github.com/sanic-org/sanic
2016 年 5 月发布的第一个版本,整体表现还是不错的,使用的人也越来越多。
Sanic 是 Python3.7+ Web 服务器和 Web 框架,旨在提高性能。它允许使用 Python3.5 中添加的 async/await 语法,可以使代码有效的避免阻塞从而达到提升响应速度的目的。
FastAPI
项目地址: https://github.com/tiangolo/fastapi
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
该框架鼓励使用 Pydantic 和 OpenAPI (以前称为 Swagger) 进行文档编制,使用 Docker 进行快速开发和部署以及基于 Starlette 框架进行的简单测试。
以上框架都能实现web服务,看个人喜欢,我选择flask框架,原因就是比较轻量
环境安装
使用flask,需要安装flask库
# 安装人脸识别包
# 人脸识别库
pip install -U insightface
# 图片处理的库
pip install opencv-python
#
pip install numpy==1.23
# ONNX Runtime是一个用于高性能运行和推断深度学习模型的开源引擎
pip install onnxruntime
pip install flask
功能实现
http提供的接口,数据传输协议,统一使用application/json
参数说明
名称 | 类型 | 说明 |
---|---|---|
userId | int | 用户ID |
userName | string | 用户姓名 |
imgData | string | 图片数据,base64,使用图片转换 |
人脸注册
用户输入用户名、用户id、用户人脸图片数据,图片转换为base64传输,图片转base64可以自己写代码实现,也可以使用在线工具转换
代码实现
@app.route('/face/add', methods=['POST'])
def faceRegister():
# 获取body里面的数据
personData = request.get_json()
img = base64_cv2(personData['imgData'])
# 获取注册图片是否有人脸,返回的结果是个人脸集合,如果图片上有多张脸,就返回多个结果
faces = model.get(img)
if len(faces) == 0:
# 如果图片未识别出人脸,说明图片没有人脸,或者图片质量有问题
return jsonify({'code': 201, 'msg': '图片未发现人脸,请重新注册'})
elif len(faces) > 1:
# 如果图片中有多个人脸对象,那你无法判断应该用哪一个人脸对象,所以,注册的图片,只能有一张脸
return jsonify({'code': 202, 'msg': '图片中多张人脸,无法判断用哪一张脸'})
# 获取人脸数据,上面已经做了限制,所以要注册的人脸数据faces[0]就行
embedding = faces[0].normed_embedding
# 人脸数据添加到全局人脸列表中
faces_embedding.append({"userName": personData['userName'], "userId": personData['userId'], "embedding": embedding})
# 把注册的人脸数据,写入到本地文件
faceData = open('facedata/face_ai_db', 'wb')
pickle.dump(faces_embedding, faceData)
faceData.close()
return jsonify({'code': 200, 'msg': '注册成功'})
人脸搜索
输入人脸数据,返回对应的人员数据,图片相似度得分、姓名、用户id
代码实现
# 人脸识别
@app.route('/face/search', methods=['POST'])
def faceRecognition():
# 获取body里面的数据
personData = request.get_json()
img = base64_cv2(personData['imgData'])
# 获取注册图片是否有人脸,返回的结果是个人脸集合,如果图片上有多张脸,就返回多个结果
faces = model.get(img)
if len(faces) == 0:
return jsonify({'code': 201, 'msg': '图片未发现人脸'})
result = dict()
for face in faces_embedding:
score = compute_sim(face['embedding'], faces[0].normed_embedding)
# 设置阈值是40,大于40分认为是准确识别到人,如果精度要高,修改阈值,比如:90,才算正确
if score > 40:
result['userName'] = face['userName']
result['userId'] = face['userId']
result['score'] = score
break
return jsonify({'code': 200, 'msg': 'success', 'data': result})
人脸删除
当员工离职,通过用户id,删除库里的数据
代码实现
# 人脸删除
@app.route('/face/del', methods=['POST'])
def delFace():
# 获取body里面的数据
personData = request.get_json()
userId = personData['userId']
for face in faces_embedding:
if face['userId'] == userId:
# 删除加载到内存的人脸数据
faces_embedding.remove(face)
break
faceData = open('./facedata/face_ai_db', 'wb')
# 人脸数据有修改,重新加载到本地文件,保持最新
pickle.dump(faces_embedding, faceData)
faceData.close()
return jsonify({'code': 200, 'msg': '员工删除成功'})
完成代码实现
import base64
import os
import cv2
import insightface.app
import numpy as np
from flask import Flask, jsonify, request
from pickle4 import pickle
from numpy.linalg import norm
app = Flask("faceai")
# 初始化人脸识别模型
model = insightface.app.FaceAnalysis()
model.prepare(ctx_id=0, det_size=(640, 640))
# 加载本地人脸库数据
faces_embedding = list()
# 人脸注册
@app.route('/face/add', methods=['POST'])
def faceRegister():
# 获取body里面的数据
personData = request.get_json()
img = base64_cv2(personData['imgData'])
# 获取注册图片是否有人脸,返回的结果是个人脸集合,如果图片上有多张脸,就返回多个结果
faces = model.get(img)
if len(faces) == 0:
# 如果图片未识别出人脸,说明图片没有人脸,或者图片质量有问题
return jsonify({'code': 201, 'msg': '图片未发现人脸,请重新注册'})
elif len(faces) > 1:
# 如果图片中有多个人脸对象,那你无法判断应该用哪一个人脸对象,所以,注册的图片,只能有一张脸
return jsonify({'code': 202, 'msg': '图片中多张人脸,无法判断用哪一张脸'})
# 获取人脸数据,上面已经做了限制,所以要注册的人脸数据faces[0]就行
embedding = faces[0].normed_embedding
# 人脸数据添加到全局人脸列表中
faces_embedding.append({"userName": personData['userName'], "userId": personData['userId'], "embedding": embedding})
# 把注册的人脸数据,写入到本地文件
faceData = open('facedata/face_ai_db', 'wb')
pickle.dump(faces_embedding, faceData)
faceData.close()
return jsonify({'code': 200, 'msg': '注册成功'})
# 人脸识别
@app.route('/face/search', methods=['POST'])
def faceRecognition():
# 获取body里面的数据
personData = request.get_json()
img = base64_cv2(personData['imgData'])
# 获取注册图片是否有人脸,返回的结果是个人脸集合,如果图片上有多张脸,就返回多个结果
faces = model.get(img)
if len(faces) == 0:
return jsonify({'code': 201, 'msg': '图片未发现人脸'})
result = dict()
for face in faces_embedding:
score = compute_sim(face['embedding'], faces[0].normed_embedding)
if score > 40:
result['userName'] = face['userName']
result['userId'] = face['userId']
result['score'] = score
break
return jsonify({'code': 200, 'msg': 'success', 'data': result})
# 人脸删除
@app.route('/face/del', methods=['POST'])
def delFace():
# 获取body里面的数据
personData = request.get_json()
userId = personData['userId']
for face in faces_embedding:
if face['userId'] == userId:
faces_embedding.remove(face)
break
faceData = open('./facedata/face_ai_db', 'wb')
pickle.dump(faces_embedding, faceData)
faceData.close()
return jsonify({'code': 200, 'msg': '员工删除成功'})
def loadFaceData():
path = './facedata/face_ai_db'
# 判断文件是否存在,如果存在直接读取,不存在,就创建一个空文件
if os.path.exists(path):
faceData = open(path, 'rb')
faces_embedding = pickle.load(faceData)
faceData.close()
return faces_embedding
else:
faceData = open(path, 'wb')
faces_embedding = list()
pickle.dump(faces_embedding, faceData)
faceData.close()
return faces_embedding
# 余弦相似度算法
def compute_sim(feat1, feat2):
feat1 = feat1.ravel()
feat2 = feat2.ravel()
sim = np.dot(feat1, feat2) / (norm(feat1) * norm(feat2))
sim = sim * 100
return int(sim)
# 图片base64转换为numpy数组对象
def base64_cv2(base64_str):
imgString = base64.b64decode(base64_str)
nparr = np.frombuffer(imgString, np.uint8)
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
return image
# 服务启动入口
if __name__ == '__main__':
# 加载本地人脸数据
faces_embedding = loadFaceData()
app.run(port=8888)
测试结果
人脸注册
人脸搜索
人脸删除
到此,基于falsk人脸检测we版本的就开发完成了,写作不易,如果对你有帮助,动动小手点赞评论加关注,谢谢