欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-13 18:53:46
在公司做了个年会的签到、抽奖系统。用java web做的,用公司的办公app扫二维码码即可签到,扫完码就在大屏幕上显示这个人的照片。之后领导让我改得高大上一点,用人脸识别来...

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

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

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

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

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

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

下面给出代码

拍摄页面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>

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

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

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