首先要在 face++ 注册一个账号,并且创建一个应用,拿到 api key 和 api secret;
下载 java 接入工具,一个 jar 包:https://github.com/FacePlusPlus/facepp-java-sdk
请求返回的数据是 json 格式,需要导入 json 解析包:
commons-beanutils-1.8.3.jar
commons-lang-2.5.jar
commons-collections-3.2.2.jar
ezmorph-1.0.6.jar
json-lib-2.3-jdk15.jar
图片上传类:FileUpLoad.java
1 package com.krry; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.List; 8 import java.util.UUID; 9 import javax.servlet.annotation.WebServlet; 10 import javax.servlet.http.HttpServlet; 11 import javax.servlet.http.HttpServletRequest; 12 import javax.servlet.http.HttpServletResponse; 13 14 import org.apache.commons.fileupload.FileItem; 15 import org.apache.commons.fileupload.FileItemFactory; 16 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 17 import org.apache.commons.fileupload.servlet.ServletFileUpload; 18 19 /** 20 * FileUpLoad 21 * @author krry 22 * 23 */ 24 @WebServlet(name="fileUp",urlPatterns={"/show"}) 25 public class FileUpLoad extends HttpServlet{ 26 27 private static final long serialVersionUID = 1L; 28 29 @Override 30 public void service(HttpServletRequest request,HttpServletResponse response) throws IOException{ 31 //判断是不是文件上传,根据请求包里面的内容是否为二进制提交的上传的文件 true/false 32 boolean isMultPart = ServletFileUpload.isMultipartContent(request); 33 if(isMultPart){ 34 //创建文件上传工厂 35 FileOutputStream out = null; 36 InputStream in = null; 37 try { 38 FileItemFactory factory = new DiskFileItemFactory(); 39 //创建容器文件类,负责处理上传的文件数据 40 ServletFileUpload upload = new ServletFileUpload(factory); 41 //解析上传文件,其实就是解析表单传过来的数据 42 List<FileItem> fileItems = upload.parseRequest(request); 43 //只需要拿一张图片 44 in = fileItems.get(0).getInputStream(); 45 //获取源文件名 46 String Ofilename = fileItems.get(0).getName(); 47 //拿到后缀名 48 String ext = Ofilename.substring(Ofilename.indexOf("."), Ofilename.length()); 49 //拿到一个目录,绝对地址 50 String path =request.getSession().getServletContext().getRealPath("/"); 51 //生成一个唯一的名字 52 String fileName = UUID.randomUUID().toString() + ext; 53 //定义上传目录 54 String dirPath = path+"/upload"; 55 File dirFile = new File(dirPath); 56 //如果此文件夹不存在 57 if(!dirFile.exists()){ 58 dirFile.mkdirs();//创建此文件夹 59 } 60 //配置文件路径 61 String filePath = path+"/upload/"+fileName; 62 //输出流 63 out = new FileOutputStream(filePath); 64 //边读取边编写 字节流 65 byte[] b = new byte[1024]; 66 int length = 0; 67 while((length = in.read(b)) != -1){ //读到字节数组里面去 68 out.write(b,0,length); //从0开始写,写你读了多少 69 } 70 //放在request作用域里 71 request.setAttribute("imgSrc", filePath); 72 request.setAttribute("imgSrcPage", "upload/"+fileName); 73 //请求转发,转发到另一个servlet 74 request.getRequestDispatcher("showImage").forward(request, response); 75 } catch (Exception e) { 76 77 e.printStackTrace(); 78 }finally{ 79 out.close(); 80 in.close(); 81 } 82 } 83 } 84 85 }
人脸识别类:Analysis.java
这里返回一个 json 字符串,放到作用域,在前台获取
1 package com.krry; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.DataOutputStream; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.net.HttpURLConnection; 10 import java.net.URL; 11 import java.net.URLEncoder; 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.HashMap; 15 import java.util.Iterator; 16 import java.util.List; 17 import java.util.Map; 18 import java.util.Random; 19 import java.util.TreeMap; 20 21 import javax.net.ssl.SSLException; 22 import javax.servlet.annotation.WebServlet; 23 import javax.servlet.http.HttpServlet; 24 import javax.servlet.http.HttpServletRequest; 25 import javax.servlet.http.HttpServletResponse; 26 27 import net.sf.json.JSONArray; 28 import net.sf.json.JSONObject; 29 30 /** 31 * analysis 32 * @author krry 33 * String 转json 需要的jar包:版本不能弄错 34 * commons-beanutils-1.8.3.jar 35 commons-lang-2.5.jar 36 commons-collections-3.2.2.jar 37 ezmorph-1.0.6.jar 38 json-lib-2.3-jdk15.jar 39 */ 40 @WebServlet(name="analysis",urlPatterns={"/showImage"}) 41 public class Analysis extends HttpServlet{ 42 43 private static final long serialVersionUID = 1L; 44 45 @Override 46 public void service(HttpServletRequest request,HttpServletResponse response) throws IOException{ 47 48 //获取上传的图片地址 49 String imgPath = request.getAttribute("imgSrc").toString(); 50 File file = new File(imgPath); 51 byte[] buff = getBytesFromFile(file); 52 String url = "https://api-cn.faceplusplus.com/facepp/v3/detect"; 53 HashMap<String, String> map = new HashMap<String, String>(); 54 HashMap<String, byte[]> byteMap = new HashMap<String, byte[]>(); 55 map.put("api_key", "15N9eBF_ieE-N2nvI7oK_A9anh2meuUS"); 56 map.put("api_secret", "cBnvsIAo8VaOj53lXCWlpuOSd0NrsFif"); 57 map.put("return_landmark", "1"); 58 map.put("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,emotion,ethnicity,beauty,mouthstatus,eyegaze,skinstatus"); 59 byteMap.put("image_file", buff); 60 try{ 61 byte[] bacd = post(url, map, byteMap); 62 String str = new String(bacd); 63 64 JSONObject json = JSONObject.fromObject(str); 65 JSONArray faces = json.getJSONArray("faces"); 66 67 //解析失败 68 if(str.indexOf("error_message") != -1 || faces.size() == 0){ 69 request.getSession().setAttribute("error", "解析失败,请重新选择"); 70 //重定向到首页 71 response.sendRedirect("index.jsp"); 72 }else{ 73 //将人脸信息字符串str信息放到作用域 74 request.setAttribute("face", str); 75 //请求转发,返回到页面 76 request.getRequestDispatcher("showImage.jsp").forward(request, response); 77 } 78 }catch (Exception e) { 79 e.printStackTrace(); 80 } 81 82 } 83 84 85 private final static int CONNECT_TIME_OUT = 30000; 86 private final static int READ_OUT_TIME = 50000; 87 private static String boundaryString = getBoundary(); 88 89 protected static byte[] post(String url, HashMap<String, String> map, HashMap<String, byte[]> fileMap) throws Exception { 90 HttpURLConnection conne; 91 URL url1 = new URL(url); 92 conne = (HttpURLConnection) url1.openConnection(); 93 conne.setDoOutput(true); 94 conne.setUseCaches(false); 95 conne.setRequestMethod("POST"); 96 conne.setConnectTimeout(CONNECT_TIME_OUT); 97 conne.setReadTimeout(READ_OUT_TIME); 98 conne.setRequestProperty("accept", "*/*"); 99 conne.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString); 100 conne.setRequestProperty("connection", "Keep-Alive"); 101 conne.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)"); 102 DataOutputStream obos = new DataOutputStream(conne.getOutputStream()); 103 Iterator iter = map.entrySet().iterator(); 104 while(iter.hasNext()){ 105 Map.Entry<String, String> entry = (Map.Entry) iter.next(); 106 String key = entry.getKey(); 107 String value = entry.getValue(); 108 obos.writeBytes("--" + boundaryString + " "); 109 obos.writeBytes("Content-Disposition: form-data; name="" + key 110 + "" "); 111 obos.writeBytes(" "); 112 obos.writeBytes(value + " "); 113 } 114 if(fileMap != null && fileMap.size() > 0){ 115 Iterator fileIter = fileMap.entrySet().iterator(); 116 while(fileIter.hasNext()){ 117 Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIter.next(); 118 obos.writeBytes("--" + boundaryString + " "); 119 obos.writeBytes("Content-Disposition: form-data; name="" + fileEntry.getKey() 120 + ""; filename="" + encode(" ") + "" "); 121 obos.writeBytes(" "); 122 obos.write(fileEntry.getValue()); 123 obos.writeBytes(" "); 124 } 125 } 126 obos.writeBytes("--" + boundaryString + "--" + " "); 127 obos.writeBytes(" "); 128 obos.flush(); 129 obos.close(); 130 InputStream ins = null; 131 int code = conne.getResponseCode(); 132 try{ 133 if(code == 200){ 134 ins = conne.getInputStream(); 135 }else{ 136 ins = conne.getErrorStream(); 137 } 138 }catch (SSLException e){ 139 e.printStackTrace(); 140 return new byte[0]; 141 } 142 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 143 byte[] buff = new byte[4096]; 144 int len; 145 while((len = ins.read(buff)) != -1){ 146 baos.write(buff, 0, len); 147 } 148 byte[] bytes = baos.toByteArray(); 149 ins.close(); 150 return bytes; 151 } 152 private static String getBoundary() { 153 StringBuilder sb = new StringBuilder(); 154 Random random = new Random(); 155 for(int i = 0; i < 32; ++i) { 156 sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length()))); 157 } 158 return sb.toString(); 159 } 160 private static String encode(String value) throws Exception{ 161 return URLEncoder.encode(value, "UTF-8"); 162 } 163 164 public static byte[] getBytesFromFile(File f) { 165 if (f == null) { 166 return null; 167 } 168 try { 169 FileInputStream stream = new FileInputStream(f); 170 ByteArrayOutputStream out = new ByteArrayOutputStream(1000); 171 byte[] b = new byte[1000]; 172 int n; 173 while ((n = stream.read(b)) != -1) 174 out.write(b, 0, n); 175 stream.close(); 176 out.close(); 177 return out.toByteArray(); 178 } catch (IOException e) { 179 } 180 return null; 181 } 182 183 184 }
解析 json 字符串,先转换成 json 格式,json 数组,一个个获取人脸属性
1 <%@page import="net.sf.json.JSONObject"%> 2 <%@page import="net.sf.json.JSONArray"%> 3 <%@ page language="java" import="java.io.File" pageEncoding="utf-8"%> 4 <%@page import="java.math.BigDecimal"%> 5 <%@ page import="java.util.*" %> 6 <% 7 8 String path = request.getContextPath(); 9 int port = request.getServerPort(); 10 String basePath = null; 11 if(port==80){ 12 basePath = request.getScheme()+"://"+request.getServerName()+path; 13 }else{ 14 basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path; 15 } 16 pageContext.setAttribute("basePath", basePath); 17 18 19 Object str = request.getAttribute("face"); 20 JSONObject json = JSONObject.fromObject(str); 21 JSONArray faces = json.getJSONArray("faces"); 22 StringBuffer strBuff = new StringBuffer(); 23 //识别出人脸的个数 24 int length = faces.size(); 25 26 //识别出多少个人脸,就是循环多少次 27 for(int i = 0;i < length;i++){ 28 29 JSONObject face = faces.getJSONObject(i); //拿到第i+1个人脸部信息 30 JSONObject attribute = face.getJSONObject("attributes");//拿到脸部属性 31 32 //年龄 33 JSONObject age = attribute.getJSONObject("age"); 34 int ageValue = age.getInt("value"); 35 36 //性别 37 JSONObject gender = attribute.getJSONObject("gender"); 38 String sex = gender.getString("value"); 39 if(sex.equals("Male")) sex = "男"; 40 else sex = "女"; 41 42 //人种 43 JSONObject ethnicity = attribute.getJSONObject("ethnicity"); 44 String races = ethnicity.getString("value"); 45 if(races.equals("Asian")) races = "亚洲人"; 46 else if(races.equals("White")) races = "白人"; 47 else races = "黑人"; 48 49 //微笑程度 50 JSONObject smile = attribute.getJSONObject("smile"); 51 String smileRange = String.format("%.2f",smile.getDouble("value")); 52 53 //表情 54 JSONObject emotion = attribute.getJSONObject("emotion"); 55 Map<String,Double> mapp = new TreeMap<String,Double>(); 56 //装配表情信息到map 57 double sadness = emotion.getDouble("sadness"); 58 mapp.put("伤心", sadness); 59 double neutral = emotion.getDouble("neutral"); 60 mapp.put("平静", neutral); 61 double disgust = emotion.getDouble("disgust"); 62 mapp.put("厌恶", disgust); 63 double anger = emotion.getDouble("anger"); 64 mapp.put("愤怒", anger); 65 double happiness = emotion.getDouble("happiness"); 66 mapp.put("高兴", happiness); 67 double surprise = emotion.getDouble("surprise"); 68 mapp.put("惊讶", surprise); 69 double fear = emotion.getDouble("fear"); 70 mapp.put("恐惧", fear); 71 72 //利用list取最大值 73 List<Double> listmap = new ArrayList<Double>(); 74 for(String key:mapp.keySet()){ 75 listmap.add(mapp.get(key)); 76 } 77 //取到最大值 78 double valueMax = Collections.max(listmap); 79 //根据map的value获取map的key 80 String emotionMax = ""; 81 for (Map.Entry<String, Double> entry : mapp.entrySet()) { 82 if(valueMax == entry.getValue()){ 83 emotionMax = entry.getKey(); 84 } 85 } 86 87 //颜值分数 88 JSONObject beauty = attribute.getJSONObject("beauty"); 89 String beautys = ""; 90 if(sex.equals("男")) beautys = String.format("%.2f",beauty.getDouble("male_score")); 91 else beautys = String.format("%.2f",beauty.getDouble("female_score")); 92 93 //面部状态 94 JSONObject skinstatus = attribute.getJSONObject("skinstatus"); 95 Map<String,String> mapSkin = new TreeMap<String,String>(); 96 //装配信息到map 97 double health = skinstatus.getDouble("health"); 98 mapSkin.put("健康指数", String.format("%.2f",health)); 99 double stain = skinstatus.getDouble("stain"); 100 mapSkin.put("色斑指数", String.format("%.2f",stain)); 101 double acne = skinstatus.getDouble("acne"); 102 mapSkin.put("青春痘指数", String.format("%.2f",acne)); 103 double dark_circle = skinstatus.getDouble("dark_circle"); 104 mapSkin.put("黑眼圈指数", String.format("%.2f",dark_circle)); 105 106 strBuff.append("<br>"); 107 strBuff.append("<span>年龄:").append(ageValue).append("岁</span>"); 108 strBuff.append("<span>性别:").append(sex).append("</span>"); 109 strBuff.append("<br>").append("<br>"); 110 strBuff.append("<span>人种:").append(races+"</span>"); 111 strBuff.append("<span>微笑程度:").append(smileRange+"%</span>"); 112 strBuff.append("<br>").append("<br>"); 113 strBuff.append("<span>表情:").append(emotionMax+"</span>"); 114 strBuff.append("<span>颜值分数:").append(beautys+"</span>"); 115 strBuff.append("<br>").append("<br>"); 116 int counts = 0; 117 for(String key:mapSkin.keySet()){ 118 counts++; 119 strBuff.append("<span>"+key+":").append(mapSkin.get(key)+"</span>"); 120 if(counts % 2 == 0) strBuff.append("<br>").append("<br>"); 121 } 122 123 strBuff.append("<br>"); 124 125 } 126 127 128 129 %> 130 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 131 <html> 132 <head> 133 134 <title> Java开发人脸特征识别系统 --krry</title> 135 136 <meta http-equiv="pragma" content="no-cache"> 137 <meta http-equiv="cache-control" content="no-cache"> 138 <meta http-equiv="expires" content="0"> 139 <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 140 <meta http-equiv="description" content="This is my page"> 141 142 <style type="text/css"> 143 *{padding:0px;margin:0px;} 144 body{background: #FFF;background:url("images/78788.jpg");background-size:cover;} 145 .a_btn{text-decoration:none;font-family:"幼圆";background:#fff;transition:.6s;color:#f60;cursor:pointer;width:100px;height:40px;border-radius:10px;display: inline-block;text-align:center;line-height:40px;} 146 .aa{position: absolute;left: 48px;top: 31px;} 147 .a_btn:hover{background:red;color:#fff;transition:.6s;} 148 .clear{clear:both;} 149 .title{margin-top: 25px;font-size:48px;font-family:"幼圆";color: #ff7272;text-shadow: 1px 1px 3px #ff3737;text-align:center;line-height:45px;} 150 .content{margin:0 auto;width:900px;} 151 .content .img{float:left;margin-top:30px;} 152 .content .img img{border-radius:10px;} 153 .content .describe{margin-top:30px;width:500px;color:#000;font-family:"幼圆";font-size:18px;float:right;} 154 .content .describe span{width: 190px;display: inline-block;} 155 </style> 156 157 </head> 158 159 <body> 160 <div class="aa"><a class="a_btn" href="${basePath}">返回</a></div> 161 <div class="title">人脸识别结果</div> 162 <div class="content"> 163 <div class = "img"> 164 <img style="float:left" width="350" src="${imgSrcPage}" /> 165 </div> 166 <div style="float:right" class="describe"><%=strBuff.toString() %></div> 167 <div class="clear"></div> 168 </div> 169 </body> 170 </html>
首页:
1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 2 3 <% 4 5 Object ermsg = request.getSession().getAttribute("error"); 6 StringBuffer strBuff = new StringBuffer(); 7 if(ermsg != null){ 8 //清除作用域 9 request.getSession().removeAttribute("error"); 10 strBuff.append("loading('"+ermsg+"',4)"); 11 } 12 %> 13 14 <!doctype html> 15 <html> 16 <head> 17 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 18 <meta name="keywords" content="关键词,关键词"> 19 <meta name="description" content=""> 20 <link rel="stylesheet" type="text/css" href="css/sg.css" /> 21 <link rel="stylesheet" type="text/css" href="css/sg/css/animate.css" /> 22 <link rel="stylesheet" type="text/css" href="css/sg/css/sg.css" /> 23 24 <title> Java开发人脸特征识别系统 --krry</title> 25 <style type="text/css"> 26 *{padding:0px;margin:0px;} 27 body{background:url("images/5.jpg");background-size:cover;font-size:18px;font-family:"微软雅黑";color:#666;} 28 img{border:none;} 29 h1{font-size:24px;} 30 .title{margin-top: 30px;font-size:48px;font-family:"幼圆";color: #ff7272;text-shadow: 1px 1px 3px #ff3737;text-align:center;line-height:45px;} 31 .box{margin:100px auto;text-align:center;} 32 .box .b_btn{width:100px;height:40px;background:#fff;transition:.6s;font-size:16px;font-family:"微软雅黑";display:block;margin:54px auto;text-align:center; 33 cursor:pointer;line-height:40px;text-decoration:none;color:#F60;border:1px;border-radius:12px} 34 .box .b_btn:hover{background:red;color:#fff;transition:.6s;} 35 .busbu{top:0;background:rgba(0, 0, 0, 0.2);display:none;width:100%;height:100%;position:absolute;z-index:1000;} 36 </style> 37 </head> 38 39 <body> 40 <!--action 提交的路径 method:post(打包传输) get(明文传输) enctype(采用二进制)--> 41 <form action="show" method="post" enctype="multipart/form-data"> 42 <div class="box"> 43 <div class="title">人脸特征识别系统 --krry</div> 44 <input type="file" id="fileChoice" accept="image/jpeg,image/png" style="display:none" name="files"/> 45 <a class="b_btn" href="javascript:void(0);" onclick="choice()">选择照片</a> 46 <input type="button" value="提交解析" class="b_btn" id="but"/> 47 <input type="submit" class="sub_btn" style="display:none;"/> 48 </div> 49 </form> 50 <div class="busbu"></div> 51 <script src="js/jquery-1.11.3.min.js"></script> 52 <script src="js/sg.js"></script> 53 <script src="js/sgutil.js"></script> 54 <script type="text/javascript" src="css/sg/tz_util.js"></script> 55 <script type="text/javascript" src="css/sg/sg.js"></script> 56 <script type="text/javascript"> 57 <%=strBuff.toString() %> 58 //选择照片 59 function choice(){ 60 //js 弱类型 快速定位 61 var choose = document.getElementById("fileChoice"); 62 choose.click(); 63 } 64 $("#but").click(function(){ 65 if($("#fileChoice").val()){//如果选择了照片 66 $(".sub_btn").click(); 67 $(".busbu").show(); 68 loading("正在解析",900);//再展示 69 }else{ 70 $.tmDialog.alert({open:"top",content:"请先选择一张照片",title:"Krry温馨提醒"}); 71 } 72 }); 73 74 </script> 75 </body> 76 77 </html>
需要注意的点,json 字符串的转换和解析,获取 json 中人脸属性的方法