博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
人脸识别1:1对比 (三)
阅读量:4554 次
发布时间:2019-06-08

本文共 17741 字,大约阅读时间需要 59 分钟。

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

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

 

 以下是简单应用:

一 、所需权限

 二、第三方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
    > 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原理可参考这篇博文:

四、获取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": 259200012      * }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访问token24      * 该token有一定的有效期,需要自行管理,当失效时需重新获取.25      * @param ak - 百度云官网获取的 API Key26      * @param sk - 百度云官网获取的 Securet Key27      * @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 = authHost34                 // 1. grant_type为固定参数35                 + "grant_type=client_credentials"36                 // 2. 官网获取的 API Key37                 + "&client_id=" + ak38                 // 3. 官网获取的 Secret Key39                 + "&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
> 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/470B3ACCA3FE43788B5A963BF0B625F310      * 下载11      */12     public static String match(byte[] mImg1,byte[] mImg2,String accessToken) {13         // 请求url14         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,超过此分值以上则可认为是活体。注意:活体检测接口主要用于判断是否为二次翻拍,需要限制用户为当场拍照获取图片;推荐配合有动作校验活体使用

返回示例

//请求两张图片{    "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     @Override123     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 
2
12 13
17 18
22 23
30 31
38 39
46 47 48 49
55 56
63 64 65
View Code

 

项目源码地址:

转载于:https://www.cnblogs.com/jxust-jiege666/p/8590485.html

你可能感兴趣的文章
64. [Mcoi2018]终末之诗(上)
查看>>
关于进程的上下文切换
查看>>
你不知道的JS(作用域和闭包)
查看>>
[恢]hdu 1164
查看>>
vs2013 安装boost1.59
查看>>
[恢]hdu 2503
查看>>
调用动态库时声明的参数个数不一致导致的问题
查看>>
003 Python与类C语言的区别(未完)
查看>>
tomcat eclipse mysql 安装
查看>>
Linux查看CPU和内存使用情况[转]
查看>>
Delegte的BeginInvoke
查看>>
jQuery设计思想
查看>>
“嗑瓜子理论”——员工激励与管理
查看>>
状压DP 拯救莫莉斯
查看>>
入坑 可持久化线段树——主席树
查看>>
eclipse中设定文档注释
查看>>
CSS Hack及常用的技巧
查看>>
phpcms
查看>>
iOS homekit使用说明
查看>>
第 12 章 命令模式【Command Pattern】
查看>>