feat: 增加人脸识别相关功能

This commit is contained in:
caoqianming 2023-09-11 10:42:34 +08:00
parent addf8e771a
commit e71d5af84a
8 changed files with 201 additions and 20 deletions

View File

@ -29,3 +29,7 @@ class PwResetSerializer(serializers.Serializer):
class SecretLoginSerializer(serializers.Serializer):
username = serializers.CharField(label="用户名")
secret = serializers.CharField(label="密钥")
class FaceLoginSerializer(serializers.Serializer):
base64 = serializers.CharField()

View File

@ -21,4 +21,4 @@ def validate_password(password):
if re.match(pattern, password):
return True
else:
return False
return False

View File

@ -3,7 +3,7 @@ from django.urls import path
from rest_framework_simplejwt.views import TokenRefreshView
from apps.auth1.views import (CodeLogin, LoginView, LogoutView, PwResetView,
SecretLogin, SendCode, TokenBlackView, WxLogin, WxmpLogin, TokenLoginView)
SecretLogin, SendCode, TokenBlackView, WxLogin, WxmpLogin, TokenLoginView, FaceLoginView)
API_BASE_URL = 'api/auth/'
urlpatterns = [
@ -18,4 +18,5 @@ urlpatterns = [
path(API_BASE_URL + 'sms_code/', SendCode.as_view(), name='sms_code_send'),
path(API_BASE_URL + 'logout/', LogoutView.as_view(), name='session_logout'),
path(API_BASE_URL + 'reset_password/', PwResetView.as_view(), name='reset_password'),
path(API_BASE_URL + 'login_face/', FaceLoginView.as_view(), name='face_login')
]

View File

@ -17,6 +17,9 @@ from apps.utils.wx import wxClient
from django.contrib.auth.hashers import make_password
from django.db.models import Q
from apps.auth1.services import validate_password
import base64
from apps.utils.tools import tran64
from apps.auth1.serializers import FaceLoginSerializer
from apps.auth1.serializers import (CodeLoginSerializer, LoginSerializer,
@ -244,3 +247,49 @@ class PwResetView(CreateAPIView):
user.save()
return Response()
raise ParseError('账户不存在或已禁用')
class FaceLoginView(CreateAPIView):
"""人脸识别登录
人脸识别登录
"""
authentication_classes = []
permission_classes = []
serializer_class = FaceLoginSerializer
def create(self, request, *args, **kwargs):
"""
人脸识别登录
"""
from apps.hrm.services import HrmService
base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
ep, msg = HrmService.face_find_from_base64(base64_data)
user = ep.user
if user:
refresh = RefreshToken.for_user(ep.user)
# # 可设为在岗
# now = timezone.now()
# now_local = timezone.localtime()
# if 8<=now_local.hour<=17:
# ins, created = ClockRecord.objects.get_or_create(
# create_by = user, create_time__hour__range = [8,18],
# create_time__year=now_local.year, create_time__month=now_local.month,
# create_time__day=now_local.day,
# defaults={
# 'type':ClockRecord.ClOCK_WORK1,
# 'create_by':user,
# 'create_time':now
# })
# # 设为在岗
# if created:
# Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
'username':user.username,
'name':user.name
})
return Response(msg, status=400)

View File

@ -68,6 +68,9 @@ class EmployeeCreateUpdateSerializer(CustomModelSerializer):
old_job_state = instance.job_state
old_name = instance.name
instance = super().update(instance, validated_data)
if old_photo != instance.photo: # 如果照片有变动,需要更新人脸库
from apps.hrm.tasks import delete_face_pkl
delete_face_pkl.delay(instance.id)
if instance.user and instance != old_name:
instance.user.name = instance.name
instance.user.save()

View File

@ -1,17 +1,21 @@
from rest_framework.exceptions import ParseError, APIException
import logging
import os
import time
import uuid
from datetime import datetime, timedelta
from dateutil import tz
from django.conf import settings
from django.utils import timezone
from django.core.cache import cache
from apps.system.models import User
from apps.hrm.models import ClockRecord, Employee
from apps.third.errors import DH_REQUEST_ERROR
from apps.third.dahua import dhClient
from apps.third.models import TDevice
from apps.third.tapis import dhapis
from apps.third.dahua import dhClient
from apps.utils.tools import rannum, ranstr
from datetime import datetime, timedelta
from django.utils import timezone
from dateutil import tz
from threading import Thread
from django.conf import settings
myLogger = logging.getLogger('log')
@ -254,8 +258,8 @@ class HrmService:
@classmethod
def swipe_next(cls, nodeCode: str, id_number: str, swip_time: str, e_type:int, detail:dict):
from apps.vm.models import Visit
from apps.rpm.models import Rpj
from apps.vm.models import Visit
# 进行相关方/访客项目更新
Visit.objects.filter(state=Visit.V_ENTER, visitors__employee__id_number=id_number).update(
@ -353,4 +357,79 @@ class HrmService:
# 此处可触发安全帽事件逻辑
# 此处可触发安全帽事件逻辑
@classmethod
def get_facedata_from_img(cls, img_path):
import face_recognition
try:
photo_path = settings.BASE_DIR + img_path
picture_of_me = face_recognition.load_image_file(photo_path)
my_face_encoding = face_recognition.face_encodings(picture_of_me, num_jitters=2)[0]
face_data_list = my_face_encoding.tolist()
return face_data_list, ''
except:
return None, '人脸数据获取失败请重新上传图片'
@classmethod
def face_compare_from_base64(cls, base64_data):
import face_recognition
filename = str(uuid.uuid4())
filepath = settings.BASE_DIR +'/temp/face_' + filename +'.png'
with open(filepath, 'wb') as f:
f.write(base64_data)
try:
unknown_picture = face_recognition.load_image_file(filepath)
unknown_face_encoding = face_recognition.face_encodings(unknown_picture, num_jitters=2)[0]
os.remove(filepath)
except:
os.remove(filepath)
return None, '识别失败,请调整位置'
# 匹配人脸库
face_datas = cache.get('face_datas', None)
if face_datas is None:
from apps.hrm.tasks import update_all_facedata_cache
update_all_facedata_cache()
face_datas = cache.get('face_datas')
results = face_recognition.compare_faces(face_datas['datas'],
unknown_face_encoding, tolerance=0.45)
for index, value in enumerate(results):
if value:
# 识别成功
user = User.objects.get(id=face_datas['users'][index])
return user, ''
return None, '人脸未匹配,请调整位置'
@classmethod
def face_find_from_base64(cls, base64_data):
from deepface import DeepFace
img_name = str(uuid.uuid4())
img_path = settings.BASE_DIR +'/temp/face_' + img_name +'.png'
with open(img_path, 'wb') as f:
f.write(base64_data)
db_path = os.path.join(settings.BASE_DIR, 'media/face')
df = DeepFace.find(img_path=img_path, db_path=db_path, model_name='Facenet512')
if df.shape[0] > 0:
matched = df.iloc[0].identity
epId = matched.split('/')[-1].split('.')[0]
return Employee.objects.get(id=epId), ''
else:
return None, '人脸未匹配,请调整位置'
# def VGGFaceloadModel(weight_path):
# from deepface.basemodels import VGGFace
# from keras.src.engine.training import Model
# model = VGGFace.baseModel()
# model.load_weights(weight_path)
# vgg_face_descriptor = Model(inputs=model.layers[0].input, outputs=model.layers[-2].output)
# return vgg_face_descriptor
# def Facenet512loadModel(weight_path):
# from deepface.basemodels import Facenet
# model = Facenet.InceptionResNetV2(dimension=512)
# model.load_weights(weight_path)
# return model

View File

@ -1,16 +1,20 @@
from __future__ import absolute_import, unicode_literals
from celery import shared_task
from apps.hrm.models import Employee, ClockRecord
from dateutil import tz
import time
from datetime import datetime, timedelta
from celery import shared_task
from dateutil import tz
from django.core.cache import cache
from apps.hrm.models import Employee
from apps.hrm.services import HrmService
from apps.third.dahua import dhClient
from apps.third.tapis import dhapis
from apps.hrm.services import HrmService
from django.utils import timezone
from apps.utils.tasks import CustomTask
import time
import os
from django.conf import settings
import shutil
@shared_task
def update_all_employee_not_atwork():
@ -85,4 +89,39 @@ def correct_card_time():
_, res = dhClient.request(**dhapis['card_update'], json=json_data)
HrmService.save(ep, {'dh_face_card_start': start_time_str, 'dh_face_card_end': end_time_new_str})
print('已更新-' + ep.name + '-' + dh_face_card + '-' + end_time_new_str)
time.sleep(1)
time.sleep(1)
@shared_task
def update_all_facedata_cache():
"""
更新人脸数据缓存
"""
facedata_queyset = Employee.objects.filter(face_data__isnull=False,
user__is_active=True).values('user', 'face_data')
face_users = []
face_datas = []
for i in facedata_queyset:
face_users.append(i['user'])
face_datas.append(i['face_data'])
face_data_dict = {"users": face_users, "datas": face_datas}
cache.set('face_datas', face_data_dict)
@shared_task(base=CustomTask)
def delete_face_pkl(epId):
"""
尝试删除人脸库缓存文件
"""
if epId:
ep = Employee.objects.get(id=epId)
photo_path = settings.BASE_DIR + ep.photo
face_path = os.path.join(settings.FACE_PATH, f'{epId}.jpg')
shutil.copy(photo_path, face_path)
file_path = os.path.join(settings.BASE_DIR, 'media/face/representations_facenet512.pkl')
if os.path.exists(file_path):
try:
os.remove(file_path)
except Exception as e:
delete_face_pkl.apply_async(countdown=5)

View File

@ -9,6 +9,12 @@ import requests
from io import BytesIO
from rest_framework.serializers import ValidationError
def tran64(s):
missing_padding = len(s) % 4
if missing_padding != 0:
s = s+'='* (4 - missing_padding)
return s
def singleton(cls):
_instance = {}