http://blog.csdn.net/lyq8479/article/details/17362685

为了防止网页丢失还是自己保存一份安全一点

人脸检测API介绍

在Face++网站的“API文档”中,能够看到Face++提供的所有API,我们要使用的人脸检测接口是detect分类下的“/detection/detect”,它能够检测出给定图片(Image)中的所有人脸(Face)的位置和相应的面部属性,目前面部属性包括性别(gender)、年龄(age)、种族(race)、微笑程度(smiling)、眼镜(glass)和姿势(pose)。

读者可以在http://cn.faceplusplus.com/uc/doc/home?id=69中了解到人脸检测接口的详细信息,该接口的请求地址如下:

[html] view plaincopyprint?
  1. http://apicn.faceplusplus.com/v2/detection/detect?url=URL&api_secret=API_SECRET&api_key=API_KEY
http://apicn.faceplusplus.com/v2/detection/detect?url=URL&api_secret=API_SECRET&api_key=API_KEY

调用上述接口,必须要传入参数api_key、api_secret和待检测的图片。其中,待检测的图片可以是URL,也可以是POST方式提交的二进制数据。在微信公众账号后台,接收用户发送的图片,得到的是图片的访问路径(PicUrl),因此,在本例中,直接使用待检测图片的URL是最方便的。调用人脸检测接口返回的是JSON格式数据如下:

{"face": [{"attribute": {"age": {"range": 5,"value": 23},"gender": {"confidence": 99.9999,"value": "Female"},"glass": {"confidence": 99.945,"value": "None"},"pose": {"pitch_angle": {"value": 17},"roll_angle": {"value": 0.735735},"yaw_angle": {"value": -2}},"race": {"confidence": 99.6121,"value": "Asian"},"smiling": {"value": 4.86501}},"face_id": "17233b4b1b51ac91e391e5afe130eb78","position": {"center": {"x": 49.4,"y": 37.6},"eye_left": {"x": 43.3692,"y": 30.8192},"eye_right": {"x": 56.5606,"y": 30.9886},"height": 26.8,"mouth_left": {"x": 46.1326,"y": 44.9468},"mouth_right": {"x": 54.2592,"y": 44.6282},"nose": {"x": 49.9404,"y": 38.8484},"width": 26.8},"tag": ""}],"img_height": 500,"img_id": "22fd9efc64c87e00224c33dd8718eec7","img_width": 500,"session_id": "38047ad0f0b34c7e8c6efb6ba39ed355","url": "http://cn.faceplusplus.com/wp-content/themes/faceplusplus.zh/assets/img/demo/1.jpg?v=4"
}

这里只对本文将要实现的“人脸检测”功能中主要用到的参数进行说明,参数说明如下:

1)face是一个数组,当一张图片中包含多张人脸时,所有识别出的人脸信息都在face数组中。

2)age中的value表示估计年龄,range表示误差范围。例如,上述结果中value=23,range=5,表示人的真实年龄在18岁至28岁左右。

3)gender中的value表示性别,男性为Male,女性为Female;gender中的confidence表示检测结果的可信度。

4)race中的value表示人种,黄色人种为Asian,白色人种为White,黑色人种为Black;race中的confidence表示检测结果的可信度。

5)center表示人脸框中心点坐标,可以将x用于计算人脸的左右顺序,即x坐标的值越小,人脸的位置越靠近图片的左侧。

人脸检测API的使用方法

为了方便开发者调用人脸识别API,Face++团队提供了基于Objective-C、Java(Android)、Matlab、Ruby、C#等多种语言的开发工具包,读者可以在Face++网站的“工具下载”版块下载相关的SDK。在本例中,笔者并不打算使用官方提供的SDK进行开发,主要原因如下:1)人脸检测API的调用比较简单,自己写代码实现也并不复杂;2)如果使用SDK进行开发,笔者还要花费大量篇幅介绍SDK的使用,这些并不是本文的重点;3)自己写代码实现比较灵活。当图片中有多张人脸时,人脸检测接口返回的数据是无序的,开发者可以按照实际使用需求进行排序,例如,将图片中的人脸按照从左至右的顺序进行排序。

编程调用人脸检测API

首先,要对人脸检测接口返回的结构进行封装,建立与之对应的Java对象。由于人脸检测接口返回的参数较多,笔者只是将本例中需要用到的参数抽取出来,封装成Face对象,对应的代码如下:

package org.liufeng.course.pojo;/*** Face Model* * @author liufeng* @date 2013-12-18*/
public class Face implements Comparable<Face> {// 被检测出的每一张人脸都在Face++系统中的标识符private String faceId;// 年龄估计值private int ageValue;// 年龄估计值的正负区间private int ageRange;// 性别:Male/Femaleprivate String genderValue;// 性别分析的可信度private double genderConfidence;// 人种:Asian/White/Blackprivate String raceValue;// 人种分析的可信度private double raceConfidence;// 微笑程度private double smilingValue;// 人脸框的中心点坐标private double centerX;private double centerY;public String getFaceId() {return faceId;}public void setFaceId(String faceId) {this.faceId = faceId;}public int getAgeValue() {return ageValue;}public void setAgeValue(int ageValue) {this.ageValue = ageValue;}public int getAgeRange() {return ageRange;}public void setAgeRange(int ageRange) {this.ageRange = ageRange;}public String getGenderValue() {return genderValue;}public void setGenderValue(String genderValue) {this.genderValue = genderValue;}public double getGenderConfidence() {return genderConfidence;}public void setGenderConfidence(double genderConfidence) {this.genderConfidence = genderConfidence;}public String getRaceValue() {return raceValue;}public void setRaceValue(String raceValue) {this.raceValue = raceValue;}public double getRaceConfidence() {return raceConfidence;}public void setRaceConfidence(double raceConfidence) {this.raceConfidence = raceConfidence;}public double getSmilingValue() {return smilingValue;}public void setSmilingValue(double smilingValue) {this.smilingValue = smilingValue;}public double getCenterX() {return centerX;}public void setCenterX(double centerX) {this.centerX = centerX;}public double getCenterY() {return centerY;}public void setCenterY(double centerY) {this.centerY = centerY;}// 根据人脸中心点坐标从左至右排序
    @Overridepublic int compareTo(Face face) {int result = 0;if (this.getCenterX() > face.getCenterX())result = 1;elseresult = -1;return result;}
}

与普通Java类不同的是,Face类实现了Comparable接口,并实现了该接口的compareTo()方法,这正是Java中对象排序的关键所在。112-119行代码是通过比较每个Face的脸部中心点的横坐标来决定对象的排序方式,这样能够实现检测出的多个Face按从左至右的先后顺序进行排序。

接下来,是人脸检测API的调用及相关处理逻辑,笔者将这些实现全部封装在FaceService类中,该类的完整实现如下:

package org.liufeng.course.service;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.liufeng.course.pojo.Face;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;/*** 人脸检测服务* * @author liufeng* @date 2013-12-18*/
public class FaceService {/*** 发送http请求* * @param requestUrl 请求地址* @return String*/private static String httpRequest(String requestUrl) {StringBuffer buffer = new StringBuffer();try {URL url = new URL(requestUrl);HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();httpUrlConn.setDoInput(true);httpUrlConn.setRequestMethod("GET");httpUrlConn.connect();// 将返回的输入流转换成字符串InputStream inputStream = httpUrlConn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}bufferedReader.close();inputStreamReader.close();// 释放资源inputStream.close();inputStream = null;httpUrlConn.disconnect();} catch (Exception e) {e.printStackTrace();}return buffer.toString();}/*** 调用Face++ API实现人脸检测* * @param picUrl 待检测图片的访问地址* @return List<Face> 人脸列表*/private static List<Face> faceDetect(String picUrl) {List<Face> faceList = new ArrayList<Face>();try {// 拼接Face++人脸检测的请求地址String queryUrl = "http://apicn.faceplusplus.com/v2/detection/detect?url=URL&api_secret=API_SECRET&api_key=API_KEY";// 对URL进行编码queryUrl = queryUrl.replace("URL", java.net.URLEncoder.encode(picUrl, "UTF-8"));queryUrl = queryUrl.replace("API_KEY", "替换成自己的API Key");queryUrl = queryUrl.replace("API_SECRET", "替换成自己的API Secret");// 调用人脸检测接口String json = httpRequest(queryUrl);// 解析返回json中的Face列表JSONArray jsonArray = JSONObject.fromObject(json).getJSONArray("face");// 遍历检测到的人脸for (int i = 0; i < jsonArray.size(); i++) {// faceJSONObject faceObject = (JSONObject) jsonArray.get(i);// attributeJSONObject attrObject = faceObject.getJSONObject("attribute");// positionJSONObject posObject = faceObject.getJSONObject("position");Face face = new Face();face.setFaceId(faceObject.getString("face_id"));face.setAgeValue(attrObject.getJSONObject("age").getInt("value"));face.setAgeRange(attrObject.getJSONObject("age").getInt("range"));face.setGenderValue(genderConvert(attrObject.getJSONObject("gender").getString("value")));face.setGenderConfidence(attrObject.getJSONObject("gender").getDouble("confidence"));face.setRaceValue(raceConvert(attrObject.getJSONObject("race").getString("value")));face.setRaceConfidence(attrObject.getJSONObject("race").getDouble("confidence"));face.setSmilingValue(attrObject.getJSONObject("smiling").getDouble("value"));face.setCenterX(posObject.getJSONObject("center").getDouble("x"));face.setCenterY(posObject.getJSONObject("center").getDouble("y"));faceList.add(face);}// 将检测出的Face按从左至右的顺序排序Collections.sort(faceList);} catch (Exception e) {faceList = null;e.printStackTrace();}return faceList;}/*** 性别转换(英文->中文)* * @param gender* @return*/private static String genderConvert(String gender) {String result = "男性";if ("Male".equals(gender))result = "男性";else if ("Female".equals(gender))result = "女性";return result;}/*** 人种转换(英文->中文)* * @param race* @return*/private static String raceConvert(String race) {String result = "黄色";if ("Asian".equals(race))result = "黄色";else if ("White".equals(race))result = "白色";else if ("Black".equals(race))result = "黑色";return result;}/*** 根据人脸识别结果组装消息* * @param faceList 人脸列表* @return*/private static String makeMessage(List<Face> faceList) {StringBuffer buffer = new StringBuffer();// 检测到1张脸if (1 == faceList.size()) {buffer.append("共检测到 ").append(faceList.size()).append(" 张人脸").append("\n");for (Face face : faceList) {buffer.append(face.getRaceValue()).append("人种,");buffer.append(face.getGenderValue()).append(",");buffer.append(face.getAgeValue()).append("岁左右").append("\n");}}// 检测到2-10张脸else if (faceList.size() > 1 && faceList.size() <= 10) {buffer.append("共检测到 ").append(faceList.size()).append(" 张人脸,按脸部中心位置从左至右依次为:").append("\n");for (Face face : faceList) {buffer.append(face.getRaceValue()).append("人种,");buffer.append(face.getGenderValue()).append(",");buffer.append(face.getAgeValue()).append("岁左右").append("\n");}}// 检测到10张脸以上else if (faceList.size() > 10) {buffer.append("共检测到 ").append(faceList.size()).append(" 张人脸").append("\n");// 统计各人种、性别的人数int asiaMale = 0;int asiaFemale = 0;int whiteMale = 0;int whiteFemale = 0;int blackMale = 0;int blackFemale = 0;for (Face face : faceList) {if ("黄色".equals(face.getRaceValue()))if ("男性".equals(face.getGenderValue()))asiaMale++;elseasiaFemale++;else if ("白色".equals(face.getRaceValue()))if ("男性".equals(face.getGenderValue()))whiteMale++;elsewhiteFemale++;else if ("黑色".equals(face.getRaceValue()))if ("男性".equals(face.getGenderValue()))blackMale++;elseblackFemale++;}if (0 != asiaMale || 0 != asiaFemale)buffer.append("黄色人种:").append(asiaMale).append("男").append(asiaFemale).append("女").append("\n");if (0 != whiteMale || 0 != whiteFemale)buffer.append("白色人种:").append(whiteMale).append("男").append(whiteFemale).append("女").append("\n");if (0 != blackMale || 0 != blackFemale)buffer.append("黑色人种:").append(blackMale).append("男").append(blackFemale).append("女").append("\n");}// 移除末尾空格buffer = new StringBuffer(buffer.substring(0, buffer.lastIndexOf("\n")));return buffer.toString();}/*** 提供给外部调用的人脸检测方法* * @param picUrl 待检测图片的访问地址* @return String*/public static String detect(String picUrl) {// 默认回复信息String result = "未识别到人脸,请换一张清晰的照片再试!";List<Face> faceList = faceDetect(picUrl);if (null != faceList) {result = makeMessage(faceList);}return result;}public static void main(String[] args) {String picUrl = "http://pic11.nipic.com/20101111/6153002_002722872554_2.jpg";System.out.println(detect(picUrl));}
}

上述代码虽然多,但条理很清晰,并不难理解,所以笔者只挑重点的进行讲解,主要说明如下:

1)70行:参数url表示图片的链接,由于链接中存在特殊字符,作为参数传递时必须进行URL编码。请读者记住:不管是什么应用,调用什么接口,凡是通过GET传递的参数中可能会包含特殊字符,都必须进行URL编码,除了中文以外,特殊字符还包括等号“=”、与“&”、空格“ ”等。

2)76-97行:使用JSON-lib解析人脸检测接口返回的JSON数据,并将解析结果存入List中。

3)99行:对集合中的对象进行排序,使用Collections.sort()方法排序的前提是集合中的Face对象实现了Comparable接口。

4)146-203行:组装返回给用户的消息内容。考虑到公众平台的文本消息内容长度有限制,当一张图片中识别出的人脸过多,则只返回一些汇总信息给用户。

5)211-219行:detect()方法是public的,提供给其他类调用。笔者可以在本地的开发工具中运行上面的main()方法,测试detect()方法的输出。

公众账号后台的实现

在公众账号后台的CoreService类中,需要对用户发送的消息类型进行判断,如果是图片消息,则调用人脸检测方法进行分析,如果是其他消息,则返回人脸检测的使用指南。CoreService类的完整代码如下:

package org.liufeng.course.service;import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.liufeng.course.message.resp.TextMessage;
import org.liufeng.course.util.MessageUtil;/*** 核心服务类* * @author liufeng* @date 2013-12-19*/
public class CoreService {/*** 处理微信发来的请求*/public static String processRequest(HttpServletRequest request) {// 返回给微信服务器的xml消息String respXml = null;try {// xml请求解析Map<String, String> requestMap = MessageUtil.parseXml(request);// 发送方帐号(open_id)String fromUserName = requestMap.get("FromUserName");// 公众帐号String toUserName = requestMap.get("ToUserName");// 消息类型String msgType = requestMap.get("MsgType");// 回复文本消息TextMessage textMessage = new TextMessage();textMessage.setToUserName(fromUserName);textMessage.setFromUserName(toUserName);textMessage.setCreateTime(new Date().getTime());textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);// 图片消息if (MessageUtil.REQ_MESSAGE_TYPE_IMAGE.equals(msgType)) {// 取得图片地址String picUrl = requestMap.get("PicUrl");// 人脸检测String detectResult = FaceService.detect(picUrl);textMessage.setContent(detectResult);}// 其它类型的消息elsetextMessage.setContent(getUsage());respXml = MessageUtil.textMessageToXml(textMessage);} catch (Exception e) {e.printStackTrace();}return respXml;}/*** 人脸检测帮助菜单*/public static String getUsage() {StringBuffer buffer = new StringBuffer();buffer.append("人脸检测使用指南").append("\n\n");buffer.append("发送一张清晰的照片,就能帮你分析出种族、年龄、性别等信息").append("\n");buffer.append("快来试试你是不是长得太着急");return buffer.toString();}
}

到这里,人脸检测应用就全部开发完成了,整个项目的完整结构如下:

运行结果如下:

笔者用自己的相片测试了两次,测试结果分别是26岁、30岁,这与笔者的实际年龄相差不大,可见,Face++的人脸检测准确度还是比较高的。为了增加人脸检测应用的趣味性和娱乐性,笔者忽略了年龄估计值的正负区间。读者可以充分发挥自己的想像力和创造力,使用Face++ API实现更多实用、有趣的功能。应用开发不是简单的接口调用!

FACE++学习二、获得face属性相关推荐

  1. VUE学习(二十)、插槽

    VUE学习(二十).插槽 一.默认插槽 1.Category.vue <template><div class="category"><h3>{ ...

  2. C#多线程学习(二) 如何操纵一个线程

    C#多线程学习(二) 如何操纵一个线程 原文链接:http://kb.cnblogs.com/page/42529/ [1] C#多线程学习(二) 如何操纵一个线程 [2] C#多线程学习(二) 如何 ...

  3. mysql用创建的用户登陆并修改表格_MySQL 基础学习二:创建一个用户表,并增删改查...

    MySQL 基础学习二:创建一个用户表,并 增删改查 提示:MySQL 命令建议都用大写,因为小写运行时,还是翻译成大写的. 第一步,创建一个用户表 1,打开控制台,进入数据库 C:\Users\Ad ...

  4. PyTorch框架学习二——基本数据结构(张量)

    PyTorch框架学习二--基本数据结构(张量) 一.什么是张量? 二.Tensor与Variable(PyTorch中) 1.Variable 2.Tensor 三.Tensor的创建 1.直接创建 ...

  5. 学习日报 1026 使用属性升级MyBank

    学习日报 1026 使用属性升级MyBank 访问修饰符 理解访问修饰符 公开的与私有的 任何对象都会有公开的一面 任何对象也会有私有的一面 餐厅的大厅与后厨 大厅是公开的,食客随意走动 后厨是私有的 ...

  6. 深度学习二(Pytorch物体检测实战)

    深度学习二(Pytorch物体检测实战) 文章目录 深度学习二(Pytorch物体检测实战) 1.PyTorch基础 1.1.基本数据结构:Tensor 1.1.1.Tensor数据类型 1.1.2. ...

  7. (转)MyBatis框架的学习(二)——MyBatis架构与入门

    http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图:  下面作简要概述: ...

  8. 机器学习之深度学习 二分类、多分类、多标签分类、多任务分类

    多任务学习可以运用到许多的场景. 首先,多任务学习可以学到多个任务的共享表示,这个共享表示具有较强的抽象能力,能够适应多个不同但相关的目标,通常可以使主任务获取更好的泛化能力. 此外,由于使用了共享表 ...

  9. MongoDB学习笔记~对集合属性的操作

    $unset清除元素 请注意在单个数组元素上使用$unset的结果可能与你设想的不一样.其结果只是将元素的值设置为null,而非删除整个元素.要想彻底删除某个数组元素,可以用$pull 和$pop操作 ...

  10. Elasticsearch 学习(二).实战使用

    Elasticsearch 学习(二).实战使用 参考:http://www.passjava.cn/#/01.PassJava/02.PassJava_Architecture/15.Elastic ...

最新文章

  1. read-sequence的返回值
  2. Python—实训day11—Pyecharts绘图
  3. [luogu P2590 ZJOI2008] 树的统计 (树链剖分)
  4. 多线程爬虫191023
  5. postgres 密码更改
  6. PAT1070. 结绳
  7. SpringCloudSpringBootmybatis分布式微服务云架构-hystrix参数详解
  8. Vue打包项目图片等静态资源的处理
  9. Push or pull?
  10. 【Flutter】flutter doctor 报错Android license status unknown. Run `flutter doctor --android-licenses‘
  11. java单线程爬虫使用Jsoup爬取bt磁力链接
  12. fiddler抓不到pc微信小程序包解决办法
  13. 阿铭Linux_网站维护学习笔记20190410
  14. 阿里巴巴国际站之关键词整理
  15. matlab 深度网络,深度信念网络matlab代码
  16. SQL-按照最新时间分组
  17. python中使用modbus_tk操作浮点数
  18. 手机兼容性测试--testin云测
  19. 幼儿园管理系统测试报告
  20. 简单的菜单,进行修改menu4

热门文章

  1. Mendix入门教程第一篇-demo实例
  2. linux服务ssh详解
  3. 云计算——云计算服务类型
  4. JSP汽车维修服务管理系统myeclipse开发SqlServer数据库bs框架java编程web网页结构
  5. C++控制台输出彩色文字
  6. 如何判断邮箱号格式是否正确
  7. Python自动化测试——接口基础详解(1)
  8. 水果忍者pc版《3DM-Fruit Ninja HD-CHS-full》 fruit.bin zip解压密码分析
  9. html图片右侧文字链接,超链接文字的 右边有个图片,是用css弄在一起的吗?_html/css_WEB-ITnose...
  10. Job for mongod.service failed ——一次因为Linux权限问题导致的MongoDB启动失败