zoukankan      html  css  js  c++  java
  • 人脸识别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

  • 相关阅读:
    leetcode第9题判断回文数
    leetcode整数反转第七题
    leetcode刷题两数之和
    找工作之旅
    C#和.Ne学习第五天
    C#和.Ne学习第四天
    C#和.Ne学习第三天
    C#和.Ne学习第二天
    C#和.Ne学习第一天
    从今天开始正事学习C#和.Net了
  • 原文地址:https://www.cnblogs.com/jxust-jiege666/p/8590485.html
Copyright © 2011-2022 走看看