不要让昨天 占据你的今天 夏午晴天

夏午晴天

人脸识别1:1对比 (三)

  本项目采用了 百度人脸识别 第三方接口,实现了自选图片人脸识别和 两张图片的1:1对比,可返回比对相似度信息。

目前百度向个人开发者提供了免费人脸识别接口,QPS限制为2,企业认证后并发数可增至 5,亲测可用。

 

 以下是简单应用:

一 、所需权限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

 二、第三方app id app key

  可自行去百度 AI 平台申请注册

三、工具类

  1. http 工具类
     1 /**
     2  * http 工具类
     3  */
     4 public class HttpUtil {
     5 
     6     public static String post(String requestUrl, String accessToken, String params)
     7             throws Exception {
     8         String contentType = "application/x-www-form-urlencoded";
     9         return HttpUtil.post(requestUrl, accessToken, contentType, params);
    10     }
    11 
    12     public static String post(String requestUrl, String accessToken, String contentType, String params)
    13             throws Exception {
    14         String encoding = "UTF-8";
    15         if (requestUrl.contains("nlp")) {
    16             encoding = "GBK";
    17         }
    18         return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
    19     }
    20 
    21     public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
    22             throws Exception {
    23         String url = requestUrl + "?access_token=" + accessToken;
    24         return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
    25     }
    26 
    27     public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
    28             throws Exception {
    29         URL url = new URL(generalUrl);
    30         // 打开和URL之间的连接
    31         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    32         connection.setRequestMethod("POST");
    33         // 设置通用的请求属性
    34         connection.setRequestProperty("Content-Type", contentType);
    35         connection.setRequestProperty("Connection", "Keep-Alive");
    36         connection.setUseCaches(false);
    37         connection.setDoOutput(true);
    38         connection.setDoInput(true);
    39 
    40         // 得到请求的输出流对象
    41         DataOutputStream out = new DataOutputStream(connection.getOutputStream());
    42         out.write(params.getBytes(encoding));
    43         out.flush();
    44         out.close();
    45 
    46         // 建立实际的连接
    47         connection.connect();
    48         // 获取所有响应头字段
    49         Map<String, List<String>> headers = connection.getHeaderFields();
    50         // 遍历所有的响应头字段
    51         for (String key : headers.keySet()) {
    52             System.err.println(key + "--->" + headers.get(key));
    53         }
    54         // 定义 BufferedReader输入流来读取URL的响应
    55         BufferedReader in = null;
    56         in = new BufferedReader(
    57                 new InputStreamReader(connection.getInputStream(), encoding));
    58         String result = "";
    59         String getLine;
    60         while ((getLine = in.readLine()) != null) {
    61             result += getLine;
    62         }
    63         in.close();
    64         System.err.println("result:" + result);
    65         return result;
    66     }
    67 }
    View Code

     

  2. Base64 工具类
     1 public class Base64Util {
     2     private static final char last2byte = (char) Integer.parseInt("00000011", 2);
     3     private static final char last4byte = (char) Integer.parseInt("00001111", 2);
     4     private static final char last6byte = (char) Integer.parseInt("00111111", 2);
     5     private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
     6     private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
     7     private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
     8     private static final char[] encodeTable = new char[]
     9             {
    10                     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    11                     'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    12                     'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    13                     'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
    14             };
    15 
    16     public Base64Util() {
    17     }
    18 
    19     public static String encode(byte[] from) {
    20         StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
    21         int num = 0;
    22         char currentByte = 0;
    23 
    24         int i;
    25         for (i = 0; i < from.length; ++i) {
    26             for (num %= 8; num < 8; num += 6) {
    27                 switch (num) {
    28                     case 0:
    29                         currentByte = (char) (from[i] & lead6byte);
    30                         currentByte = (char) (currentByte >>> 2);
    31                     case 1:
    32                     case 3:
    33                     case 5:
    34                     default:
    35                         break;
    36                     case 2:
    37                         currentByte = (char) (from[i] & last6byte);
    38                         break;
    39                     case 4:
    40                         currentByte = (char) (from[i] & last4byte);
    41                         currentByte = (char) (currentByte << 2);
    42                         if (i + 1 < from.length) {
    43                             currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
    44                         }
    45                         break;
    46                     case 6:
    47                         currentByte = (char) (from[i] & last2byte);
    48                         currentByte = (char) (currentByte << 4);
    49                         if (i + 1 < from.length) {
    50                             currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
    51                         }
    52                 }
    53 
    54                 to.append(encodeTable[currentByte]);
    55             }
    56         }
    57 
    58         if (to.length() % 4 != 0) {
    59             for (i = 4 - to.length() % 4; i > 0; --i) {
    60                 to.append("=");
    61             }
    62         }
    63 
    64         return to.toString();
    65     }
    66 }
    View Code

     

  以上是实现的Base64的加密算法,使用自带 Base64.encodeToString(); 方法也可以。

  Base64原理可参考这篇博文:http://www.cnblogs.com/jxust-jiege666/p/8590116.html

四、获取token

主要代码:

 1 /**
 2  * 获取token类
 3  */
 4 public class AuthService {
 5 
 6     /**
 7      * 获取权限token
 8      * @return 返回示例:
 9      * {
10      * "access_token": "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567",
11      * "expires_in": 2592000
12      * }
13      */
14     public static String getAuth() {
15         // 官网获取的 API Key 更新为你注册的
16         String clientId = "百度云应用的AK";
17         // 官网获取的 Secret Key 更新为你注册的
18         String clientSecret = "百度云应用的SK";
19         return getAuth(clientId, clientSecret);
20     }
21 
22     /**
23      * 获取API访问token
24      * 该token有一定的有效期,需要自行管理,当失效时需重新获取.
25      * @param ak - 百度云官网获取的 API Key
26      * @param sk - 百度云官网获取的 Securet Key
27      * @return assess_token 示例:
28      * "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567"
29      */
30     public static String getAuth(String ak, String sk) {
31         // 获取token地址
32         String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
33         String getAccessTokenUrl = authHost
34                 // 1. grant_type为固定参数
35                 + "grant_type=client_credentials"
36                 // 2. 官网获取的 API Key
37                 + "&client_id=" + ak
38                 // 3. 官网获取的 Secret Key
39                 + "&client_secret=" + sk;
40         try {
41             URL realUrl = new URL(getAccessTokenUrl);
42             // 打开和URL之间的连接
43             HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
44             connection.setRequestMethod("GET");
45             connection.connect();
46             // 获取所有响应头字段
47             Map<String, List<String>> map = connection.getHeaderFields();
48             // 遍历所有的响应头字段
49             for (String key : map.keySet()) {
50                 System.err.println(key + "--->" + map.get(key));
51             }
52             // 定义 BufferedReader输入流来读取URL的响应
53             BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
54             String result = "";
55             String line;
56             while ((line = in.readLine()) != null) {
57                 result += line;
58             }
59             /**
60              * 返回结果示例
61              */
62             System.err.println("result:" + result);
63             JSONObject jsonObject = new JSONObject(result);
64             String access_token = jsonObject.getString("access_token");
65             return access_token;
66         } catch (Exception e) {
67             System.err.printf("获取token失败!");
68             e.printStackTrace(System.err);
69         }
70         return null;
71     }
72 
73 }
View Code

 

注意:access_token的有效期为30天,切记需要每30天进行定期更换,或者每次请求都拉取新token;

五、验证请求

主要代码:

 1 public class FaceMatch {
 2 
 3     /**
 4      * 重要提示代码中所需工具类
 5      * FileUtil,Base64Util,HttpUtil,GsonUtils请从
 6      * https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
 7      * https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
 8      * https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
 9      * https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3
10      * 下载
11      */
12     public static String match(byte[] mImg1,byte[] mImg2,String accessToken) {
13         // 请求url
14         String url = "https://aip.baidubce.com/rest/2.0/face/v2/match";
15         try {
16 //            String imgStr = Base64.encodeToString(mImg1, 0);
17             String imgStr = Base64Util.encode(mImg1);
18             String imgParam = URLEncoder.encode(imgStr, "UTF-8");
19             String imgStr2 = Base64Util.encode(mImg2);
20             String imgParam2 = URLEncoder.encode(imgStr2, "UTF-8");
21 
22             String param = "images=" + imgParam + "," + imgParam2;
23 
24             // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
25             String result = HttpUtil.post(url, accessToken, param);
26             System.out.println(result);
27             return result;
28         } catch (Exception e) {
29             e.printStackTrace();
30         }
31         return null;
32     }
33 }
View Code

 

注意事项:

  • 请求体格式化:Content-Type为application/x-www-form-urlencoded,通过urlencode格式化请求体。
  • Base64编码:请求的图片需经过Base64编码,图片的base64编码指将图片数据编码成一串字符串,使用该字符串代替图像地址。您可以首先得到图片的二进制,然后用Base64格式编码即可。需要注意的是,图片的base64编码是不包含图片头的,如data:image/jpg;base64,
  • 图片格式:现支持PNG、JPG、JPEG、BMP,不支持GIF图片

URL参数:

参数
access_token 通过API Key和Secret Key获取的access_token

Header:

参数
Content-Type application/x-www-form-urlencoded

Body中放置请求参数,参数详情如下:

  请求参数

参数必选类型说明
images string 分别base64编码后的2张图片数据,需urlencode,半角逗号分隔,单次请求最大不超过20M
ext_fields string 返回质量信息,取值固定,目前支持qualities(质量检测)(对所有图片都会做改处理)
image_liveness string 返回的活体信息,“faceliveness,faceliveness” 表示对比对的两张图片都做活体检测;“,faceliveness” 表示对第一张图片不做活体检测、第二张图做活体检测;“faceliveness,” 表示对第一张图片做活体检测、第二张图不做活体检测;
注:需要用于判断活体的图片,图片中的人脸像素面积需要不小于100px*100px,人脸长宽与图片长宽比例,不小于1/3
types string

请求对比的两张图片的类型,示例:“7,13”
7表示生活照:通常为手机、相机拍摄的人像图片、或从网络获取的人像图片等
11表示身份证芯片照:二代身份证内置芯片中的人像照片
12表示带水印证件照:一般为带水印的小图,如公安网小图
13表示证件照片:如拍摄的身份证、工卡、护照、学生证等证件图片,注:需要确保人脸部分不可太小,通常为100px*100px

  说明:两张请求的图片请分别进行base64编码。

返回说明

返回参数

字段必选类型说明
log_id uint64 请求唯一标识码,随机数
result_num uint32 返回结果数目,即:result数组中元素个数
result array(object) 结果数据,index和请求图片index对应。数组元素为每张图片的匹配得分数组,top n。得分范围[0,100.0]
+index_i uint32 比对图片1的index
+index_j uint32 比对图片2的index
+score double 比对得分,推荐80分作为阈值,80分以上可以判断为同一人,此分值对应万分之一误识率
ext_info array(dict) 对应参数中的ext_fields
+qualities string 质量相关的信息,无特殊需求可以不使用
+faceliveness string 活体检测分数,单帧活体检测参考阈值0.393241,超过此分值以上则可认为是活体。注意:活体检测接口主要用于判断是否为二次翻拍,需要限制用户为当场拍照获取图片;推荐配合客户端SDK有动作校验活体使用

返回示例

//请求两张图片
{
    "log_id": 73473737,
    "result_num":1,
    "result": [
        {
            "index_i": 0,
            "index_j": 1,
            "score": 44.3
        }
    ]
}

六、主页面activity

主要代码:

  1 import android.content.ContentResolver;
  2 import android.content.Intent;
  3 import android.graphics.Bitmap;
  4 import android.graphics.BitmapFactory;
  5 import android.net.Uri;
  6 import android.os.Bundle;
  7 import android.os.Handler;
  8 import android.os.Message;
  9 import android.support.v7.app.AlertDialog;
 10 import android.support.v7.app.AppCompatActivity;
 11 import android.text.TextUtils;
 12 import android.util.Log;
 13 import android.view.View;
 14 import android.widget.Button;
 15 import android.widget.ImageView;
 16 import android.widget.TextView;
 17 import android.widget.Toast;
 18 
 19 import com.example.lifen.baidufacecomparedemo.R;
 20 import com.example.lifen.baidufacecomparedemo.utils.AuthService;
 21 import com.example.lifen.baidufacecomparedemo.utils.FaceMatch;
 22 
 23 import java.io.ByteArrayOutputStream;
 24 import java.io.FileNotFoundException;
 25 
 26 /**
 27  * 人脸对比 1:1
 28  *
 29  * @author LiFen
 30  */
 31 public class MainActivity extends AppCompatActivity {
 32     private static final String TAG = "MainActivity";
 33     private static final int REQUEST_CODE1 = 11;
 34     private static final int REQUEST_CODE2 = 12;
 35     ImageView mImageView1;
 36     ImageView mImageView2;
 37     Button mCompareBtn;
 38     TextView mResultText;
 39     private byte[] mImg1;
 40     private byte[] mImg2;
 41     String key = "";//api_key
 42     String secret ="";//api_secret
 43     private final static int i = 100;
 44 
 45     private Handler handler = new Handler(){
 46         @Override
 47         public void handleMessage(Message msg) {
 48             if(msg.what == i){
 49                 mResultText.setText((String)msg.obj);
 50             }
 51         }
 52     };
 53     @Override
 54     protected void onCreate(Bundle savedInstanceState) {
 55         super.onCreate(savedInstanceState);
 56         setContentView(R.layout.activity_main);
 57 
 58         mImageView1 = (ImageView) findViewById(R.id.img1);
 59         mImageView2 = (ImageView) findViewById(R.id.img2);
 60         mCompareBtn = (Button) findViewById(R.id.compareBtn);
 61         mResultText = (TextView) findViewById(R.id.resultBtn);
 62         if(TextUtils.isEmpty(key) || TextUtils.isEmpty(secret)){
 63             AlertDialog.Builder builder = new AlertDialog.Builder(this);
 64             builder.setMessage("please enter key and secret");
 65             builder.setTitle("");
 66             builder.show();
 67             return;
 68         }
 69         mImageView1.setOnClickListener(new View.OnClickListener() {
 70             @Override
 71             public void onClick(View v) {
 72                 startAlbumActivity(REQUEST_CODE1);
 73             }
 74         });
 75         mImageView2.setOnClickListener(new View.OnClickListener() {
 76             @Override
 77             public void onClick(View v) {
 78                 startAlbumActivity(REQUEST_CODE2);
 79             }
 80         });
 81         mCompareBtn.setOnClickListener(new View.OnClickListener() {
 82             @Override
 83             public void onClick(View v) {
 84                 startCompare();
 85             }
 86         });
 87     }
 88 
 89     private void startCompare() {
 90         if ("".equals(mImg1) || mImg1 == null || "".equals(mImg2) || mImg2 == null) {
 91             Toast.makeText(this, "请选择图片再比对", Toast.LENGTH_SHORT).show();
 92             return;
 93         }
 94         mResultText.setText("比对中...");
 95         new Thread(new Runnable() {
 96             @Override
 97             public void run() {
 98                 try{
 99                     String accessToken = AuthService.getAuth(key,secret);
100                     Log.i(TAG, "run: " +accessToken);
101                     Log.i(TAG, "run: " + mImg1.toString());
102                     Log.i(TAG, "run: " + mImg2.toString());
103                     String result = FaceMatch.match(mImg1,mImg2,accessToken);
104                     Message msg = new Message();
105                     msg.what = i;
106                     msg.obj = result;
107                     handler.sendMessage(msg);
108                 }catch (Exception e){
109                     Log.i(TAG, "startCompare: " + e.toString());
110                 }
111             }
112         }).start();
113     }
114 
115     private void startAlbumActivity(int requestCode) {
116         Intent intent = new Intent();
117         intent.setType("image/*");
118         intent.setAction(Intent.ACTION_GET_CONTENT);
119         startActivityForResult(intent, requestCode);
120     }
121 
122     @Override
123     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
124         if (data == null)
125             return;
126         Uri uri = data.getData();
127         Log.e("uri", uri.toString());
128         ContentResolver cr = this.getContentResolver();
129         Bitmap bitmap = null;
130         try {
131             bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
132                 /* 将Bitmap设定到ImageView */
133         } catch (FileNotFoundException e) {
134             Log.e("Exception", e.getMessage(), e);
135         }
136         if (resultCode == RESULT_OK && requestCode == REQUEST_CODE1) {
137             mImageView1.setImageBitmap(bitmap);
138             ByteArrayOutputStream baos = new ByteArrayOutputStream();
139             bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
140             byte[] datas = baos.toByteArray();
141             mImg1 = datas;
142         } else if (resultCode == RESULT_OK && requestCode == REQUEST_CODE2) {
143             mImageView2.setImageBitmap(bitmap);
144             ByteArrayOutputStream baos = new ByteArrayOutputStream();
145             bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
146             byte[] datas = baos.toByteArray();
147             mImg2 = datas;
148         }
149         super.onActivityResult(requestCode, resultCode, data);
150     }
151 }
View Code

注意:key自行注册获取

七、布局文件

页面效果:

 

代码如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <ScrollView
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     android:paddingBottom="@dimen/activity_vertical_margin"
 8     android:paddingLeft="@dimen/activity_horizontal_margin"
 9     android:paddingRight="@dimen/activity_horizontal_margin"
10     android:paddingTop="@dimen/activity_vertical_margin"
11     tools:context="com.example.lifen.baidufacecomparedemo.activity.MainActivity">
12 
13     <LinearLayout
14         android:layout_width="match_parent"
15         android:layout_height="match_parent"
16         android:orientation="vertical">
17 
18         <LinearLayout
19             android:layout_width="match_parent"
20             android:layout_height="wrap_content"
21             android:orientation="horizontal">
22 
23             <ImageView
24                 android:id="@+id/img1"
25                 android:layout_width="0dp"
26                 android:layout_height="180dp"
27                 android:layout_weight="1"
28                 android:scaleType="centerCrop"
29                 android:src="@drawable/head"/>
30 
31             <TextView
32                 android:layout_width="wrap_content"
33                 android:layout_height="match_parent"
34                 android:gravity="center"
35                 android:text="VS"
36                 android:textColor="@android:color/black"
37                 android:textSize="20dp"/>
38 
39             <ImageView
40                 android:id="@+id/img2"
41                 android:layout_width="0dp"
42                 android:layout_height="180dp"
43                 android:layout_weight="1"
44                 android:scaleType="centerCrop"
45                 android:src="@drawable/head"/>
46 
47         </LinearLayout>
48 
49         <Button
50             android:id="@+id/compareBtn"
51             android:layout_width="match_parent"
52             android:layout_height="wrap_content"
53             android:layout_marginTop="@dimen/activity_horizontal_margin"
54             android:text="比对"/>
55 
56         <TextView
57             android:id="@+id/resultBtn"
58             android:layout_width="match_parent"
59             android:layout_height="wrap_content"
60             android:layout_marginTop="@dimen/activity_horizontal_margin"
61             android:background="#eeeeee"
62             android:padding="6dp"/>
63 
64     </LinearLayout>
65 </ScrollView>
View Code

 

项目源码地址:https://download.csdn.net/download/qq_36726507/10292513

posted on 2018-03-17 16:05  夏晴天  阅读(3045)  评论(0编辑  收藏  举报

导航

Live2D