前言
人脸识别,好高大上!!!
理解之后很简单。
支付宝使用的就是face++,
至于face++账号信息,apikey…..,本文不做讲述,网上很多.
一.设计思想
1. 先想一想,如果让你实现人脸识别,你会怎么做?
寻找图片上的关键点,制作一套算法,分析脸部信息,将得到的数据存入数据库,
登录的时候,通过同样的算法,分析数据,和数据库中存入的信息进行比对……
工作量好大!!!
face++有着它独有的非常优秀的算法,我们可以将我们的图片上传到face++服务器来获取对应的图片数据,剩下的事情就很简单了
2. 四个face++ api简介:
这里只介绍用到的api,
2.1 使用api肯定需要先注册一下信息,获取api_key和api_secret,可以注册试用的进行获取
2.2 create
作用: 创建一个FaceSet,创建一个脸部信息集合,引用官网的描述:
参数:api_key,api_secret,……
返回值: faceset_token, outer_id……,这里写的两个返回值需要记住,是这个脸部信息集合的唯一标识,具体返回值信息如下图:
2.3 addFace
作用:向脸部信息集合faceSet添加一条或多条脸部信息,便于后期搜索
参数: 不用说,肯定需要,api_key,api_secret,faceSet_token或outer_id(脸部信息唯一标识),还有图片信息,官网截图:
返回值:可以获取插入的结果信息
.
2.4 Search
作用: 传入一张图片信息到face++服务器,会返回最相似的face_token
参数:api_key,api_secret,image_url或image_file或image_base64或face_token,详细参数列表如下
返回值:返回值包含和你传入图片信息最像的图片的face_token,(可以再和数据库中对应的信息进行比较)
2.4 Detect
作用:
传入一张图片信息,获取这张图片的face_token,注意,一张相同图片获取多次的face_token不同
参数:api_key,api_secret, image_url或image_file或image_base64,
返回值:图片对应的face_token
在faces中包含face_token
3. 设计分析创建调用create api创建faceSet,取得faceSet_token,对应你的一张用户信息表
注册时:调用detect api传入用户注册的图片信息,获取face_token,
将face_token存入faceSet,(调用addFace api存入)
将face_token存入数据库登录: 从前端获取用户图片,将图片编码为base64作为参数image_base64调用search api
返回值为在faceSet中,和传入图片相似度高的face_token
通过返回的face_token,在数据库中进行查询,实现登陆
二.用到的技术
有了上面的分析,即使使用javaweb也能实现了
本案例使用
maven
java的ssm框架
配上Druid连接池
前端使用了jquery,(不懂前端,通过参考和自己设计写的很low)
三.实现
3.1前端界面:
实体类:
User{
String username;
String password;
String other; //在本案例中没有作用
String faceToken;
}
技术不高,自己写的一个简单的界面
注册界面:register.html
Title* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
body {
background: url(img/bg.jpg) no-repeat center;
}
h1 {
color: #fff;
text-align: center;
line-height: 80px;
}
.media {
width: 534px;
height: 400px;
margin: 40px auto 0;
transform-style: preserve-3d;
transform: perspective(600px) rotateY(25deg);
}
#register {
width: 200px;
height: 50px;
background: #0089ff;
margin: 60px auto 0;
text-align: center;
line-height: 50px;
color: #fff;
/* color: red;*/
border-radius: 16px;
transform-style: preserve-3d;
transform: perspective(600px) rotateY(25deg) rotateZ(-10deg);
cursor: pointer;
}
用户名:
密码 :
备注字段:
//这里写的是网页脚本
//调用摄像头获取媒体视频流
/***
* 默认的写法:navigator.getUserMedia
* 火狐:navigator.mozGetUserMedia
* 微软:navigator.msGetUserMedia
* 谷歌:navigator.webkitGetUserMedia
*
* @type {((constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback) => void) | *}
*/
var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
var video = document.getElementById("myVideo");
/***
* 四个参数 getUserMedia.call(navigator,{video:true,audio:false},function(){},function(){});
* 1.要调用的对象
* 2.约束对象:eg:只调用视频
* 3.调用成功的方法
* 4.调用失败的方法
*/
getUserMedia.call(navigator, {video: true, audio: false}, function (localMediaStream) {
//这里是调用成功的方法,如果调用成功,将视频流对象传到myVideo,localMediaStream是传入的视频流对象
/*document.getElementById("myVideo").src= window.URL.createObjectURL(localMediaStream);
* 上一行的代码已经过时了
* */
try {
video.src = window.URL.createObjectURL(localMediaStream);
} catch (e) {
//执行的是这段代码
video.srcObject = localMediaStream;
}
/***
* 下面三行代码可以代替了video的autoplay属性
*/
/* video.onloadedmetadata = function () {
video.play();
}*/
}, function (e) {
console.log("获取摄像头失败", e);//通过控制台将我们的错误信息打印
});
//获取登陆按钮
var btn_register = document.getElementById("register");
var toUpPic = document.getElementById("toUpPic");
//获取canvas对象
var canvas = document.getElementById("myCanvas");
//获取上下文对象
var context = canvas.getContext("2d");
//登陆按钮绑定点击事件
toUpPic.onclick = function () {
//点击登录按钮获取面部信息,(点击登录按钮的时候将图像画到)
// context.drawImage(video,x轴开始位置,y轴开始位置,x轴结束位置,y轴结束位置);
context.drawImage(video, 0, 0, 534, 400);
//image/png 表示画成什么格式
var imgSrc = document.getElementById("myCanvas").toDataURL("image/png");
alert(imgSrc);
// var Baseimg=imgSrc.split(",")[1];
$.post("getFaceToken.do", {imgSrc: imgSrc}, function (faceToken) {
alert(faceToken);
if (faceToken) {
$("#faceToken").val(faceToken);
} else {
alert("登录失败,请重新扫描");
}
});
}
btn_register.οnclick=function () {
$("#registerForm").submit();
}
登录界面:login.html
Title* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
body {
background: url(img/bg.jpg) no-repeat center;
}
h1 {
color: #fff;
text-align: center;
line-height: 80px;
}
.media {
width: 534px;
height: 400px;
margin: 40px auto 0;
transform-style: preserve-3d;
transform: perspective(600px) rotateY(25deg);
}
#login {
width: 200px;
height: 50px;
background: #0089ff;
margin: 60px auto 0;
text-align: center;
line-height: 50px;
color: #fff;
/* color: red;*/
border-radius: 16px;
transform-style: preserve-3d;
transform: perspective(600px) rotateY(25deg) rotateZ(-10deg);
cursor: pointer;
}
//这里写的是网页脚本
//调用摄像头获取媒体视频流
/***
* 默认的写法:navigator.getUserMedia
* 火狐:navigator.mozGetUserMedia
* 微软:navigator.msGetUserMedia
* 谷歌:navigator.webkitGetUserMedia
*
* @type {((constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback) => void) | *}
*/
var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
var video = document.getElementById("myVideo");
/***
* 四个参数 getUserMedia.call(navigator,{video:true,audio:false},function(){},function(){});
* 1.要调用的对象
* 2.约束对象:eg:只调用视频
* 3.调用成功的方法
* 4.调用失败的方法
*/
getUserMedia.call(navigator, {video: true, audio: false}, function (localMediaStream) {
//这里是调用成功的方法,如果调用成功,将视频流对象传到myVideo,localMediaStream是传入的视频流对象
/*document.getElementById("myVideo").src= window.URL.createObjectURL(localMediaStream);
* 上一行的代码已经过时了
* */
try {
video.src = window.URL.createObjectURL(localMediaStream);
} catch (e) {
//执行的是这段代码
video.srcObject = localMediaStream;
}
/***
* 下面三行代码可以代替了video的autoplay属性
*/
/* video.onloadedmetadata = function () {
video.play();
}*/
}, function (e) {
console.log("获取摄像头失败", e);//通过控制台将我们的错误信息打印
});
//获取登陆按钮
var btn_login = document.getElementById("login");
//获取canvas对象
var canvas=document.getElementById("myCanvas");
//获取上下文对象
var context = canvas.getContext("2d");
//登陆按钮绑定点击事件
btn_login.onclick = function () {
//点击登录按钮获取面部信息,(点击登录按钮的时候将图像画到)
// context.drawImage(video,x轴开始位置,y轴开始位置,x轴结束位置,y轴结束位置);
context.drawImage(video, 0, 0, 534, 400);
//image/png 表示画成什么格式
var imgSrc = document.getElementById("myCanvas").toDataURL("image/png");
alert(imgSrc);
// var Baseimg=imgSrc.split(",")[1];
$.post("login.do",{imgSrc:imgSrc},function (result) {
if(result){
location.href="success.jsp";
}else{
alert("登录失败,请重新扫描");
}
})
}
3.2后端界面
face相关类,通过face++官网查到一个demo,本案例修改demo并封装了自己的信息,打成实现功能
获取到的demo:
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import javax.net.ssl.SSLException;
public class FaceTest {
public static void main(String[] args) throws Exception{
File file = new File("你的本地图片路径");
byte[] buff = getBytesFromFile(file);
String url = "https://api-cn.faceplusplus.com/facepp/v3/detect";
HashMap map = new HashMap<>();
HashMap byteMap = new HashMap<>();
map.put("api_key", "你的KEY");
map.put("api_secret", "你的SECRET");
map.put("return_landmark", "1");
map.put("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,emotion,ethnicity,beauty,mouthstatus,eyegaze,skinstatus");
byteMap.put("image_file", buff);
try{
byte[] bacd = post(url, map, byteMap);
String str = new String(bacd);
System.out.println(str);
}catch (Exception e) {
e.printStackTrace();
}
}
private final static int CONNECT_TIME_OUT = 30000;
private final static int READ_OUT_TIME = 50000;
private static String boundaryString = getBoundary();
protected static byte[] post(String url, HashMap map, HashMap fileMap) throws Exception {
HttpURLConnection conne;
URL url1 = new URL(url);
conne = (HttpURLConnection) url1.openConnection();
conne.setDoOutput(true);
conne.setUseCaches(false);
conne.setRequestMethod("POST");
conne.setConnectTimeout(CONNECT_TIME_OUT);
conne.setReadTimeout(READ_OUT_TIME);
conne.setRequestProperty("accept", "*/*");
conne.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
conne.setRequestProperty("connection", "Keep-Alive");
conne.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)");
DataOutputStream obos = new DataOutputStream(conne.getOutputStream());
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry = (Map.Entry) iter.next();
String key = entry.getKey();
String value = entry.getValue();
obos.writeBytes("--" + boundaryString + "\r\n");
obos.writeBytes("Content-Disposition: form-data; name=\"" + key
+ "\"\r\n");
obos.writeBytes("\r\n");
obos.writeBytes(value + "\r\n");
}
if(fileMap != null && fileMap.size() > 0){
Iterator fileIter = fileMap.entrySet().iterator();
while(fileIter.hasNext()){
Map.Entry fileEntry = (Map.Entry) fileIter.next();
obos.writeBytes("--" + boundaryString + "\r\n");
obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey()
+ "\"; filename=\"" + encode(" ") + "\"\r\n");
obos.writeBytes("\r\n");
obos.write(fileEntry.getValue());
obos.writeBytes("\r\n");
}
}
obos.writeBytes("--" + boundaryString + "--" + "\r\n");
obos.writeBytes("\r\n");
obos.flush();
obos.close();
InputStream ins = null;
int code = conne.getResponseCode();
try{
if(code == 200){
ins = conne.getInputStream();
}else{
ins = conne.getErrorStream();
}
}catch (SSLException e){
e.printStackTrace();
return new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[4096];
int len;
while((len = ins.read(buff)) != -1){
baos.write(buff, 0, len);
}
byte[] bytes = baos.toByteArray();
ins.close();
return bytes;
}
private static String getBoundary() {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for(int i = 0; i < 32; ++i) {
sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length())));
}
return sb.toString();
}
private static String encode(String value) throws Exception{
return URLEncoder.encode(value, "UTF-8");
}
public static byte[] getBytesFromFile(File f) {
if (f == null) {
return null;
}
try {
FileInputStream stream = new FileInputStream(f);
ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = stream.read(b)) != -1)
out.write(b, 0, n);
stream.close();
out.close();
return out.toByteArray();
} catch (IOException e) {
}
return null;
}
}
哪里需要改?
四.总结
人无我有,人有我优
思路很清晰,具体实现很难!!!
实现后感觉很简单