首先要在 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 + "\r\n");
109 obos.writeBytes("Content-Disposition: form-data; name=\"" + key
110 + "\"\r\n");
111 obos.writeBytes("\r\n");
112 obos.writeBytes(value + "\r\n");
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 + "\r\n");
119 obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey()
120 + "\"; filename=\"" + encode(" ") + "\"\r\n");
121 obos.writeBytes("\r\n");
122 obos.write(fileEntry.getValue());
123 obos.writeBytes("\r\n");
124 }
125 }
126 obos.writeBytes("--" + boundaryString + "--" + "\r\n");
127 obos.writeBytes("\r\n");
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 中人脸属性的方法
git:https://github.com/Krryxa/face