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


Posted in Javascript onNovember 26, 2018

在公司做了个年会的签到、抽奖系统。用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实现人脸签到

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

Javascript 相关文章推荐
alert中断settimeout计时功能
Jul 26 Javascript
DOM基础教程之使用DOM控制表单
Jan 20 Javascript
Bootstrap教程JS插件滚动监听学习笔记分享
May 18 Javascript
基于Phantomjs生成PDF的实现方法
Nov 07 Javascript
微信小程序 底部导航栏目开发资料
Dec 05 Javascript
Vue.js实现输入框绑定的实例代码
Aug 24 Javascript
详解tween.js的使用教程
Sep 14 Javascript
Vue下路由History模式打包后页面空白的解决方法
Jun 29 Javascript
微信小程序开发注意指南和优化实践(小结)
Jun 21 Javascript
基于vue+uniapp直播项目实现uni-app仿抖音/陌陌直播室功能
Nov 12 Javascript
vue中实现图片压缩 file文件的方法
May 28 Javascript
js面试题之异步问题的深入理解
Sep 20 Javascript
tracking.js页面人脸识别插件使用方法
Apr 16 #Javascript
详解Node.js 中使用 ECDSA 签名遇到的坑
Nov 26 #Javascript
Vue.js的动态组件模板的实现
Nov 26 #Javascript
解决Vue开发中对话框被遮罩层挡住的问题
Nov 26 #Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
Nov 26 #Javascript
electron制作仿制qq聊天界面的示例代码
Nov 26 #Javascript
Vuex的初探与实战小结
Nov 26 #Javascript
You might like
根德Grundig S400/S500/S700电路分析
2021/03/02 无线电
NOT NULL 和NULL
2007/01/15 PHP
PHP中MVC模式的模板引擎开发经验分享
2011/03/23 PHP
如何批量替换相对地址为绝对地址(利用bat批处理实现)
2013/05/27 PHP
PHP实现中文圆形印章特效
2015/06/19 PHP
jquery 模式对话框终极版实现代码
2009/09/28 Javascript
在JavaScript中监听IME键盘输入事件
2011/05/29 Javascript
jQuery 选择器项目实例分析及实现代码
2012/12/28 Javascript
动态的绑定事件addEventListener方法的使用
2014/01/24 Javascript
浅析Bootstrap缩略图组件与警示框组件
2016/04/29 Javascript
jquery自适应布局的简单实例
2016/05/28 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
Vue.js@2.6.10更新内置错误处机制Fundebug同步支持相应错误监控
2019/05/13 Javascript
JS实现的雪花飘落特效示例
2019/12/03 Javascript
vue实现下拉菜单树
2020/10/22 Javascript
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
2016/06/14 Python
python如何获取服务器硬件信息
2017/05/11 Python
用生成器来改写直接返回列表的函数方法
2017/05/25 Python
python如何实现int函数的方法示例
2018/02/19 Python
python3连接kafka模块pykafka生产者简单封装代码
2019/12/23 Python
Python实现屏幕录制功能的代码
2020/03/02 Python
基于python图像处理API的使用示例
2020/04/03 Python
keras模型保存为tensorflow的二进制模型方式
2020/05/25 Python
python变量的作用域是什么
2020/05/26 Python
美国知名的旅游网站:OneTravel
2018/10/09 全球购物
eharmony澳大利亚:网上约会服务
2020/02/29 全球购物
环境科学专业研究生求职信
2013/10/02 职场文书
工厂厂长的职责
2013/12/12 职场文书
旅游管理专业个人求职信范文
2013/12/24 职场文书
社团活动总结模板
2014/06/30 职场文书
暑期培训班策划方案
2014/08/26 职场文书
公务员群众路线心得体会
2014/11/03 职场文书
证券公司客户经理岗位职责
2015/04/09 职场文书
python 机器学习的标准化、归一化、正则化、离散化和白化
2021/04/16 Python
python 使用tkinter与messagebox写界面和弹窗
2022/03/20 Python
python turtle绘图
2022/05/04 Python