FACE++学习二、获得face属性
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中了解到人脸检测接口的详细信息,该接口的请求地址如下:
- 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/Female
private String genderValue;
// 性别分析的可信度
private double genderConfidence;
// 人种:Asian/White/Black
private 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;
} // 根据人脸中心点坐标从左至右排序
@Override
public int compareTo(Face face) {
int result = 0;
if (this.getCenterX() > face.getCenterX())
result = 1;
else
result = -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++) {
// face
JSONObject faceObject = (JSONObject) jsonArray.get(i);
// attribute
JSONObject attrObject = faceObject.getJSONObject("attribute");
// position
JSONObject 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++;
else
asiaFemale++;
else if ("白色".equals(face.getRaceValue()))
if ("男性".equals(face.getGenderValue()))
whiteMale++;
else
whiteFemale++;
else if ("黑色".equals(face.getRaceValue()))
if ("男性".equals(face.getGenderValue()))
blackMale++;
else
blackFemale++;
}
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);
}
// 其它类型的消息
else
textMessage.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属性的更多相关文章
- SVG 学习<二>进阶 SVG世界,视野,视窗 stroke属性 svg分组
目录 SVG 学习<一>基础图形及线段 SVG 学习<二>进阶 SVG世界,视野,视窗 stroke属性 svg分组 SVG 学习<三>渐变 SVG 学习<四 ...
- Java开发学习(二十二)----Spring事务属性、事务传播行为
一.事务配置 上面这些属性都可以在@Transactional注解的参数上进行设置. readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true. timeout ...
- Quartz定时任务学习(二)web应用/Quartz定时任务学习(三)属性文件和jar
web中使用Quartz 1.首先在web.xml文件中加入 如下内容(根据自己情况设定) 在web.xml中添加QuartzInitializerServlet,Quartz为能够在web应用中使用 ...
- emberjs学习二(ember-data和localstorage_adapter)
emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...
- ReactJS入门学习二
ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...
- Quartz学习--二 Hello Quartz! 和源码分析
Quartz学习--二 Hello Quartz! 和源码分析 三. Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...
- DjangoRestFramework学习二之序列化组件、视图组件 serializer modelserializer
DjangoRestFramework学习二之序列化组件.视图组件 本节目录 一 序列化组件 二 视图组件 三 xxx 四 xxx 五 xxx 六 xxx 七 xxx 八 xxx 一 序列化组 ...
- SpringMVC入门学习(二)
SpringMVC入门学习(二) ssm框架 springMVC 在上一篇博客中,我简单介绍了一下SpringMVC的环境配置,和简单的使用,今天我们将进一步的学习下Springmvc的操作. mo ...
- day 82 Vue学习二之vue结合项目简单使用、this指向问题
Vue学习二之vue结合项目简单使用.this指向问题 本节目录 一 阶段性项目流程梳理 二 vue切换图片 三 vue中使用ajax 四 vue实现音乐播放器 五 vue的计算属性和监听器 六 ...
- day 90 DjangoRestFramework学习二之序列化组件
DjangoRestFramework学习二之序列化组件 本节目录 一 序列化组件 二 xxx 三 xxx 四 xxx 五 xxx 六 xxx 七 xxx 八 xxx 一 序列化组件 首先按照 ...
随机推荐
- VBS调用keybd_event事件
----------------发送alt+v组合按键----------------------Set Wrap = CreateObject("DynamicWrapper") ...
- leetcode136 利用异或运算找不同的元素
Given an array of integers, every element appears twice except for one. Find that single one. Note: ...
- Chapter 1 First Sight——25
"They are… very nice-looking." I struggled with the conspicuous understatement. 他们都很好看,我与轻 ...
- 第三十六节,os系统级别操作模块
在使用os模块时需要先 import os 引入模块 os.getcwd()模块函数 功能:获取当前工作目录,即当前python脚本工作的目录路径[无参] 使用方法:os.getcwd() 格式如:a ...
- ASP.NET MVC View向Controller提交数据
我们知道使用MVC的一个很重的的用途就是把Controller和View之间进行解耦,通过控制器来调用不同的视图,这就注定了Controller和View之间的传值是一个很重的知识点,这篇博文主要解释 ...
- servlet第2讲(上集)----创建servlet实例(实现servlet接口)
- 【记录】ACM计划
ACM进阶计划ACM队不是为了一场比赛而存在的,为的是队员的整体提高.大学期间,ACM队队员必须要学好的课程有:lC/C++两种语言l高等数学l线性代数l数据结构l离散数学l数据库原理l操作系统原理l ...
- HP ProLiant DL380 G6 服务器 - 清 BIOS 的方法
问题 HP ProLiant DL380 G6服务器的BIOS位置在哪里? 如何清BIOS,具体步骤是什么? 解决方案 DL380 G6服务器清BIOS过程分为三步: 1. 为服务器断电(拔掉电源线) ...
- perl面向对象
来源: http://www.cnblogs.com/itech/archive/2012/08/21/2649580.html Perl面向对象 首先让我们来看看有关 Perl 面向对象编程 ...
- Zabbix3.0 安装Graphtree
zabbix中,想要集中展示图形,唯一的选择是screen,zatree可以解决这个问题,但是性能不是很好. Graphtree由OneOaas开发并开源出来,用来解决zabbix的图形展示问题,性能 ...