face_photo_recognition

流程

    本节总结下人脸图片识别登录的流程,分为以下几步:
        用户进入登录界面
      ->点击人脸识别登录
      ->用户截取图片,点击上传
      ->后台接收图片,读取人员数据库内容,逐一对比
      ->对比结果处理,满足阈值则通过检测,返回登录用户名,反之,返回失败原因
      ->前端接收返回结果,成功,则跳转进入系统;失败则要求用户重传人脸图片

实现

前端代码

  首先,在登录界面加入跳转代码

1
<a href="/face/login/">人脸登录</a>

  接着在人脸识别界面点击打开摄像头按钮,当人脸进入摄像头视野范围后截取图片进行上传

点击显/隐js代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
function $(elem) {
return document.querySelector(elem);
}

// 获取媒体方法(旧方法)
navigator.getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMeddia || navigator.msGetUserMedia;

var canvas = $('canvas'),
context = canvas.getContext('2d'),
video = $('video'),
snap = $('#snap'),
close = $('#close'),
mediaStreamTrack;

// 获取媒体方法(新方法)
// 使用新方法打开摄像头
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(function(stream) {
console.log(stream);

mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[1];

video.src = (window.URL || window.webkitURL).createObjectURL(stream);
video.play();
}).catch(function(err) {
console.log(err);
})
}
// 使用旧方法打开摄像头
else if (navigator.getMedia) {
navigator.getMedia({
video: true
}, function(stream) {
mediaStreamTrack = stream.getTracks()[0];

video.src = (window.URL || window.webkitURL).createObjectURL(stream);
video.play();
}, function(err) {
console.log(err);
});
}

// 截取图像
snap.addEventListener('click', function() {
context.drawImage(video, 0, 0, 200, 150);
}, false);

// 关闭摄像头
close.addEventListener('click', function() {
mediaStreamTrack && mediaStreamTrack.stop();
}, false);

$("#upload").addEventListener('click', function() {
var csrfToken = document.getElementsByName("csrfmiddlewaretoken");

jQuery.ajax({
type:"POST",
url:"/face/login/loginFaceCheck/",
//必须添加 csrf_token
dataType:'json',
data:{
"id":1,
"faceImg":canvas.toDataURL('image/png'),
'csrfmiddlewaretoken':csrfToken[0].value//csrfToken
},
success:function (displayList) {
<!–// 处理认证后的数据–>
if (displayList.canLogin === true){
alert("验证成功!");
alert(displayList.AuthName);
window.location.href='/accounts/profile/';
}
else{
alert("验证失败!");
}
},
error:function () {
alert("验证失败: 未检测到人脸!");
<!–DisplayNo1.text("验证失败: 未检测到人脸").removeClass("label-success").addClass("label-danger");–>
}
})
}, false);

  现在的HTML5在引入webrtc机制,旨在不依赖外部插件为H5开发人员提供更好的音/视频开发支持,这种机制由标准化组织W3C进行H5 API的制定,由浏览器厂商来完成较低层的支持,现在主要的支持浏览器是chrome和Firefox,还有基于chrome内核的浏览器,更多支持可关注webrtc
  本次实验所用js代码,先获取各元素,然后从用户获取媒体流,再根据用户的动作与服务器交互

  因django后台开启csrf安全验证

django项目settings文件
1
MIDDLEWARE = ['django.middleware.csrf.CsrfViewMiddleware',]

  所以我们要在模板页面加入


{% csrf_token %}

  此时每个H5页面都引入了一个隐藏的csrf标签,通过以下代码进行获取:
1
2
var csrfToken = document.getElementsByName("csrfmiddlewaretoken");
csrfToken[0].value;

  当用户点击update按钮时,我们采用ajax方法将数据post到后台

后台代码

  主要涉及两个函数

数据库匹配

点击显/隐views.py代码
单张图片与数据库认证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def auth_user(img):
# 人脸登陆验证
known_people = Faces.objects.all()
known_face = []
auth_name = []
for people in known_people:
auth_name.append(people.user.username)
face = face_recognition.load_image_file(people.face_img)
print(people.face_img)
face_location = face_recognition.face_locations(face)
known_face += face_recognition.face_encodings(face, face_location)

canLogin = False
AuthName = "未授权用户"


# 1> 加载相机刚拍摄的人脸
unknown_face = face_recognition.load_image_file(img)
locate_unknown_face = face_recognition.face_locations(unknown_face)
unknown_face_tmp_encoding = []
try:
unknown_face_tmp_encoding = face_recognition.face_encodings(unknown_face, locate_unknown_face)[0]
except IndexError:
canLogin = False # 图片中未发现人脸

results1 = face_recognition.compare_faces(known_face, unknown_face_tmp_encoding, 0.4)
for i, face_distance in enumerate(results1):
if face_distance == True:
canLogin = True
AuthName = auth_name[i]
break

JsonBackInfo = {
"canLogin": canLogin,
"AuthName": AuthName
}

os.system('del *.jpg')
return JsonBackInfo

  在这里采取往数据库中存用户图片地址的方式记录用户人脸数据,不过,这里在进行用户匹配的时候会有很多重复的动作,当用户增多时,这些重复的动作会是我们优化的一个方向;
  对于人脸识别,这里,我用的是开源项目face_recognition,它是基于dlib的人脸识别库,人脸识别的流程:
  人脸位置检测:

1
face_recognition.face_locations()

  ps:在face_recognition源码中,这最终是通过下面这段调用dlib库函数
1
face_detector = dlib.get_frontal_face_detector()


  dlib是基于hog+线性分类器(svm)的分类器,外加图片金字塔和滑动窗口的方法,这种方法是在2005年由Dalal, Triggs提出,dlib实现的这个版本允许我们训练自己的人脸检测器,需要我们自己提供数据集及标注的xml文件,我们获得的检测结果是每张人脸左上、右下两个标记点组成的标记框,将其保存进变量中


  人脸编码:
1
face_recognition.face_encodings()


  将检测出位置的人脸进行编码(缩小图像编码的范围,利于人脸对比),这是为了之后人脸对比做准备,将每张人脸编码为128维向量


  人脸比较:
1
face_recognition.compare_faces(known_face, unknown_face_tmp_encoding, 0.4)


  比较待测人脸的编码向量与每个已知人脸的编码向量,取欧式距离作为计算依据,设定阈值(默认是0.6,2017年在LFW上获得99.38%的好成绩),将通过阈值检测的第一个为true的对象记为匹配账户


注:当时有尝试直接往mysql数据库中存二进制的人脸编码数据,希望能减少之后进行对比的开销,但存在编码问题,所以暂时选择只存储图片路径;其次,若存进数据库,那么到时进行数据库的查询将变得数据量庞大而缓慢,若数据库与服务器不在一个主机上,传输会成为问题;

处理人脸登录请求

点击显/隐views.py代码
获取用户请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def loginFaceCheck(request):
if request.method == "POST" and request.is_ajax():
# 获取base64格式的图片
start = time.clock()
faceImage = request.POST.get('faceImg')
# 提取出base64格式,并进行转换为图片
index = faceImage.find('base64,')
base64Str = faceImage[index+6:]
img = base64.b64decode(base64Str)

# 不能直接将传来的图片转为numpy数组,会报错“'utf-8' codec can't decode byte 0x89…”,用python先保存再读取
imgName = 'temp.jpg'
with open(imgName, 'wb') as f:
f.write(img)
end = time.clock()
print("img_get_write:", end - start)

start = time.clock()
JsonBackInfo = auth_user(imgName)
end = time.clock()
print("auth_user_time:", end - start)

if JsonBackInfo['AuthName'] != "未授权用户":
user = User.objects.get_by_natural_key(username=JsonBackInfo['AuthName']) # authenticate(username='admin', password='123456')
if user is not None:
if user.is_active:
ori_login(request, user)

return JsonResponse(JsonBackInfo)
else:
return render(request, 'login_registration.html')

  该函数处理人脸识别登录请求,为get时,渲染login_registration.html页面并返回;为post时,通过request.POST.get(‘faceImg’)获取用户图片,这里关于time的方法是用来查看各个阶段的运行时间,最后组装成Jon返回用户信息

Show comments from Gitment