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 相关文章推荐
Javascript模块模式分析
May 16 Javascript
jquery列表拖动排列(由项目提取相当好用)
Jun 17 Javascript
JavaScript的面向对象编程基础
Aug 13 Javascript
微信小程序 火车票查询实例讲解
Oct 17 Javascript
easyui下拉框动态级联加载的示例代码
Nov 29 Javascript
vue微信分享 vue实现当前页面分享其他页面
Dec 02 Javascript
JS实现多物体运动的方法详解
Jan 23 Javascript
详解vue中axios的封装
Jul 18 Javascript
JavaScript引用类型Object常见用法实例分析
Aug 08 Javascript
深入理解Vue 组件之间传值
Aug 16 Javascript
jQuery使用bind动态绑定事件无效的处理方法
Dec 11 jQuery
D3.js(v3)+react 实现带坐标与比例尺的柱形图 (V3版本)
May 09 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
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
成本8450万,票房仅2亿,口碑两极分化,又一部DC电影扑街了
2020/04/09 欧美动漫
yii上传文件或图片实例
2014/04/01 PHP
PHP实现获取图片颜色值的方法
2014/07/11 PHP
php实现excel中rank函数功能的方法
2015/01/20 PHP
php身份证号码检查类实例
2015/06/18 PHP
PHP自定义图片缩放函数实现等比例不失真缩放的方法
2016/08/19 PHP
Zend Framework数据库操作方法实例总结
2016/12/11 PHP
YII2框架中查询生成器Query()的使用方法示例
2020/03/18 PHP
JavaScript null和undefined区别分析
2009/10/14 Javascript
JS this作用域以及GET传输值过长的问题解决方法
2013/08/06 Javascript
没有document.getElementByName方法
2013/08/19 Javascript
将中国标准时间转换成标准格式的代码
2014/03/20 Javascript
jQuery带进度条全屏图片轮播特效代码分享
2020/06/28 Javascript
JavaScript运行过程中的“预编译阶段”和“执行阶段”
2015/12/16 Javascript
详解webpack4多入口、多页面项目构建案例
2018/05/25 Javascript
微信小程序左滑动显示菜单功能的实现
2018/06/14 Javascript
一文了解Vue中的nextTick
2019/05/06 Javascript
VSCode 配置uni-app的方法
2020/07/11 Javascript
vue+element-ui JYAdmin后台管理系统模板解析
2020/07/28 Javascript
pymongo实现控制mongodb中数字字段做加法的方法
2015/03/26 Python
Python实现程序的单一实例用法分析
2015/06/03 Python
在Python的Django框架中生成CSV文件的方法
2015/07/22 Python
关于win10在tensorflow的安装及在pycharm中运行步骤详解
2020/03/16 Python
Keras - GPU ID 和显存占用设定步骤
2020/06/22 Python
PyCharm最新激活码PyCharm2020.2.3有效
2020/11/18 Python
美国面料纺织品商城:Fabric.com
2017/06/28 全球购物
eDreams意大利:南欧领先的在线旅行社
2018/11/23 全球购物
房地产销售大学生自我评价分享
2013/11/11 职场文书
房屋租赁协议书范本
2014/04/10 职场文书
2016年共产党员个人承诺书
2016/03/24 职场文书
Nest.js参数校验和自定义返回数据格式详解
2021/03/29 Javascript
django上传文件的三种方式
2021/04/29 Python
Python3 如何开启自带http服务
2021/05/18 Python
十大最强电系宝可梦,阿尔宙斯电系之一,第七被称为雷神
2022/03/18 日漫
Python的代理类实现,控制访问和修改属性的权限你都了解吗
2022/03/21 Python