当前位置: 移动技术网 > IT编程>开发语言>JavaScript > trackingjs+websocket+百度人脸识别API实现人脸签到

trackingjs+websocket+百度人脸识别API实现人脸签到

2019年06月01日  | 移动技术网IT编程  | 我要评论

在公司做了个年会的签到、抽奖系统。用java web做的,用公司的办公app扫二维码码即可签到,扫完码就在大屏幕上显示这个人的照片。之后领导让我改得高大上一点,用人脸识别来签到,就把扫二维码的步骤改成人脸识别。

了解了相关技术后,大致思路如下:先用websocket与后台建立通讯;用trackingjs在页面调用电脑摄像头,监听人脸,发现有人脸进入屏幕了,就把图片转成base64字符串,通过websocket发送到后端;后端拿到图片,调用百度的人脸识别api,去人脸库中匹配(当然事先要在百度云建立好了自己的人脸库),得到相似度最高的那个人的信息,签到表中纪录这个人,然后把这个人在人脸库中的姓名、照片等信息返回给前端显示。流程图如图所示。

中间隔了几天,实际尝试后,发现上面的思路有问题,websocket传输的数据大小最大为8kb,超出就自动与后台断开了,没法传图片。

所以又改变了一下,直接上流程图。其实就是把图片改为用ajax传给controller

下面给出代码

拍摄页面trackingjs.jsp

<%@ page language="java" contenttype="text/html; charset=utf-8" pageencoding="utf-8"%>
<!doctype html >
<html>
<head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>insert title here</title>
 <script src="js/jquery-1.9.1.js"></script>
 <script src="js/tracking-min.js"></script>
 <script src="js/face-min.js"></script>
 <style>
  * {
   padding: 0;
   margin: 0;
  }
 
  .container {
   position: relative;
   width: 581px;
   height: 436px;
   float:left;
  }
  .message{
   float:left;
  }
  video, #canvas {
   position: absolute;
   width: 581px;
   height: 436px;
  }
 
 </style>
 <script>
  $(function () {
   var video = document.getelementbyid('video');
   var canvas = document.getelementbyid('canvas');
   var context = canvas.getcontext('2d');
   var shortcut = document.getelementbyid('shortcut');
   var sccontext = shortcut.getcontext('2d');
 var time =10000;//向后台发照片的冷却时间
 
   var tracker = new tracking.objecttracker('face');
   tracker.setinitialscale(4);
   tracker.setstepsize(2);
   tracker.setedgesdensity(0.1);
 
   tracking.track('#video', tracker, {camera: true});
 var flag=true;
   tracker.on('track', function (event) {
   if (event.data.length === 0) {
   context.clearrect(0, 0, canvas.width, canvas.height);
   }else{
   context.clearrect(0, 0, canvas.width, canvas.height);
   event.data.foreach(function (rect) {
     context.strokestyle = '#ff0000';
     context.strokerect(rect.x, rect.y, rect.width, rect.height);
     context.fillstyle = "#ff0000";
     //console.log(rect.x, rect.width, rect.y, rect.height);
    });
   if(flag){
   console.log("拍照");
   getphoto();
   flag=false;
   settimeout(function(){flag=true;},time);
   }else{
   //console.log("冷却中");
   }
   }
   });
   
   function getphoto() {
   sccontext.drawimage(video,0,0,290,218);
   var imgstr = shortcut.todataurl("image/png");
   
   //讲拍照的图片数据发送到controller,调用百度云,签到,返回签到结果
   $.ajax({
   url:"identifyuser",
   type:"post",
   datatype:"json",
   data:{
   imgstr:imgstr.substring(imgstr.indexof(",")+1)
   },
   success:function(result){
   if(result.result == "true"){
    if(result.user != "404"){
    send("user_info:"+result.user);
    }
   }
    
   }
   });
   
   }
 
   
  var websocket = null; 
  //判断当前浏览器是否支持websocket 
  if ('websocket' in window) { 
  websocket = new websocket("ws://localhost:8081/baiduface/websocket"); 
  } else { 
  alert('当前浏览器不支持websocket!请更换浏览器!');
  } 
  //连接发生错误的回调方法 
  websocket.onerror = function () { 
  setmessageinnerhtml("websocket连接发生错误"); 
  }; 
  //连接成功建立的回调方法
  websocket.onopen = function () { 
  setmessageinnerhtml("websocket连接成功"); 
  } ;
  
  //接收到消息的回调方法 
  websocket.onmessage = function (event) { 
  setmessageinnerhtml(event.data); 
  };
  //连接关闭的回调方法
  websocket.onclose = function () { 
  setmessageinnerhtml("websocket连接关闭"); 
  };
  //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
  window.onbeforeunload = function () { 
  closewebsocket(); 
  }; 
  //将消息显示在网页上 
  function setmessageinnerhtml(innerhtml) { 
  document.getelementbyid('checkinmsg').innerhtml += innerhtml + '<br/>'; 
  } 
  //关闭websocket连接 
  function closewebsocket() { 
  websocket.close(); 
  } 
  //发送消息 
  function send(msg) { 
  websocket.send(msg); 
  } 
  });
 
 </script>
  
</head>
<body>
 <div class="container">
  <video id="video" preload autoplay loop muted></video>
  <canvas id="canvas" width="581" height="436"></canvas>
 </div>
 <div class="message">
 <canvas id="shortcut" width="290" height="218" ></canvas>
 <div id="checkinmsg"></div>
 </div>
</body>
</html>

controller:

@requestmapping(value="/identifyuser")
 public void identifyuser(httpservletrequest request,httpservletresponse response) throws ioexception, interruptedexception{
 response.setheader("content-type", "application/json;charset=utf-8");
 printwriter pw= response.getwriter();
 
 string imgstr = request.getparameter("imgstr");
 
 baidufaceapi baiduapi = new baidufaceapi();
 jsonobject obj= baiduapi.identifyuserbybase64(imgstr);//返回百度云的计算结果
 system.out.println(obj.tostring());
 
 map<string, object> resultmap = new hashmap<string, object>();
 
 int result_num = obj.getint("result_num");//人脸个数
 if(result_num == 1){
 jsonobject result0 = obj.getjsonarray("result").getjsonobject(0);
 resultmap.put("result", "true");
 double score = result0.getjsonarray("scores").getdouble(0);//与人脸库中最相似的人脸的相似度
 if(score>=85){//暂且设为如果大于85则可以认为是同一个人
 resultmap.put("user",result0.getstring("user_info"));
 }else{
 resultmap.put("user","404");
 }
 }else{
 resultmap.put("result","false");
 }
 pw.write(net.sf.json.jsonobject.fromobject(resultmap).tostring());
 pw.flush();
 pw.close();
 }

controller 中,baidufaceapi类中的 identifyuserbybase64()方法,以及base64字符串转byte[]的方法。
百度云人脸识别文档地址:

public class baidufaceapi {
 //设置appid/ak/sk
 private static final string app_id = "你的appid";
 private static final string api_key = "你的apikey";
 private static final string secret_key = "你的secretkey";
 //定义aipface
 private aipface client; 
 
 /**
 * 构造函数,实例化aipface
 */
 public baidufaceapi(){
  client = new aipface(app_id, api_key, secret_key);
  // 可选:设置网络连接参数
  client.setconnectiontimeoutinmillis(2000);//建立连接的超时时间
  client.setsockettimeoutinmillis(60000);//通过打开的连接传输数据的超时时间(单位:毫秒)
 
  // 可选:设置代理服务器地址, http和socket二选一,或者均不设置
  //client.sethttpproxy("proxy_host", proxy_port); // 设置http代理
  //client.setsocketproxy("proxy_host", proxy_port); // 设置socket代理
  
 }//人脸识别。从人脸库中查找相似度最高的1张图片
 public jsonobject identifyuserbybase64(string base64str){
  // 传入可选参数调用接口
  hashmap<string, string> options = new hashmap<string, string>();
  //options.put("ext_fields", "faceliveness");//判断活体
  options.put("user_top_num", "1");
  string groupid = "group1";
  byte[] byt = imageutil.base64strtobytearray(base64str);
  return client.identifyuser(groupid, byt, options);
 
 }
 
 
}
public static byte[] base64strtobytearray(string imgstr) 
 { //对字节数组字符串进行base64解码并生成图片 
  if (imgstr == null) //图像数据为空 
   return null; 
  base64decoder decoder = new base64decoder(); 
  try 
  { 
   //base64解码 
   byte[] b = decoder.decodebuffer(imgstr); 
   for(int i=0;i<b.length;++i) 
   { 
    if(b[i]<0) 
    {//调整异常数据 
     b[i]+=256; 
    } 
   } 
   return b;
  } 
  catch (exception e) 
  { 
   return null; 
  } 
 } 

websocket服务端:

package com.digitalchina.communication.remote.service;
 
import java.io.ioexception;
import java.nio.bytebuffer;
import java.util.concurrent.copyonwritearrayset;
 
import javax.websocket.*;
import javax.websocket.server.serverendpoint;
 
@serverendpoint("/websocket")
public class websocketserver {
 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
 private static int onlinecount = 0;
 
 //concurrent包的线程安全set,用来存放每个客户端对应的mywebsocket对象。若要实现服务端与单一客户端通信的话,可以使用map来存放,其中key可以为用户标识
 private static copyonwritearrayset<websocketserver> websocketset = new copyonwritearrayset<websocketserver>();
 
 //与某个客户端的连接会话,需要通过它来给客户端发送数据
 private session session;
 
 /**
 * 连接建立成功调用的方法
 * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
 */
 @onopen
 public void onopen(session session){
 this.session = session;
 websocketset.add(this); //加入set中
 addonlinecount(); //在线数加
 system.out.println("有新连接加入!当前在线人数为" + getonlinecount());
 }
 
 /**
 * 连接关闭调用的方法
 */
 @onclose
 public void onclose(){
 websocketset.remove(this); //从set中删除
 subonlinecount(); //在线数减
 system.out.println("有一连接关闭!当前在线人数为" + getonlinecount());
 }
 
 /**
 * 收到客户端消息后调用的方法
 * @param message 客户端发送过来的消息
 * @param session 可选的参数
 */
 @onmessage
 public void onmessage(string message, session session) {
 system.out.println("来自客户端的消息:" + message);
 //群发消息
 for(websocketserver item: websocketset){
 try {
 item.sendmessage(message);
 } catch (ioexception e) {
 e.printstacktrace();
 continue;
 }
 }
 }
 
 /**
 * 发生错误时调用
 * @param session
 * @param error
 */
 @onerror
 public void onerror(session session, throwable error){
 system.out.println("发生错误");
 error.printstacktrace();
 }
 
 /**
 * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
 * @param message
 * @throws ioexception
 */
 public void sendmessage(string message) throws ioexception{
 this.session.getbasicremote().sendtext(message);
 //this.session.getasyncremote().sendtext(message);
 }
 
 public static synchronized int getonlinecount() {
 return onlinecount;
 }
 
 public static synchronized void addonlinecount() {
 websocketserver.onlinecount++;
 }
 public static synchronized void subonlinecount() {
 websocketserver.onlinecount--;
 }
}

大屏幕欢迎页面jsp:

<%@ page language="java" contenttype="text/html; charset=utf-8" pageencoding="utf-8"%>
 
<!doctype html >
<html>
<head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>大屏幕</title>
 <script src="js/jquery-1.9.1.js"></script>
 
 <script type="text/javascript">
 
 $(function(){
 var websocket = null; 
 //判断当前浏览器是否支持websocket 
 if ('websocket' in window) { 
 websocket = new websocket("ws://localhost:8081/baiduface/websocket"); 
 } else { 
 alert('当前浏览器不支持websocket!请更换浏览器!');
 } 
 //连接发生错误的回调方法 
 websocket.onerror = function () { 
 setmessageinnerhtml("websocket连接发生错误"); 
 }; 
 //连接成功建立的回调方法
 websocket.onopen = function () { 
 setmessageinnerhtml("websocket连接成功"); 
 } ;
 
 //接收到消息的回调方法 
 websocket.onmessage = function (event) { 
 setmessageinnerhtml(event.data); 
 };
 //连接关闭的回调方法
 websocket.onclose = function () { 
 setmessageinnerhtml("websocket连接关闭"); 
 };
 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
 window.onbeforeunload = function () { 
 closewebsocket(); 
 }; 
 //将消息显示在网页上 
 function setmessageinnerhtml(innerhtml) { 
 document.getelementbyid('checkinmsg').innerhtml += innerhtml + '<br/>'; 
 } 
 //关闭websocket连接 
 function closewebsocket() { 
 websocket.close(); 
 } 
 //发送消息 
 function send(msg) { 
 websocket.send(msg); 
 } 
 });
 
 </script>
</head>
<body>
 <div id="checkinmsg"></div>
</body>
</html>

最后发张成果图,我事先在百度人脸库传了一张胡歌的图片,然后用手机打开一张胡歌的图片,让电脑摄像头拍摄,抓到了人脸,识别出了这是胡歌。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网