[043] 微信公众平台开发教程第19篇-应用实例之人脸检测

CSDN2013年度博客之星评选活动開始,本人有幸入围参加评选,假设博客中的文章对你有所帮助,请为柳峰投上宝贵一票,很感谢!

投票地址:http://vote.blog.csdn.net/blogstaritem/blogstar2013/lyq8479

        在笔者的公众账号小q机器人(微信号:xiaoqrobot)中有一个很好玩的功能"人脸检測",它能够检測出用户发送的图片中有多少张人脸,而且还能分析出每张脸所相应的人种、性别和年龄。差点儿每天都有一些用户在使用“人脸检測”,该功能的趣味性和娱乐性在于能够让用户知道自己的长相与真实年龄是否相符,是否男(女)性化偷笑本文将为读者介绍人脸检測应用的完整实现过程。

        人脸检測属于人脸识别的范畴,它是一个复杂的具有挑战性的模式匹配问题,国内外很多组织、科研机构都在专门研究该问题。国内的Face++团队专注于研发人脸检測、识别、分析和重建技术,而且向广大开发人员开放了人脸识别API,本文介绍的“人脸检測”应用正是基于Face++ API进行开发。

Face++简单介绍

        Face++是北京旷视科技有限公司旗下的人脸识别云服务平台,Face++平台通过提供云端API、离线SDK、以及面向用户的自主研发产品等形式,将人脸识别技术广泛应用到互联网及移动应用场景中。Face++为广大开发人员提供了简单易用的API,开发人员能够轻松搭建属于自己的云端身份认证、用户兴趣挖掘、移动体感交互、社交娱乐分享等多种类型的应用。

        Face++提供的技术服务包含人脸检測、人脸分析和人脸识别,主要说明例如以下:

                1)人脸检測:能够从图片中高速、准确的定位面部的关键区域位置,包含眉毛、眼睛、鼻子、嘴巴等。
                2)人脸分析:能够从图片或实时视频流中分析出人脸的性别(精确度达96%)、年龄、种族等多种属性。
                3)人脸识别:能够高速判定两张照片是否为同一个人,或者高速判定视频中的人像是否为某一位特定的人。

        Face++的中文网址为http://cn.faceplusplus.com/,要使用Face++ API,须要注冊成为Face++开发人员,也就是要注冊一个Face++账号。注冊完成后,先创建应用,创建应用时须要填写“应用名称”、“应用描写叙述”、“APIserver”、“应用类型”和“应用平台”,读者能够依据实际情况填写。应用创建完成后,能够看到应用的具体信息,例如以下图所看到的。

                

        上图中,最重要的是API KEY和API SECRET,在调用Face++提供的API时,须要传入这两个参数。

人脸检測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 plaincopy在CODE上查看代码片派生到我的代码片
 
  1. 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格式数据例如以下:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. {  
  2.     "face": [  
  3.         {  
  4.             "attribute": {  
  5.                 "age": {  
  6.                     "range": 5,  
  7.                     "value": 23  
  8.                 },  
  9.                 "gender": {  
  10.                     "confidence": 99.9999,  
  11.                     "value": "Female"  
  12.                 },  
  13.                 "glass": {  
  14.                     "confidence": 99.945,  
  15.                     "value": "None"  
  16.                 },  
  17.                 "pose": {  
  18.                     "pitch_angle": {  
  19.                         "value": 17  
  20.                     },  
  21.                     "roll_angle": {  
  22.                         "value": 0.735735  
  23.                     },  
  24.                     "yaw_angle": {  
  25.                         "value": -2  
  26.                     }  
  27.                 },  
  28.                 "race": {  
  29.                     "confidence": 99.6121,  
  30.                     "value": "Asian"  
  31.                 },  
  32.                 "smiling": {  
  33.                     "value": 4.86501  
  34.                 }  
  35.             },  
  36.             "face_id": "17233b4b1b51ac91e391e5afe130eb78",  
  37.             "position": {  
  38.                 "center": {  
  39.                     "x": 49.4,  
  40.                     "y": 37.6  
  41.                 },  
  42.                 "eye_left": {  
  43.                     "x": 43.3692,  
  44.                     "y": 30.8192  
  45.                 },  
  46.                 "eye_right": {  
  47.                     "x": 56.5606,  
  48.                     "y": 30.9886  
  49.                 },  
  50.                 "height": 26.8,  
  51.                 "mouth_left": {  
  52.                     "x": 46.1326,  
  53.                     "y": 44.9468  
  54.                 },  
  55.                 "mouth_right": {  
  56.                     "x": 54.2592,  
  57.                     "y": 44.6282  
  58.                 },  
  59.                 "nose": {  
  60.                     "x": 49.9404,  
  61.                     "y": 38.8484  
  62.                 },  
  63.                 "width": 26.8  
  64.             },  
  65.             "tag": ""  
  66.         }  
  67.     ],  
  68.     "img_height": 500,  
  69.     "img_id": "22fd9efc64c87e00224c33dd8718eec7",  
  70.     "img_width": 500,  
  71.     "session_id": "38047ad0f0b34c7e8c6efb6ba39ed355",  
  72.     "url": "http://cn.faceplusplus.com/wp-content/themes/faceplusplus.zh/assets/img/demo/1.jpg?v=4"  
  73. }  

这里仅仅对本文将要实现的“人脸检測”功能中主要用到的参数进行说明,参数解释例如以下:

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对象,相应的代码例如以下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. package org.liufeng.course.pojo;  
  2.   
  3. /** 
  4.  * Face Model 
  5.  *  
  6.  * @author liufeng 
  7.  * @date 2013-12-18 
  8.  */  
  9. public class Face implements Comparable<Face> {  
  10.     // 被检測出的每一张人脸都在Face++系统中的标识符  
  11.     private String faceId;  
  12.     // 年龄预计值  
  13.     private int ageValue;  
  14.     // 年龄预计值的正负区间  
  15.     private int ageRange;  
  16.     // 性别:Male/Female  
  17.     private String genderValue;  
  18.     // 性别分析的可信度  
  19.     private double genderConfidence;  
  20.     // 人种:Asian/White/Black  
  21.     private String raceValue;  
  22.     // 人种分析的可信度  
  23.     private double raceConfidence;  
  24.     // 微笑程度  
  25.     private double smilingValue;  
  26.     // 人脸框的中心点坐标  
  27.     private double centerX;  
  28.     private double centerY;  
  29.   
  30.     public String getFaceId() {  
  31.         return faceId;  
  32.     }  
  33.   
  34.     public void setFaceId(String faceId) {  
  35.         this.faceId = faceId;  
  36.     }  
  37.   
  38.     public int getAgeValue() {  
  39.         return ageValue;  
  40.     }  
  41.   
  42.     public void setAgeValue(int ageValue) {  
  43.         this.ageValue = ageValue;  
  44.     }  
  45.   
  46.     public int getAgeRange() {  
  47.         return ageRange;  
  48.     }  
  49.   
  50.     public void setAgeRange(int ageRange) {  
  51.         this.ageRange = ageRange;  
  52.     }  
  53.   
  54.     public String getGenderValue() {  
  55.         return genderValue;  
  56.     }  
  57.   
  58.     public void setGenderValue(String genderValue) {  
  59.         this.genderValue = genderValue;  
  60.     }  
  61.   
  62.     public double getGenderConfidence() {  
  63.         return genderConfidence;  
  64.     }  
  65.   
  66.     public void setGenderConfidence(double genderConfidence) {  
  67.         this.genderConfidence = genderConfidence;  
  68.     }  
  69.   
  70.     public String getRaceValue() {  
  71.         return raceValue;  
  72.     }  
  73.   
  74.     public void setRaceValue(String raceValue) {  
  75.         this.raceValue = raceValue;  
  76.     }  
  77.   
  78.     public double getRaceConfidence() {  
  79.         return raceConfidence;  
  80.     }  
  81.   
  82.     public void setRaceConfidence(double raceConfidence) {  
  83.         this.raceConfidence = raceConfidence;  
  84.     }  
  85.   
  86.     public double getSmilingValue() {  
  87.         return smilingValue;  
  88.     }  
  89.   
  90.     public void setSmilingValue(double smilingValue) {  
  91.         this.smilingValue = smilingValue;  
  92.     }  
  93.   
  94.     public double getCenterX() {  
  95.         return centerX;  
  96.     }  
  97.   
  98.     public void setCenterX(double centerX) {  
  99.         this.centerX = centerX;  
  100.     }  
  101.   
  102.     public double getCenterY() {  
  103.         return centerY;  
  104.     }  
  105.   
  106.     public void setCenterY(double centerY) {  
  107.         this.centerY = centerY;  
  108.     }  
  109.   
  110.     // 依据人脸中心点坐标从左至右排序  
  111.     @Override  
  112.     public int compareTo(Face face) {  
  113.         int result = 0;  
  114.         if (this.getCenterX() > face.getCenterX())  
  115.             result = 1;  
  116.         else  
  117.             result = -1;  
  118.         return result;  
  119.     }  
  120. }  

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. package org.liufeng.course.service;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8. import java.util.ArrayList;  
  9. import java.util.Collections;  
  10. import java.util.List;  
  11. import org.liufeng.course.pojo.Face;  
  12. import net.sf.json.JSONArray;  
  13. import net.sf.json.JSONObject;  
  14.   
  15. /** 
  16.  * 人脸检測服务 
  17.  *  
  18.  * @author liufeng 
  19.  * @date 2013-12-18 
  20.  */  
  21. public class FaceService {  
  22.     /** 
  23.      * 发送http请求 
  24.      *  
  25.      * @param requestUrl 请求地址 
  26.      * @return String 
  27.      */  
  28.     private static String httpRequest(String requestUrl) {  
  29.         StringBuffer buffer = new StringBuffer();  
  30.         try {  
  31.             URL url = new URL(requestUrl);  
  32.             HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();  
  33.             httpUrlConn.setDoInput(true);  
  34.             httpUrlConn.setRequestMethod("GET");  
  35.             httpUrlConn.connect();  
  36.             // 将返回的输入流转换成字符串  
  37.             InputStream inputStream = httpUrlConn.getInputStream();  
  38.             InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
  39.             BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
  40.   
  41.             String str = null;  
  42.             while ((str = bufferedReader.readLine()) != null) {  
  43.                 buffer.append(str);  
  44.             }  
  45.             bufferedReader.close();  
  46.             inputStreamReader.close();  
  47.             // 释放资源  
  48.             inputStream.close();  
  49.             inputStream = null;  
  50.             httpUrlConn.disconnect();  
  51.   
  52.         } catch (Exception e) {  
  53.             e.printStackTrace();  
  54.         }  
  55.         return buffer.toString();  
  56.     }  
  57.   
  58.     /** 
  59.      * 调用Face++ API实现人脸检測 
  60.      *  
  61.      * @param picUrl 待检測图片的訪问地址 
  62.      * @return List<Face> 人脸列表 
  63.      */  
  64.     private static List<Face> faceDetect(String picUrl) {  
  65.         List<Face> faceList = new ArrayList<Face>();  
  66.         try {  
  67.             // 拼接Face++人脸检測的请求地址  
  68.             String queryUrl = "http://apicn.faceplusplus.com/v2/detection/detect?url=URL&api_secret=API_SECRET&api_key=API_KEY";  
  69.             // 对URL进行编码  
  70.             queryUrl = queryUrl.replace("URL", java.net.URLEncoder.encode(picUrl, "UTF-8"));  
  71.             queryUrl = queryUrl.replace("API_KEY""替换成自己的API Key");  
  72.             queryUrl = queryUrl.replace("API_SECRET""替换成自己的API Secret");  
  73.             // 调用人脸检測接口  
  74.             String json = httpRequest(queryUrl);  
  75.             // 解析返回json中的Face列表  
  76.             JSONArray jsonArray = JSONObject.fromObject(json).getJSONArray("face");  
  77.             // 遍历检測到的人脸  
  78.             for (int i = 0; i < jsonArray.size(); i++) {  
  79.                 // face  
  80.                 JSONObject faceObject = (JSONObject) jsonArray.get(i);  
  81.                 // attribute  
  82.                 JSONObject attrObject = faceObject.getJSONObject("attribute");  
  83.                 // position  
  84.                 JSONObject posObject = faceObject.getJSONObject("position");  
  85.                 Face face = new Face();  
  86.                 face.setFaceId(faceObject.getString("face_id"));  
  87.                 face.setAgeValue(attrObject.getJSONObject("age").getInt("value"));  
  88.                 face.setAgeRange(attrObject.getJSONObject("age").getInt("range"));  
  89.                 face.setGenderValue(genderConvert(attrObject.getJSONObject("gender").getString("value")));  
  90.                 face.setGenderConfidence(attrObject.getJSONObject("gender").getDouble("confidence"));  
  91.                 face.setRaceValue(raceConvert(attrObject.getJSONObject("race").getString("value")));  
  92.                 face.setRaceConfidence(attrObject.getJSONObject("race").getDouble("confidence"));  
  93.                 face.setSmilingValue(attrObject.getJSONObject("smiling").getDouble("value"));  
  94.                 face.setCenterX(posObject.getJSONObject("center").getDouble("x"));  
  95.                 face.setCenterY(posObject.getJSONObject("center").getDouble("y"));  
  96.                 faceList.add(face);  
  97.             }  
  98.             // 将检測出的Face按从左至右的顺序排序  
  99.             Collections.sort(faceList);  
  100.         } catch (Exception e) {  
  101.             faceList = null;  
  102.             e.printStackTrace();  
  103.         }  
  104.         return faceList;  
  105.     }  
  106.   
  107.     /** 
  108.      * 性别转换(英文->中文) 
  109.      *  
  110.      * @param gender 
  111.      * @return 
  112.      */  
  113.     private static String genderConvert(String gender) {  
  114.         String result = "男性";  
  115.         if ("Male".equals(gender))  
  116.             result = "男性";  
  117.         else if ("Female".equals(gender))  
  118.             result = "女性";  
  119.   
  120.         return result;  
  121.     }  
  122.   
  123.     /** 
  124.      * 人种转换(英文->中文) 
  125.      *  
  126.      * @param race 
  127.      * @return 
  128.      */  
  129.     private static String raceConvert(String race) {  
  130.         String result = "黄色";  
  131.         if ("Asian".equals(race))  
  132.             result = "黄色";  
  133.         else if ("White".equals(race))  
  134.             result = "白色";  
  135.         else if ("Black".equals(race))  
  136.             result = "黑色";  
  137.         return result;  
  138.     }  
  139.   
  140.     /** 
  141.      * 依据人脸识别结果组装消息 
  142.      *  
  143.      * @param faceList 人脸列表 
  144.      * @return 
  145.      */  
  146.     private static String makeMessage(List<Face> faceList) {  
  147.         StringBuffer buffer = new StringBuffer();  
  148.         // 检測到1张脸  
  149.         if (1 == faceList.size()) {  
  150.             buffer.append("共检測到 ").append(faceList.size()).append(" 张人脸").append(" ");  
  151.             for (Face face : faceList) {  
  152.                 buffer.append(face.getRaceValue()).append("人种,");  
  153.                 buffer.append(face.getGenderValue()).append(",");  
  154.                 buffer.append(face.getAgeValue()).append("岁左右").append(" ");  
  155.             }  
  156.         }  
  157.         // 检測到2-10张脸  
  158.         else if (faceList.size() > 1 && faceList.size() <= 10) {  
  159.             buffer.append("共检測到 ").append(faceList.size()).append(" 张人脸,按脸部中心位置从左至右依次为:").append(" ");  
  160.             for (Face face : faceList) {  
  161.                 buffer.append(face.getRaceValue()).append("人种,");  
  162.                 buffer.append(face.getGenderValue()).append(",");  
  163.                 buffer.append(face.getAgeValue()).append("岁左右").append(" ");  
  164.             }  
  165.         }  
  166.         // 检測到10张脸以上  
  167.         else if (faceList.size() > 10) {  
  168.             buffer.append("共检測到 ").append(faceList.size()).append(" 张人脸").append(" ");  
  169.             // 统计各人种、性别的人数  
  170.             int asiaMale = 0;  
  171.             int asiaFemale = 0;  
  172.             int whiteMale = 0;  
  173.             int whiteFemale = 0;  
  174.             int blackMale = 0;  
  175.             int blackFemale = 0;  
  176.             for (Face face : faceList) {  
  177.                 if ("黄色".equals(face.getRaceValue()))  
  178.                     if ("男性".equals(face.getGenderValue()))  
  179.                         asiaMale++;  
  180.                     else  
  181.                         asiaFemale++;  
  182.                 else if ("白色".equals(face.getRaceValue()))  
  183.                     if ("男性".equals(face.getGenderValue()))  
  184.                         whiteMale++;  
  185.                     else  
  186.                         whiteFemale++;  
  187.                 else if ("黑色".equals(face.getRaceValue()))  
  188.                     if ("男性".equals(face.getGenderValue()))  
  189.                         blackMale++;  
  190.                     else  
  191.                         blackFemale++;  
  192.             }  
  193.             if (0 != asiaMale || 0 != asiaFemale)  
  194.                 buffer.append("黄色人种:").append(asiaMale).append("男").append(asiaFemale).append("女").append(" ");  
  195.             if (0 != whiteMale || 0 != whiteFemale)  
  196.                 buffer.append("白色人种:").append(whiteMale).append("男").append(whiteFemale).append("女").append(" ");  
  197.             if (0 != blackMale || 0 != blackFemale)  
  198.                 buffer.append("黑色人种:").append(blackMale).append("男").append(blackFemale).append("女").append(" ");  
  199.         }  
  200.         // 移除末尾空格  
  201.         buffer = new StringBuffer(buffer.substring(0, buffer.lastIndexOf(" ")));  
  202.         return buffer.toString();  
  203.     }  
  204.   
  205.     /** 
  206.      * 提供给外部调用的人脸检測方法 
  207.      *  
  208.      * @param picUrl 待检測图片的訪问地址 
  209.      * @return String 
  210.      */  
  211.     public static String detect(String picUrl) {  
  212.         // 默认回复信息  
  213.         String result = "未识别到人脸,请换一张清楚的照片再试!";  
  214.         List<Face> faceList = faceDetect(picUrl);  
  215.         if (null != faceList) {  
  216.             result = makeMessage(faceList);  
  217.         }  
  218.         return result;  
  219.     }  
  220.   
  221.     public static void main(String[] args) {  
  222.         String picUrl = "http://pic11.nipic.com/20101111/6153002_002722872554_2.jpg";  
  223.         System.out.println(detect(picUrl));  
  224.     }  
  225. }  

上述代码尽管多,但条理很清楚,并不难理解,所以笔者仅仅挑重点的进行解说,主要说明例如以下:

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类的完整代码例如以下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. package org.liufeng.course.service;  
  2.   
  3. import java.util.Date;  
  4. import java.util.Map;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import org.liufeng.course.message.resp.TextMessage;  
  7. import org.liufeng.course.util.MessageUtil;  
  8.   
  9. /** 
  10.  * 核心服务类 
  11.  *  
  12.  * @author liufeng 
  13.  * @date 2013-12-19 
  14.  */  
  15. public class CoreService {  
  16.     /** 
  17.      * 处理微信发来的请求 
  18.      */  
  19.     public static String processRequest(HttpServletRequest request) {  
  20.         // 返回给微信server的xml消息  
  21.         String respXml = null;  
  22.         try {  
  23.             // xml请求解析  
  24.             Map<String, String> requestMap = MessageUtil.parseXml(request);  
  25.             // 发送方帐号(open_id)  
  26.             String fromUserName = requestMap.get("FromUserName");  
  27.             // 公众帐号  
  28.             String toUserName = requestMap.get("ToUserName");  
  29.             // 消息类型  
  30.             String msgType = requestMap.get("MsgType");  
  31.   
  32.             // 回复文本消息  
  33.             TextMessage textMessage = new TextMessage();  
  34.             textMessage.setToUserName(fromUserName);  
  35.             textMessage.setFromUserName(toUserName);  
  36.             textMessage.setCreateTime(new Date().getTime());  
  37.             textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  
  38.   
  39.             // 图片消息  
  40.             if (MessageUtil.REQ_MESSAGE_TYPE_IMAGE.equals(msgType)) {  
  41.                 // 取得图片地址  
  42.                 String picUrl = requestMap.get("PicUrl");  
  43.                 // 人脸检測  
  44.                 String detectResult = FaceService.detect(picUrl);  
  45.                 textMessage.setContent(detectResult);  
  46.             }  
  47.             // 其它类型的消息  
  48.             else  
  49.                 textMessage.setContent(getUsage());  
  50.   
  51.             respXml = MessageUtil.textMessageToXml(textMessage);  
  52.         } catch (Exception e) {  
  53.             e.printStackTrace();  
  54.         }  
  55.         return respXml;  
  56.     }  
  57.   
  58.     /** 
  59.      * 人脸检測帮助菜单 
  60.      */  
  61.     public static String getUsage() {  
  62.         StringBuffer buffer = new StringBuffer();  
  63.         buffer.append("人脸检測使用指南").append(" ");  
  64.         buffer.append("发送一张清楚的照片,就能帮你分析出种族、年龄、性别等信息").append(" ");  
  65.         buffer.append("快来试试你是不是长得太着急");  
  66.         return buffer.toString();  
  67.     }  
  68. }  

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

        

执行结果例如以下:

         

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

转帖请注明本文出自柳峰的博客(http://blog.csdn.net/lyq8479),请尊重他人的辛勤劳动成果,谢谢!

原文地址:https://www.cnblogs.com/mfryf/p/3598799.html