Django实现人脸识别登录
Demo示例下载
1、账号密码登录
2、人脸识别登录
3、注册
4、更改密码
5、示例网站
一、流程说明
1、注册页面:前端打开摄像头,拍照,点击确定后上传图像
2、后端获取到图像,先通过face_recognition第三方库识别是否能够获取到人脸特征,然后把人脸特征通过json转成字符串保存到数据库中
3、登录页面:可使用账号密码登录,也可使用人脸登录。
使用人脸登录时:先通过摄像头获取人脸图像,上传到后端
4、后端通过face_recognition获取图像的人脸特征,转成字符串,然后和数据库中的一一比对,比对成功则能够登录。
二、实现过程
1、创建虚拟环境
python使用3.8版本。
打开cmd窗口,进入d盘的pythonpro目录,接着进入venv目录,执行以下命令:
python -m venv face_login
然后激活虚拟环境:
按回车键。
2、导入第三方库
python.exe -m pip install --upgrade pip
pip install Django==3.2.7 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install PyMySQL==0.9.2 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install mysqlclient==2.0.1 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install pillow==9.5.0 -i https://pypi.mirrors.ustc.edu.cn/simple/
安装 face_recognition可参考博客点我跳转
先下载dlib,然后再安装:
pip install dlib-19.19.0-cp38-cp38-win_amd64.whl.whl
pip install face_recognition -i https://pypi.tuna.tsinghua.edu.cn/simple
3、创建django项目
退出到pythonpro目录
cd ..
cd ..
cd ..
创建项目,执行命令:
django-admin startproject face_login
创建子应用,切换到项目根目录:
cd face_login
创建子应用
python manage.py startapp login
自此项目创建完成。
4、pycharm加载项目
打开pycharm,File->open->打开D盘下的pythonpro里面的face_login项目。
然后配置解释器:File->settings->Project:face_login->Python Interpreter:
5、创建MySQL数据库
命名为:face_login,字符集选择utf8mb4
6、settings.py配置
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-lu=z^(gz4118++$nznr_nz$p9u6_+-ieh=z-uz66)^esir%ei5'
DEBUG = True
ALLOWED_HOSTS = ['*'] # 允许所有访问
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'login' # 添加子应用
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'face_login.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 添加模板
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'face_login.wsgi.application'
# 使用本地数据库
ip = '127.0.0.1'
DATABASE_NAME = 'face_login' # mysql数据库名称
DATABASE_USER = 'root' # mysql数据库用户名
DATABASE_PASS = '123456' # mysql数据库密码
DATABASE_HOST = ip # mysql数据库IP
DATABASE_PORT = 3306 # mysql数据库端口
# 配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 修改数据库为MySQL,并进行配置
'NAME': DATABASE_NAME, #
'USER': DATABASE_USER, # 用户名
'PASSWORD': DATABASE_PASS, # 密码
'HOST': DATABASE_HOST,
'PORT': DATABASE_PORT,
'OPTIONS': {'charset': 'utf8mb4', }
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'zh-hans' # 使用中国时区
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# 静态文件配置
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS
# 媒体文件
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_USER_ICON = os.path.join(BASE_DIR, 'media/user_icon')
7、models模型
from django.db import models
# 用户表
class User(models.Model):
username = models.CharField(max_length=255, unique=True, verbose_name="账号")
password = models.CharField(default='123456', max_length=32, verbose_name="密码")
icon = models.ImageField(upload_to='user_icon', default=r'user_icon/default_icon.jpeg', null=True, blank=True,
verbose_name='头像')
icon_feature = models.TextField(null=True, blank=True, verbose_name='人脸特征')
class Meta:
db_table = 'user'
verbose_name_plural = "用户"
verbose_name = "用户"
def __str__(self):
return self.username
数据迁移:点击PyCharm左下角的Terminal,然后执行:
python manage.py makemigrations
python manage.py migrate
8、urls.py路由
from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path
from django.views.static import serve
from login import views
urlpatterns = [
path('admin/', admin.site.urls),
path("", views.login, name="login"), # 登录
path("login/", views.login, name="login"), # 登录
path("register/", views.register, name="register"), # 注册
path("logout/", views.logout, name="logout"), # 退出
path("update_pass/", views.update_pass, name="update_pass"), # 更新密码
path("index/", views.index, name="index"), # 首页
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭
]
9、views.py调度器
import json
import os
import face_recognition
import numpy as np
from django.http import JsonResponse
from django.shortcuts import render, redirect
# 登录
from django.urls import reverse
from face_login.settings import MEDIA_USER_ICON
from login.forms import Login, RegisterForm
from login.models import User
def login(request):
if request.method == "GET":
# get请求说明是用户进入登录界面
if request.GET.get('type') == 'face':
# 人脸识别登录
return render(request, "login_face.html")
# 账号密码
form = Login()
return render(request, "login.html", {"form": form})
# 人脸识别登录
if request.POST.get('type') == 'face':
icon = request.FILES.get('icon')
# 验证是否可以获取人像信息
if not icon:
return JsonResponse(data={'code': 1, 'msg': '获取人脸失败,请打开摄像头拍照!'})
known_pic = face_recognition.load_image_file(icon)
known_pic_encoding = face_recognition.face_encodings(known_pic)
if not known_pic_encoding:
return JsonResponse(data={'code': 1, 'msg': '获取人脸失败,请对准摄像头拍照!'})
# 人脸特征
icon_feature = known_pic_encoding[0]
for user in User.objects.all():
if not user.icon_feature:
continue
# 比较已知人脸和未知人脸,返回结果为true或者false。
if face_recognition.compare_faces(icon_feature, [np.array(json.loads(user.icon_feature))])[0]:
request.session["login_in"] = True
request.session["user_id"] = user.id
request.session["icon"] = user.icon.name
request.session["username"] = user.username # 把用户Id写进浏览器session
return JsonResponse(data={'code': 2, 'msg': '登录成功'})
return JsonResponse(data={'code': 1, 'msg': '登录失败,人脸识别不通过'})
# 账号密码登录
form = Login(request.POST)
if form.is_valid():
# 验证用户的登录form是否有效
username = form.cleaned_data["username"] # 账号
password = form.cleaned_data["password"] # 密码
result = User.objects.filter(username=username) # 通过账号获取用户信息
if result:
user = result.first()
# 验证密码是否正确
if user.password == password:
request.session["login_in"] = True
request.session["user_id"] = user.id
request.session["icon"] = user.icon.name
request.session["username"] = user.username # 把用户Id写进浏览器session
return redirect(reverse("index")) # 跳转到首页
else:
return render(request, "login.html", {"form": form, "error": "账号或密码错误"})
else:
return render(request, "login.html", {"form": form, "error": "账号不存在"})
def save_icon(icon, username):
# 保存图片
photo_name = '{}_{}'.format(username, icon.name)
filePath = os.path.join(MEDIA_USER_ICON, photo_name)
with open(filePath, 'wb') as fp:
for part in icon.chunks():
fp.write(part)
return r'user_icon/{}'.format(photo_name)
def is_valid(data, icon):
# 验证用户是否可以注册
if User.objects.filter(username=data['username']):
return [False, '用户名已经存在!']
if data['password1'] != data['password2']:
return [False, '第二次输入密码不对!']
if not icon:
return [False, '获取人脸失败,请打开摄像头拍照!']
# 验证是否可以获取人像信息
known_pic = face_recognition.load_image_file(icon)
known_pic_encoding = face_recognition.face_encodings(known_pic)
if not known_pic_encoding:
return [False, '获取人脸失败,请对准摄像头拍照!']
# 验证人脸是否被注册
icon_feature = known_pic_encoding[0]
for user in User.objects.all():
if not user.icon_feature:
continue
# 比较已知人脸和未知人脸,返回结果为true或者false。
if face_recognition.compare_faces(icon_feature, [np.array(json.loads(user.icon_feature))])[0]:
return [False, '注册失败,该人脸信息已被注册!']
# 人脸特征
icon_feature = json.dumps(known_pic_encoding[0].tolist())
return [True, '验证通过', icon_feature]
# 注册
def register(request):
if request.method == "GET":
# get请求说明用户进入了注册页面
form = RegisterForm()
return render(request, "register.html", {"form": form})
# post请求说明用户输入了账号、密码发起了注册请求
data = request.POST
icon = request.FILES.get('icon')
ret = is_valid(data, icon)
if not ret[0]:
return JsonResponse(data={'code': 1, 'msg': ret[1]})
# 验证通过
username = data["username"] # 用户名
password = data["password2"] # 密码
icon_name = save_icon(icon, username)
icon_feature = ret[2]
# 创建用户
User.objects.create(username=username, password=password, icon=icon_name, icon_feature=icon_feature)
return JsonResponse(data={'code': 2, 'msg': '注册成功!'})
# 登出
def logout(request):
if not request.session.get("login_in", None): # 不在登录状态跳转回首页
return redirect(reverse("index"))
request.session.flush() # 清除session信息
return redirect(reverse("login"))
def is_update_valid(data, icon):
# 验证用户是否可以更改密码
if data['password1'] != data['password2']:
return [False, '第二次输入密码不对!']
if not icon:
return [False, '获取人脸失败,请打开摄像头拍照!']
# 验证是否可以获取人像信息
known_pic = face_recognition.load_image_file(icon)
known_pic_encoding = face_recognition.face_encodings(known_pic)
if not known_pic_encoding:
return [False, '获取人脸失败,请对准摄像头拍照!']
# 验证人脸是否被注册
icon_feature = known_pic_encoding[0]
for user in User.objects.all():
if not user.icon_feature:
continue
# 比较已知人脸和未知人脸,返回结果为true或者false。
if face_recognition.compare_faces(icon_feature, [np.array(json.loads(user.icon_feature))])[0]:
user.password = data['password1']
user.save()
return [True, '验证通过!']
return [False, '该人脸信息未被注册,请注册新账号!']
# 更新密码
def update_pass(request):
if request.method == "GET":
# get请求说明用户进入了更新密码页面
form = RegisterForm()
return render(request, "update_pass.html", {"form": form})
# post请求说明用户输入了账号、密码发起了注册请求
data = request.POST
icon = request.FILES.get('icon')
ret = is_update_valid(data, icon)
if not ret[0]:
return JsonResponse(data={'code': 1, 'msg': ret[1]})
# 验证通过
return JsonResponse(data={'code': 2, 'msg': '密码更改成功!'})
# 首页
def index(request):
# 登录成功后进入首页
return render(request, "index.html")
10、首页
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/media/django.jpg">
<title>人脸登录系统系统</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/dashboard.css" rel="stylesheet">
<link href="/static/css/custom.css" rel="stylesheet">
<script src="/static/js/ie-emulation-modes-warning.js"></script>
<script src="/static/js/html5shiv.min.js"></script>
<script src="/static/js/respond.min.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" style="background-color: #652F12;font-size: 18px">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/" style="color: whitesmoke;font-size: 18px">人脸登录识别系统</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
{% if request.session.login_in == True %}
<li>
<span href="#" style="color: whitesmoke">{{ request.session.username }}</span>
<img class="img-main" style="height: 50px;width: 50px;border-radius: 50%"
src="/media/{{ request.session.icon }}"
alt="{{ request.session.username }}">
</li>
<li><a href="{% url 'logout' %}" style="color: whitesmoke">退出</a></li>
{% else %}
<li><a href="{% url 'login' %}" style="color: whitesmoke">登录</a></li>
<li><a href="{% url 'register' %}" style="color: whitesmoke">注册</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="row" style="text-align: center; font-size: 25px">
<ul>
<li style="margin-right: 2px;padding: 0">
<a href="https://liangdongchang.blog.csdn.net/" target="_blank">我的博客【点我跳转】
</a>
</li>
<li style="margin-right: 2px;padding: 0">
<a href="http://newbook.qsxbc.com/all_book/" target="_blank">图书管理系统【点我跳转】
</a>
</li>
<li style="margin-right: 2px;padding: 0">
<a href="http://movie.qsxbc.com/" target="_blank">电影管理系统【点我跳转】
</a>
</li>
<li style="margin-right: 2px;padding: 0">
<a href="http://course.qsxbc.com/" target="_blank">在线课程管理系统【点我跳转】
</a>
</li>
<li style="margin-right: 2px;padding: 0">
<a href="#" target="_blank">健身课程管理系统
</a>
</li>
</ul>
</div>
</div>
</div>
<script src="/static/js/jquery-2.1.1.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/ie10-viewport-bug-workaround.js"></script>
<script src="/static/js/custom.js"></script>
<script src="/static/js/plugins/highstock/js/highstock.js"></script>
<script src="/static/js/plugins/highstock/js/modules/exporting.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
11、登录页面
人脸识别登录
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/media/django.jpg">
<title>人脸识别登录系统</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/dashboard.css" rel="stylesheet">
<link href="/static/css/custom.css" rel="stylesheet">
<script src="/static/js/ie-emulation-modes-warning.js"></script>
<script src="/static/js/html5shiv.min.js"></script>
<script src="/static/js/respond.min.js"></script>
</head>
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-size: 200px;
}
.col-md-4 {
display: flex;
border-radius: 20px;
flex-direction: column;
margin-top: 0;
margin-left: 100px;
padding-top: 0;
width: 650px;
height: 440px;
backdrop-filter: blur(10px);
border: grey solid 3px;
animation-name: shake;
animation-duration: 1s;
background: #FFFFFF;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
</style>
<body>
<div class="col-md-4 col-md-offset-4">
<h3 style="text-align: center;color: #4CAF50;">
人脸识别登录
</h3>
{% if error %}
<p style="color: red">{{ error }}</p>
{% endif %}
<div style="margin-bottom: 5px">
<a href="{% url 'login' %}">账号密码登录</a>
</div>
<div class="form-group">
<label>获取头像</label>
<div id="cameraBtn" style="margin-bottom: 5px;text-align: center">
<input type="button" id="init" onclick="captureInit()" value="开启摄像头"/>
<input type="button" id="capture" onclick="capture_s()" value="开始拍照"/>
<input type="button" id="queren" onclick="queren_s()" value="确定登录"/>
</div>
<div id="cameraDiv" style="width: 650px">
<!-- 视频流 -->
<video id="video" autoplay style="width: 300px;height: 200px"></video>
<!--描绘video截图-->
<canvas id="canvas" width="300" height="200"></canvas>
</div>
</div>
<p style="text-align: center;margin-top: 10px;color: #b3b3b3;">未有账号?
<a href="{% url 'register' %}" style="color: #4CAF50;">点我注册</a>
</p>
<p style="text-align: center;margin-top: 10px;color: #b3b3b3;">
<a href="{% url 'update_pass' %}" style="color: #4CAF50;">忘记密码?</a>
</p>
</div>
</body>
<script src="/static/js/jquery-2.1.1.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script type="text/javascript">
var cb = getPara("cb") || "setImg";
var filetypeid = getPara("filetypeid") || "filetypeid";//附件类型id
var video = document.getElementById("video");
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var imgWidth = getPara("width") || "300";//这个值div的宽一致
var imgHeight = getPara("height") || "200";//这个值div的高一致
var captureState = 0;//未开启,1已开启,2已拍照,为2才可点击确认按钮
var checkedCount = 0; // 课程类型选择数量
var checkList = [] // 课程类型选择id列表
var style = getPara("style") || "big-btn-blue";
video.style.width = imgWidth;
video.style.height = imgHeight;
var st = style.split(",");
document.getElementById("init").className = st[0];
document.getElementById("capture").className = st[1] || st[0];
document.getElementById("queren").className = st[2] || st[0];
//访问用户媒体设备的兼容方法
function getUserMedia(constrains, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新标准API
navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit内核浏览器
navigator.webkitGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.mozGetUserMedia) {
//Firefox浏览器
navagator.mozGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constrains).then(success).catch(error);
} else {
alert("不支持的浏览器");
}
}
//成功的回调函数
function success(stream) {
//兼容webkit内核浏览器
var CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
try {
video.srcObject = stream;
} catch (e) {
video.src = CompatibleURL.createObjectURL(stream);
}
//播放视频
video.play();
}
//异常的回调函数
function error(error) {
alert("访问用户媒体设备失败," + error.name + "" + error.message);
}
function getPara(name, search) {
var p = getParas(name, search);
if (p.length == 0) {
return null;
} else if (p.length == 1) {
return p[0];
} else {
return p.toString();
}
}
function getParas(name, search) {
if (!search) {
search = window.location.search.substr(1);//1.html?a=1&b=2
}
var para = [];
var pairs = search.split("&");//a=1&b=2&b=2&c=2&b=2
for (var i = 0; i < pairs.length; i = i + 1) {
var sign = pairs[i].indexOf("=");
if (sign == -1) {//如果没有找到=号,那么就跳过,跳到下一个字符串(下一个循环)。
continue;
}
var aKey = pairs[i].substring(0, sign);
if (aKey == name) {
para.push(unescape(pairs[i].substring(sign + 1)));
}
}
return para;
}
//开启摄像头
function captureInit() {
if ((navigator.mediaDevices != undefined && navigator.mediaDevices.getUserMedia != undefined)
|| navigator.getUserMedia != undefined
|| navigator.webkitGetUserMedia != undefined
|| navigator.mozGetUserMedia != undefined) {
//调用用户媒体设备,访问摄像头,改为触发事件
getUserMedia({video: {width: imgWidth, height: imgHeight}}, success, error);
if (captureState === 0) {
captureState = 1;//标记此按钮已点击
}
} else {
captureState = 0;//异常标识按钮没点
alert("你的浏览器不支持访问用户媒体设备或访问地址不是https开头,您可以点击右侧下载解决方案");
}
}
//注册拍照按钮的单击事件
function capture_s() {
//绘制画面
if (captureState == 0) {
alert("请先开启摄像头");
return;
}
context.drawImage(video, 0, 0, imgWidth, imgHeight);//后面两个长宽
//canvas.toDataURL("image/png");//即可得到base64编码
captureState = 2;
}
function convertCanvasToImage(canvas) {
//新Image对象,可以理解为DOM
var image = new Image();
// canvas.toDataURL 返回的是一串Base64编码的URL,当然,浏览器自己肯定支持
// 指定格式 PNG
image.src = canvas.toDataURL("image/png");
return image;
}
// 确认按钮,提交数据到后端进行注册
function queren_s() {
if (captureState != 2) {
alert("请先开启摄像头并拍照");
return;
}
var mycanvas1 = document.getElementById('canvas');
// 将 Canvas 转换为 Blob
mycanvas1.toBlob(blob => {
// 创建 FormData 对象,将 Blob 添加为文件对象
const formData = new FormData();
formData.append('icon', blob, 'photo.jpg');
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}')
formData.append('type', 'face')
// 使用 $.ajax() 函数发送 AJAX 请求
$.ajax({
url: '{% url 'login' %}',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: (data) => {
if (data.code === 1) {
// 登录失败
alert(data.msg)
} else {
// 登录成功
window.location.href = '/index/';
}
},
error: () => {
console.error('Error uploading photo.');
},
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
}
});
}, 'image/jpeg', 0.8);
}
</script>
</html>
12、注册页面
{% load static %}
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/media/django.jpg">
<title>人脸识别登录系统</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/dashboard.css" rel="stylesheet">
<link href="/static/css/custom.css" rel="stylesheet">
<script src="/static/js/ie-emulation-modes-warning.js"></script>
<script src="/static/js/html5shiv.min.js"></script>
<script src="/static/js/respond.min.js"></script>
</head>
{% if error %}
<p>{{ error }}</p>
{% endif %}
<style>
* {
margin: 0;
padding: 0;
}
</style>
<body>
<div class="col-md-6 col-md-offset-3" style="margin-top: 0;padding-top:0;margin-left: 350px;border: grey solid 3px;animation-name: shake;
animation-duration: 1s;background: #FFFFFF;box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);border-radius: 20px;">
<h4 style="text-align: center;color: #4CAF50;">
用户注册
</h4>
<form>
{% csrf_token %}
<div style="margin:0 auto;width: 300px">
<label for="id_username">用户名:</label>
<input type="text" name="username" class="form-control" required="" maxlength="50" id="id_username">
</div>
<div style="margin:0 auto;width: 300px">
<label for="id_password1">密码:</label>
<input type="password" name="password1" class="form-control" required="" maxlength="128" id="id_password1">
</div>
<div style="margin:0 auto;width: 300px">
<label for="id_password2">确认密码:</label>
<input type="password" name="password2" class="form-control" required="" id="id_password2">
</div>
<div style="margin:0 auto;width: 300px;margin-bottom: 5px">
<label>获取头像</label>
<div id="cameraBtn">
<input type="button" id="init" onclick="captureInit()" value="开启摄像头"/>
<input type="button" id="capture" onclick="capture_s()" value="拍照"/>
</div>
</div>
<div id="cameraDiv" style="width: 650px">
<!-- 视频流 -->
<video id="video" autoplay style="width: 300px;height: 200px"></video>
<!--描绘video截图-->
<canvas id="canvas" width="300" height="200"></canvas>
</div>
<div style="text-align: center;margin-bottom: 10px">
<button type="reset"
style="background: #4CAF50;color: #FFFFFF;width: 50px;height: 30px;margin-left: -10px;border-radius: 5px;">
取消
</button>
<input type="button" id="queren" style="background: #4CAF50;color: #FFFFFF;width: 50px;height: 30px;margin-left: 50px;border-radius: 5px;" onclick="queren_s()" value="确定"/>
</div>
<p style="text-align: center;margin-top: 10px;color: #b3b3b3">已有账号?
<a href="{% url 'login' %}"
style="color: #4CAF50;">点我登录</a>
</p>
</form>
</div>
</body>
<script src="/static/js/jquery-2.1.1.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script type="text/javascript">
var cb = getPara("cb") || "setImg";
var filetypeid = getPara("filetypeid") || "filetypeid";//附件类型id
var video = document.getElementById("video");
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var imgWidth = getPara("width") || "300";//这个值div的宽一致
var imgHeight = getPara("height") || "200";//这个值div的高一致
var captureState = 0;//未开启,1已开启,2已拍照,为2才可点击确认按钮
var style = getPara("style") || "big-btn-blue";
video.style.width = imgWidth;
video.style.height = imgHeight;
var st = style.split(",");
document.getElementById("init").className = st[0];
document.getElementById("capture").className = st[1] || st[0];
document.getElementById("queren").className = st[2] || st[0];
//访问用户媒体设备的兼容方法
function getUserMedia(constrains, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新标准API
navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit内核浏览器
navigator.webkitGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.mozGetUserMedia) {
//Firefox浏览器
navagator.mozGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constrains).then(success).catch(error);
} else {
alert("不支持的浏览器");
}
}
//成功的回调函数
function success(stream) {
//兼容webkit内核浏览器
var CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
try {
video.srcObject = stream;
} catch (e) {
video.src = CompatibleURL.createObjectURL(stream);
}
//播放视频
video.play();
}
//异常的回调函数
function error(error) {
alert("访问用户媒体设备失败," + error.name + "" + error.message);
}
function getPara(name, search) {
var p = getParas(name, search);
if (p.length == 0) {
return null;
} else if (p.length == 1) {
return p[0];
} else {
return p.toString();
}
}
function getParas(name, search) {
if (!search) {
search = window.location.search.substr(1);//1.html?a=1&b=2
}
var para = [];
var pairs = search.split("&");//a=1&b=2&b=2&c=2&b=2
for (var i = 0; i < pairs.length; i = i + 1) {
var sign = pairs[i].indexOf("=");
if (sign == -1) {//如果没有找到=号,那么就跳过,跳到下一个字符串(下一个循环)。
continue;
}
var aKey = pairs[i].substring(0, sign);
if (aKey == name) {
para.push(unescape(pairs[i].substring(sign + 1)));
}
}
return para;
}
//开启摄像头
function captureInit() {
if ((navigator.mediaDevices != undefined && navigator.mediaDevices.getUserMedia != undefined)
|| navigator.getUserMedia != undefined
|| navigator.webkitGetUserMedia != undefined
|| navigator.mozGetUserMedia != undefined) {
//调用用户媒体设备,访问摄像头,改为触发事件
getUserMedia({video: {width: imgWidth, height: imgHeight}}, success, error);
if (captureState === 0) {
captureState = 1;//标记此按钮已点击
}
} else {
captureState = 0;//异常标识按钮没点
alert("你的浏览器不支持访问用户媒体设备或访问地址不是https开头,您可以点击右侧下载解决方案");
}
}
//注册拍照按钮的单击事件
function capture_s() {
//绘制画面
if (captureState == 0) {
alert("请先开启摄像头");
return;
}
context.drawImage(video, 0, 0, imgWidth, imgHeight);//后面两个长宽
//canvas.toDataURL("image/png");//即可得到base64编码
captureState = 2;
}
function convertCanvasToImage(canvas) {
//新Image对象,可以理解为DOM
var image = new Image();
// canvas.toDataURL 返回的是一串Base64编码的URL,当然,浏览器自己肯定支持
// 指定格式 PNG
image.src = canvas.toDataURL("image/png");
return image;
}
function toVaild() {
if (document.getElementsByName('username')[0].value == '') {
alert("请输入用户名");
return false;
}
if (document.getElementsByName('password1')[0].value == '') {
alert("请输入密码");
return false;
}
if (document.getElementsByName('password2')[0].value == '') {
alert("请输入确认密码");
return false;
}
if (document.getElementsByName('password1')[0].value != document.getElementsByName('password2')[0].value) {
alert('第二次密码输入有误');
return false;
}
return true;
}
// 确认按钮,提交数据到后端进行注册
function queren_s() {
if (toVaild() == false) {
return;
}
if (captureState != 2) {
alert("请先开启摄像头并拍照");
return;
}
var mycanvas1 = document.getElementById('canvas');
// 将 Canvas 转换为 Blob
mycanvas1.toBlob(blob => {
// 创建 FormData 对象,将 Blob 添加为文件对象
const formData = new FormData();
formData.append('username', document.getElementsByName('username')[0].value);
formData.append('password1', document.getElementsByName('password1')[0].value);
formData.append('password2', document.getElementsByName('password2')[0].value);
formData.append('icon', blob, 'photo.jpg');
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}')
// 使用 $.ajax() 函数发送 AJAX 请求
$.ajax({
url: '{% url 'register' %}',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: (data) => {
if (data.code === 1) {
// 注册失败
alert(data.msg)
} else {
// 注册成功
window.location.href = '/login/';
}
},
error: () => {
console.error('Error uploading photo.');
},
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
}
});
}, 'image/jpeg', 0.8);
}
</script>
</html>