我为公司年会做了签到和抽奖系统。用java web制作,可以用公司的办公app扫描二维码登录,扫码后在大屏幕上显示这个人的照片。之后领导让我高一点,用人脸识别签到,我就把扫二维码的步骤改成了人脸识别。
了解相关技术后,大致思路如下:第一,通过websocket与后台建立通信;使用trackingjs调用页面上的电脑摄像头,监控人脸,发现有人的脸已经进入屏幕,然后将图片转换成base64字符串,通过websocket发送到后端;后端获取图片,调用百度的人脸识别API,在人脸数据库中进行匹配(当然自己的人脸数据库要提前在百度云中建立),获取相似度最高的人的信息,将此人记录在登录列表中,然后将人脸数据库中此人的姓名、照片等信息返回给前端显示。流程图如图所示。
几天后,经过实际尝试,发现上面的想法有问题。websocket传输的数据最大大小为8KB,超过时会自动断开与后台的连接,无法传输图片。
于是我又改了一遍,直接进入流程图。事实上,它是将图片改为ajax给控制器
代码如下
拍摄佩奇trackingjs.jsp
% @ page语言=' Java ' ContentType=' text/html;charset=UTF-8 '页面编码=' UTF-8 ' %!DOCTYPE html html head meta http-equiv=' Content-Type ' Content=' text/html;charset=UTF-8 '标题在此插入标题/title脚本src=' http : js/jquery-1。9 .1 .js '/script script src=' http : js/tracking-min。js '/script script src=' http : js/face-min。js '/脚本样式* { padd :保证金: 0;} .容器{位置:相对;宽度: 581 px高度: 436像素;float:left}。消息{ float:left}视频,# canvas { position: absolute宽度: 581 px高度: 436像素;}/style script $(function(){ var video=document。getelementbyid(' video ');var canvas=文档。getelementbyid(' canvas ');var上下文=canvas。get context(' 2d ');定义变量捷径=document.getElementById('捷径');var scContext=捷径。GetContext(' 2d ');定义变量时间=10000;//向后台发照片的冷却时间var跟踪器=新跟踪对象跟踪器(“脸”);追踪器。setinitialscale(4);追踪器。setstep size(2);追踪器。setedgesdensity(0.1);tracking.track('#video ',tracker,{ camera : true });定义变量标志=真;tracker.on('track ',function(event){ if(event。数据。长度===0){上下文。清除矩形(0,0,canvas.width,canvas。高度);}else{ context.clearRect(0,0,canvas.width,canvas。高度);event.data.forEach(函数{上下文。strokestyle=' # ff 0000context.strokeRect(Rect.x,rect.y,rect.width,rect。高度);context . FillStyle=' # ff 0000//console . log(rect . x,rect.width,rect.y,rect。高度);});if(flag){ console.log('拍照');getPhoto();flag=false settimeout(function(){ flag=true;},时间);}else{ //console.log('冷却中');} } });函数getPhoto(){ sccontext。绘制图像(视频,0,0,290,218);var IMGstr=THreshold。tota toul(' image/png ');//讲拍照的图片数据发送到控制器,调用百度云,签到,返回签到结果$.ajax({ url:'identifyUser ',type:'post ',dataType:'json ',data : { imgstr 3360 imgstr。子串(imgstr。indexof(',')1) },success :函数(结果){ if(结果。result==' true '){ if(result。用户!=' 404 '){发送(' user _ info : '结果。用户);} } } });} var websocket=null//判断当前浏览器是否支持WebSocket if(')窗口中的web socket’){ web socket=新web socket(' ws ://localhost :8081/Baidu Face/web socket’);} else { alert('当前浏览器不支持websocket!请更换浏览器!');} //连接发生错误的回调方法web套接字。onerror=function(){ setMessageInnerHTML(' web套接字连接发生错误');};//连接成功建立的回调方法web套接字。onopen=function(){ setMessageInnerHTML(' web套接字连接成功');} ;//接收到消息的回调方法web套接字。on message=function(事件){ setMessageInnerHTML(事件。数据);};//连接关闭的回调方法web套接字。onclose=function(){ setMessageInnerHTML(' web套接字连接关闭');};//监听窗口关闭事件,当窗口关闭时,主动去关闭求转发到连接,防止连接还没断开就关闭窗口服务器端会抛异常窗户。onbeforeunload=function(){ closeWebSocket();};//将消息显示在网页上函数setmessageinnerHTMl(innerHTMl){ document。getelementbyid(' checkinMsg ').innerHTMl=innerHTMl ' br/';} //关闭WebSocket连接函数closeWebSocket(){ web socket。close();} //发送消息函数发送(消息){ web套接字。发送(消息);} });/script/head body div class=' container ' video id=' video ' preload autoplay loop muted/video canvas id=' canvas ' width=' 581 ' height=' 436 '/canvas/div class=' message ' canvas id='捷径width=' 290 ' height=' 218 '/canvas div id=' checkinMsg '/div/body/html控制器3360
@请求映射(值='/identifyUser ')public void identifyUser(httpersvletrequest请求,HttpServletResponse响应)引发IOException,中断的异常{响应。setheader(' Content-Type ',' application/JSON;charset=utf-8 ');PrintWriter pw=响应。getwriter();字符串IMgstr=请求。GetParameter(' IMGstr ');BaiduFaceAPI baiduApi=new BaiduFaceAPI();JSONObject obj=百度API。identifyuserbybase64(IMgstr);//返回百度云的计算结果系统。出去。println(obj。tostring());映射字符串,对象结果映射=新的哈希表字符串,对象();int result _ num=obj。getint(' result _ num ');//人脸个数if(result _ num==1){ JSON对象结果0=obj。getjsonarray(' result ').getJSONObject(0);resultMap.put('result ',' true ');双倍分数=结果0。getjsonarray('分数').getDouble(0);//与人脸库中最相似的人脸的相似度如果(得分=85){//暂且设为如果大于85则可以认为是同一个人resultMap.put('user ',结果0。getstring(' user _ info ');}else{ resultMap.put('user ',' 404 ');} }else{ resultMap.put('result ',' false ');} pw。写(网。SF。JSON。JSON对象。来自对象(结果图).toString());pw。flush();pw。close();}控制器中,BaiduFaceAPI类中的identifyUserBybase64()方法,以及base64字符串转字节[]的方法。百度云人脸识别文档地址:点击打开链接
公共类BaidFaceAPI {//设置阿普ID/AK/SK私有静态最终字符串' APP_ID='你的appid ';私有静态最终字符串' API_KEY='你的“API key”;私有静态最终字符串' SECRET_KEY='你的“秘密密钥”;//定义AipFace私有AipFace客户端;/** * 构造函数,实例化AipFace */public BaiduFaceAPI(){ client=new AipFace(APP _ ID,API_KEY,SECRET _ KEY);//可选:设置网络连接参数客户。setconnectiontimeoutinmillis(2000年);//建立连接的超时时间客户。setsockettimeutinmillis(60000);//通过打开的连接传输数据的超时时间(单位:毫秒) //可选:设置代理服务器地址,http和窝二选一,或者均不设置//客户端。sethttp proxy(' proxy _ host ',proxy _ port);//设置超文本传送协议(超文本传输协议的缩写)代理//客户端。setsocketproxy(' proxy _ host ',proxy _ port);//设置窝代理}//人脸识别。从人脸库中查找相似度最高的一张图片公共JSON对象identifyUserBybase64(String base64Str){//传入可选参数调用接口字符串选项=new HashMapString,String();//options.put('ext_fields ',' face liveness ');//判断活体options.put('user_top_num ',' 1 ');字符串group id=' group 1 byte[]byt=imageu til。base64 strtobytearray(base64 str);返回client.identifyUser(groupId,byt,options);} }公共静态字节[]base64 strtobytearray(String imgStr){//对字节数组字符串进行Base64解码并生成图片if (imgStr==null) //图像数据为空返回nullBASE64Decoder解码器解码器=新的base64解码器();尝试{ //Base64解码byte[]b=decoderbuffer。decoder buffer(IMgstr);for(int I=0;ib。长度;i) { if(b[i]0) {//调整异常数据b[I]=256;} }返回b;} catch(异常e){ 0返回null} } websocket服务端:
包裹。com。数字中国。交流。远程。服务;导入Java。io。ioexception导入Java。尼奥。Bytebuffer导入Java。乌提尔。并发。复制onwriterarrayset导入javax。web套接字。*;导入javax。web套接字。服务器。ServerEndPoint@ ServerEndpoint("/web套接字")公共类WebsocketServer { //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的私有静态int OnLineCount=0;//并发包的线程安全设置,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用地图来存放,其中钥匙可以为用户标识私有静态copy on writerrarraysetwebsocketserver webSocketSet=new copy on writerrarraysetwebsocketserver();//与某个客户端的连接会话,需要通过它来给客户端发送数据私有会话会话;/** * 连接建立成功调用的方法* @param会话可选的参数会议。为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@ on open public void on open(Session Session){ this。会话=会话;websocketset。添加(此);//加入设置中addOnlineCount();//在线数加System.out.println('有新连接加入!当前在线人数为getonline count());} /** * 连接关闭调用的方法*/@ OnClose public void OnClose(){ web socket set。移除(此);//从设置中删除subOnlineCount();//在线数减System.out.println('有一连接关闭!当前在线人数为getonline count());} /** * 收到客户端消息后调用的方法* @param消息客户端发送过来的消息* @param会话可选的参数*/@ on message public void on message(字符串消息,会话会话){ System.out.println('来自客户端的消息' : '消息);//群发消息对于(WebsocketServer项目: webSocketSet){ try { item。sendmail(message)};} catch(IOexception e){ e . print stack trace();继续;} } } /** * 发生错误时调用* @ param Session * @ param error */@ OnError public void OnError(Session Session,Throwable error){系统。出去。println('发生错误');错误。print stack trace();} /** * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法* @ param消息* @引发IOException */public void sendmail(字符串消息)引发IOException { this。会话。getbasicremote().发送文本(消息);//这个。会话。GetSyncRemote().发送文本(消息);} public static synchronized int get online count(){返回联机计数;} public static synchronized void addOnlineCount(){ web socket server。在线计数;} public static synchronized void subOnlineCount(){ web socket server。在线计数-;}}大屏幕欢迎页面jsp:
% @ page语言=' Java ' ContentType=' text/html;charset=UTF-8 '页面编码=' UTF-8 ' %!DOCTYPE html html head meta http-equiv=' Content-Type ' Content=' text/html;charset=UTF-8 '标题大屏幕/title脚本src=' http : js/jquery-1。9 .1 .js '/script脚本类型=' text/JavaScript ' $(function(){ var web socket=null;//判断当前浏览器是否支持WebSocket if(')窗口中的web socket’){ web socket=新web socket(' ws ://localhost :8081/Baidu Face/web socket’);} else { alert('当前浏览器不支持websocket!请更换浏览器!');} //连接发生错误的回调方法web套接字。onerror=function(){ setMessageInnerHTML(' web套接字连接发生错误');};//连接成功建立的回调方法web套接字。onopen=function(){ setMessageInnerHTML(' web套接字连接成功');} ;//接收到消息的回调方法web套接字。on message=function(事件){ setMessageInnerHTML(事件。数据);};//连接关闭的回调方法web套接字。onclose=function(){ setMessageInnerHTML(' web套接字连接关闭');};//监听窗口关闭事件,当窗口关闭时,主动去关闭求转发到连接,防止连接还没断开就关闭窗口服务器端会抛异常窗户。onbeforeunload=function(){ closeWebSocket();};//将消息显示在网页上函数setmessageinnerHTMl(innerHTMl){ document。getelementbyid(' checkinMsg ').innerHTMl=innerHTMl ' br/';} //关闭WebSocket连接函数closeWebSocket(){ web socket。close();} //发送消息函数发送(消息){ web套接字。发送(消息);} });/script/head body div id=' CheckInmsg '/div/body/html最后发张成果图,我事先在百度人脸库传了一张胡歌的图片,然后用手机打开一张胡歌的图片,让电脑摄像头拍摄,抓到了人脸,识别出了这是胡歌。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。