先来看看目录
1、编写register.html界面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
/**解决浏览器兼容性问题**/
*{margin: 0;padding: 0;}
html,body{width: 100%;height: 100%;}/**/
body{background: url(img/bg03.jpg) no-repeat center;}
h1{color: #fff;text-align: center;line-height: 80px;}
.media{width: 534px;height: 400px;margin: 40px auto 0;
}
#register{width: 200px;height:50px;background-color: #2196f3; margin: 60px auto 0;
text-align: center;line-height: 50px;color: #fff;border-radius: 10px;}
#canvas{display: none;}
#shuru{width: 300px;height:60px;background-color: #2196f3; margin: 20px auto 0;}
</style>
</head>
<body>
<h1>百度云人脸注册</h1>
<div id="shuru">
用户名:
<input type="text" name="username" id="username"/>
<br>
用户ID:
<input type="text" name="userFaceId" id="userFaceId"/>
</div>
<div class="media">
<video id="video" width="450" height="300" src="" autoplay></video>
<canvas id="canvas" width="450" height="300"></canvas>
</div>
<button id="register" >确定注册</button>
<script type="text/javascript" th:src="@{/js/jquery-3.3.1.js}"></script>
<script type="text/javascript">
/**调用摄像头,获取媒体视频流**/
var video = document.getElementById('video');
//返回画布二维画图环境
var userContext = canvas.getContext("2d");
var getUserMedia =
//浏览器兼容,表示在火狐、Google、IE等浏览器都可正常支持
(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia)
//getUserMedia.call(要调用的对象,约束条件,调用成功的函数,调用失败的函数)
getUserMedia.call(navigator,{video: true,audio: false},function(localMediaStream){
//获取摄像头捕捉的视频流
video.srcObject=localMediaStream;
},function(e){
console.log("获取摄像头失败!!")
});
//点击按钮注册事件
var btn = document.getElementById("register");
btn.onclick = function () {
var username = $("#username").val();
var userFaceId = $("#userFaceId").val();
// alert($("#username").val());
// alert($("#userFaceId").val());
if(username != null){
//点击按钮时拿到登陆者面部信息
userContext.drawImage(video,0,0,450,300);
var userImgSrc = document.getElementById("canvas").toDataURL("img/png");
//拿到bash64格式的照片信息
var faceBase = userImgSrc.split(",")[1];
//ajax异步请求
$.ajax({
url: "register",
type: "post",
data: {"faceBase": faceBase,
"userName": username,
"userFaceId":userFaceId
},
success: function(result){
if(result === '1'){
alert("帅哥注册成功!!点击确认跳转至登录页面");
window.location.href="toLogin";
}else if(result === '2'){
alert("不是已经有你的帅脸了吗!!");
}else{
alert("系统错误!!");
}
}
})
}else{
alert("用户名不能为空");
}
}
</script>
</body>
</html>
-
需要注意的是<video>标签用于在HTML或者XHTML文档中嵌入媒体播放器,在此处用来调用摄像头,获取媒体视频流
-
canvas元素具有一个称为getContext的DOM方法,用于获取渲染上下文及其绘图功能。此函数采用一个参数,即上下文2d的类型。此处返回画布二维画图环境
-
(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia)浏览器兼容;
-
Navigator.getUserMedia()方法提醒用户需要使用音频(0或者1)和(0或者1)视频输入设备,比如相机,屏幕共享,或者麦克风。如果用户给予许可,successCallback回调就会被调用,MediaStream对象作为回调函数的参数。如果用户拒绝许可或者没有媒体可用,errorCallback就会被调用,类似的,
PermissionDeniedError
或者NotFoundError
-
-
getUserMedia.call(要调用的对象,约束条件,调用成功的函数,调用失败的函数)
2、编写utils包下的百度自带的工具类
1、FileUtil
package com.example.face.utils;
import java.io.*;
/**
* 文件读取工具类
*/
public class FileUtil {
/**
* 读取文件内容,作为字符串返回
*/
public static String readFileAsString(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
if (file.length() > 1024 * 1024 * 1024) {
throw new IOException("File is too large");
}
StringBuilder sb = new StringBuilder((int) (file.length()));
// 创建字节输入流
FileInputStream fis = new FileInputStream(filePath);
// 创建一个长度为10240的Buffer
byte[] bbuf = new byte[10240];
// 用于保存实际读取的字节数
int hasRead = 0;
while ( (hasRead = fis.read(bbuf)) > 0 ) {
sb.append(new String(bbuf, 0, hasRead));
}
fis.close();
return sb.toString();
}
/**
* 根据文件路径读取byte[] 数组
*/
public static byte[] readFileByBytes(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
} else {
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
short bufSize = 1024;
byte[] buffer = new byte[bufSize];
int len1;
while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
bos.write(buffer, 0, len1);
}
byte[] var7 = bos.toByteArray();
return var7;
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException var14) {
var14.printStackTrace();
}
bos.close();
}
}
}
}
2、Base64Util
package com.example.face.utils;
/**
* Base64 工具类
*/
public class Base64Util {
private static final char last2byte = (char) Integer.parseInt("00000011", 2);
private static final char last4byte = (char) Integer.parseInt("00001111", 2);
private static final char last6byte = (char) Integer.parseInt("00111111", 2);
private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
public Base64Util() {
}
public static String encode(byte[] from) {
StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
int num = 0;
char currentByte = 0;
int i;
for (i = 0; i < from.length; ++i) {
for (num %= 8; num < 8; num += 6) {
switch (num) {
case 0:
currentByte = (char) (from[i] & lead6byte);
currentByte = (char) (currentByte >>> 2);
case 1:
case 3:
case 5:
default:
break;
case 2:
currentByte = (char) (from[i] & last6byte);
break;
case 4:
currentByte = (char) (from[i] & last4byte);
currentByte = (char) (currentByte << 2);
if (i + 1 < from.length) {
currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
}
break;
case 6:
currentByte = (char) (from[i] & last2byte);
currentByte = (char) (currentByte << 4);
if (i + 1 < from.length) {
currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
}
}
to.append(encodeTable[currentByte]);
}
}
if (to.length() % 4 != 0) {
for (i = 4 - to.length() % 4; i > 0; --i) {
to.append("=");
}
}
return to.toString();
}
}
3、HttpUtil
package com.example.face.utils;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
/**
* http 工具类
*/
public class HttpUtil {
public static String post(String requestUrl, String accessToken, String params)
throws Exception {
String contentType = "application/x-www-form-urlencoded";
return HttpUtil.post(requestUrl, accessToken, contentType, params);
}
public static String post(String requestUrl, String accessToken, String contentType, String params)
throws Exception {
String encoding = "UTF-8";
if (requestUrl.contains("nlp")) {
encoding = "GBK";
}
return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
}
public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
throws Exception {
String url = requestUrl + "?access_token=" + accessToken;
return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
}
public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
throws Exception {
URL url = new URL(generalUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
// 设置通用的请求属性
connection.setRequestProperty("Content-Type", contentType);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
// 得到请求的输出流对象
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(params.getBytes(encoding));
out.flush();
out.close();
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> headers = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : headers.keySet()) {
System.err.println(key + "--->" + headers.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in = null;
in = new BufferedReader(
new InputStreamReader(connection.getInputStream(), encoding));
String result = "";
String getLine;
while ((getLine = in.readLine()) != null) {
result += getLine;
}
in.close();
System.err.println("result:" + result);
return result;
}
}
4、GsonUtils
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.example.face.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
/**
* Json工具类.
*/
public class GsonUtils {
private static Gson gson = new GsonBuilder().create();
public static String toJson(Object value) {
return gson.toJson(value);
}
public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
return gson.fromJson(json, classOfT);
}
public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
return (T) gson.fromJson(json, typeOfT);
}
}
3、创建工具类BaiduFaceUtil
由于在本程序中需要多次用到APP_ID、CLIENTID、CLIENTSECRET这三个定值,所以我们创建一个工具类来编写它们
package com.example.face.utils;
public class BaiduFaceUtil {
public static final String APP_ID = "";
// 官网获取的 API Key 更新为你注册的
public final static String CLIENTID = "";
// 官网获取的 Secret Key 更新为你注册的
public final static String CLIENTSECRET = "";
}
4、编写GetAccessToken
获取权限access_token,由于人脸识别的所有操作都需要access_token,但是access_token又会定时改变所以我们编写一个工具类来获取事实的access_token
package com.example.face.utils;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
/**
* 获取token类
*/
public class GetAccessToken {
/**
* 获取权限token
* @return 返回示例:
* {
* "access_token": "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567",
* "expires_in": 2592000
* }
*/
public static String getAuth() {
// 官网获取的 API Key 更新为你注册的
String clientId = "";
// 官网获取的 Secret Key 更新为你注册的
String clientSecret = "";
return getAuth(clientId, clientSecret);
}
/**
* 获取API访问token
* 该token有一定的有效期,需要自行管理,当失效时需重新获取.
* @param ak - 百度云官网获取的 API Key
* @param sk - 百度云官网获取的 Secret Key
* @return assess_token 示例:
* "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567"
*/
public static String getAuth(String ak, String sk) {
// 获取token地址
String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
String getAccessTokenUrl = authHost
// 1. grant_type为固定参数
+ "grant_type=client_credentials"
// 2. 官网获取的 API Key
+ "&client_id=" + ak
// 3. 官网获取的 Secret Key
+ "&client_secret=" + sk;
try {
URL realUrl = new URL(getAccessTokenUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
connection.setRequestMethod("GET");
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet()) {
System.err.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String result = "";
String line;
while ((line = in.readLine()) != null) {
result += line;
}
/**
* 返回结果示例
*/
System.err.println("result:" + result);
JSONObject jsonObject = new JSONObject(result);
String access_token = jsonObject.getString("access_token");
return access_token;
} catch (Exception e) {
System.err.printf("获取token失败!");
e.printStackTrace(System.err);
}
return null;
}
}
5、编写实体类User
package com.example.face.entity;
import lombok.Data;
@Data
public class User {
private int userID;
private String userName;
private String userPhoto;
private String userFaceId;
}
6、编写Mapper文件
在com.example.face.mapper包下编写FaceMapper接口
package com.example.face.mapper;
import com.example.face.entity.User;
public interface FaceMapper {
public User selectUserByName(String name);
public void addUsers(User user);
}
7、编写Mapper.xml文件
在resources.mappers目录下编写FaceMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.example.face.mapper.FaceMapper">
<select id="selectUserByName" resultType="com.example.face.entity.User" parameterType="String">
select * from `user` where userName=#{userName}
</select>
<insert id="addUsers" parameterType="com.example.face.entity.User">
insert into `user` (userID, userName, userPhoto,userFaceId) values (#{userID},#{userName},#{userPhoto},#{userFaceId});
</insert>
</mapper>
这里的namespace和每一个id一定要对!
8、创建service下的类
-
service接口FaceService
package com.example.face.service; import com.example.face.entity.User; public interface FaceService { public User selectUserByName(String name); public void addUsers(User user); }
-
service实现类FaceServiceImpl
package com.example.face.service.impl;
import com.example.face.entity.User;
import com.example.face.mapper.FaceMapper;
import com.example.face.service.FaceService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class FaceServiceImpl implements FaceService {
@Resource
private FaceMapper faceMapper;
@Override
public User selectUserByName(String name) {
return faceMapper.selectUserByName(name);
}
@Override
public void addUsers(User user) {
faceMapper.addUsers(user);
}
}
不要忘记加@Service
9、创建控制层FaceController
package com.example.face.controller;
import com.baidu.aip.face.AipFace;
import com.example.face.entity.User;
import com.example.face.service.FaceService;
import com.example.face.utils.AddFace;
import com.example.face.utils.BaiduFaceUtil;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.misc.BASE64Decoder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
@Controller
public class FaceController {
@Autowired
private FaceService userService;
@GetMapping("")
public String login(){
return "login";
}
@GetMapping("/toLogin")
public String toLogin(){
return "login";
}
@GetMapping("/toRegister")
public String toRegister(){
return "register";
}
@GetMapping("/toSuccess")
public String toSuccess(){
return "success";
}
@RequestMapping(value = "register", method = RequestMethod.POST)
@ResponseBody
public String register(String userName, String faceBase,String userFaceId) throws Exception {
if (!StringUtils.isEmpty(userName) && !StringUtils.isEmpty(faceBase)) {
//文件上传的地址
String upPath = ResourceUtils.getURL("classpath:").getPath() + "static\\images";
//用于查看路径是否正确
System.out.println(upPath);
// 图片名称
String fileName = userName + System.currentTimeMillis() + ".png";
System.out.println(upPath + "\\" + fileName);
File file = new File(upPath + "\\" + fileName);
//初始化百度云的AipFace
AipFace client = new AipFace(BaiduFaceUtil.APP_ID, BaiduFaceUtil.CLIENTID, BaiduFaceUtil.CLIENTSECRET);
//往自己demo数据库里插入一条用户数据
User user = new User();
user.setUserName(userName);
user.setUserPhoto(upPath + "\\" + fileName);
user.setUserFaceId(userFaceId);
User exitUser = userService.selectUserByName(userName);
if (exitUser != null) {
return "2";
}
userService.addUsers(user);
// 往自己demo服务器里面上传摄像头捕获的图片
GenerateImage(faceBase, "D:\\软件\\IDEA\\Floder\\Face-Login\\src\\main\\resources\\static\\images\\"+fileName);
//向百度云人脸库插入一张人脸
AddFace.add(user, "D:\\软件\\IDEA\\Floder\\Face-Login\\src\\main\\resources\\static\\images\\"+fileName);
}
return "1";
}
@RequestMapping(value = "login", method = RequestMethod.POST)
@ResponseBody
public String login(String faceBase) {
String faceData = faceBase;
//进行人像数据对比
AipFace client = new AipFace(BaiduFaceUtil.APP_ID, BaiduFaceUtil.CLIENTID, BaiduFaceUtil.CLIENTSECRET);
Double num = verifyUser(faceData, client);
if (num > 90) {
return "1";
} else {
return "2";
}
}
/**
* 人脸比对
*
* @param imgBash64 照片转bash64格式
* @param imgType 类型
* @param groupList 百度云人脸识别用户组
* @return
*/
public Double verifyUser(String imgBash64, AipFace client) {
// 传入可选参数调用接口
HashMap<String, String> options = new HashMap<String, String>();
JSONObject res = client.search(imgBash64, "BASE64", "u_g_1", options);
System.out.println(res.toString(2));
System.out.println(res.getJSONObject("result"));
System.out.println(res.getJSONObject("result").getJSONArray("user_list"));
JSONObject user = (JSONObject) res.getJSONObject("result").getJSONArray("user_list").get(0);
Double score = (Double) user.get("score");
return score;
}
// 对字节数组字符串进行Base64解码并生成图片
//imgFilePath 待保存的本地路径
public static boolean GenerateImage(String base64Str, String imgFilePath) {
if (base64Str == null) // 图像数据为空
return false;
BASE64Decoder decoder = new BASE64Decoder();
try {
// Base64解码
byte[] bytes = decoder.decodeBuffer(base64Str);
for (int i = 0; i < bytes.length; ++i) {
if (bytes[i] < 0) {// 调整异常数据
bytes[i] += 256;
}
}
// 生成jpeg图片
OutputStream out = new FileOutputStream(imgFilePath);
out.write(bytes);
out.flush();
out.close();
//====
return true;
} catch (Exception e) {
return false;
}
}
}
10、创建对应的login和success界面
Login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
/**解决浏览器兼容性问题**/
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
/**/
body {
background: url(img/bg03.jpg) no-repeat center;
}
h1 {
color: #fff;
text-align: center;
line-height: 80px;
}
.media {
width: 534px;
height: 400px;
margin: 40px auto 0;
}
#login-btn {
width: 200px;
height: 50px;
background-color: #2196f3;
margin: 60px auto 0;
text-align: center;
line-height: 50px;
color: #fff;
border-radius: 10px;
}
#register {
width: 200px;
height: 50px;
background-color: #2196f3;
margin: 60px auto 0;
text-align: center;
line-height: 50px;
color: #fff;
border-radius: 10px;
}
#canvas {
display: none;
}
#shuru {
width: 200px;
height: 50px;
background-color: #2196f3;
margin: 20px auto 0;
}
</style>
</head>
<body>
<h1>百度云人脸登录</h1>
<div id="shuru">
用户名:
<input type="text" name="username" id="username"/>
</div>
<div class="media">
<video id="video" width="450" height="300" src="" autoplay></video>
<canvas id="canvas" width="450" height="300"></canvas>
</div>
<button id="login-btn">确定登录</button>
<button id="register">注册</button>
<script type="text/javascript" th:src="@{/js/jquery-3.3.1.js}"></script>
<script type="text/javascript">
/**调用摄像头,获取媒体视频流**/
var video = document.getElementById('video');
//返回画布二维画图环境
var userContext = canvas.getContext("2d");
var getUserMedia =
//浏览器兼容,表示在火狐、Google、IE等浏览器都可正常支持
(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia)
//getUserMedia.call(要调用的对象,约束条件,调用成功的函数,调用失败的函数)
getUserMedia.call(navigator, {video: true, audio: false}, function (localMediaStream) {
//获取摄像头捕捉的视频流
video.srcObject = localMediaStream;
}, function (e) {
console.log("获取摄像头失败!!")
});
//点击按钮注册事件
var btn = document.getElementById("login-btn");
var registerBtn = document.getElementById("register");
registerBtn.onclick = function () {
window.location.href = "toRegister";
};
btn.onclick = function () {
var username = $("#username").val();
// alert($("#username").val());
if (username != null) {
//点击按钮时拿到登陆者面部信息
userContext.drawImage(video, 0, 0, 450, 300);
var userImgSrc = document.getElementById("canvas").toDataURL("img/png");
//拿到bash64格式的照片信息
var faceBase = userImgSrc.split(",")[1];
//ajax异步请求
$.ajax({
url: "login",
type: "post",
data: {
"faceBase": faceBase,
"userName": username
},
success: function (result) {
if (result === '1') {
alert("好一个帅小伙!!快去吧!");
window.location.href = "toSuccess";
} else if (result === '2') {
alert("还没有注册?快去注册!!");
} else {
alert("系统错误!!");
}
}
})
} else {
alert("用户名不能为空");
}
}
</script>
</body>
</html>
success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
success
</body>
</html>
测试
这样就可以成功实现人脸识别了,注册过后在百度云的库中会添加进入你的帅脸,然后在登录页面登录后,会在后台显示你的匹配百分比,如果超过百分之八十就可以匹配成功即登陆成功。快去试试吧。