专栏首页技术拓展与说明初级篇,利用 Android 搭建一个简易的人脸识别APP
原创

初级篇,利用 Android 搭建一个简易的人脸识别APP

初始,准备工作

  • 安装Android sudio 开发工具
  • 搭建android 运行环境(java14)
  • 腾讯云OCR产品开通,及其秘钥获取

一、建立一个Android空项目及目录层次介绍

(1)android 空项目以及环境搭建,请参考:Windows + Android studio 搭建基础的Android 环境(Java SE 14)

(2)目录结构介绍

目录结构
  • .gredia以及idea目录 :目录下放置的都是as自动生成的文件,本章无需关系
  • app 目录 : 本章用到的文件目录,仅着重介绍需要使用的文件及目录
  1. src > main > java 目录 : 放置所有代码目录,数据交互,事件操作
  2. src > main > res 目录 : 资源目录,即android 前端样式 展示界面等前端相关
  3. src > main > AndroidMinifest.xml 文件 : 整个项目配置文件,开通权限,首页访问设置,其他页面管理都在此
  4. build.gradle文件 :app模块的构建脚本
  • build目录 : 包含编译时自动生成的文件,本章无关
  • gradle目录 :包含gradle wrapper的配置文件,本章无关
  • gitgnore文件 : 文件是用来将制定的目录或文件排除在版本控制之外的,本章无关
  • build.gradle文件 : 全局的gradle构建脚本,通常文件的内容不需要修改,本章无关
  • gradle.properties文件 :全局的gradle配置文件,这里配置的属性将会影响到项目中所有的gradle编译脚本,本章无关
  • gradlew和gradlew.bat 文件 :这两个文件是用来在命令行界面执行gradle命令的,其中gradlew是在linux或mac系统中使用的,gradlew.bat是在windows系统中使用的,本章无关
  • ***.iml 文件:iml文件是所有intellij IDEA项目都会自动生成的文件,标识是 intellij IDEA项目,本章无关
  • local.properties文件 : 自动生成用于指定本机sdk的路径,不需要修改,除非sdk路径发生改变,本章无关
  • settings.gradle文件 :指定项目中所有引用的模块,通常情况下模块的引入都是自动生成的,本章无关

小结回顾及知识扩展:

(1)创建一个界面

创建一个空界面示例

(2)加载 GSON 类库

选择项目结构
选择给当前项目增加类库
选择类库加载
应用后再确定

二、构建前端页面样式

前端界面展示
前端放置资源
  • 界面放置 res > layout 文件夹下, 生成示例:activity_face.xml 文件
  • value 目录 : 放置存储值的文件
  • res/drawable-xxxx目录 : 是存放图片的目录

代码示例,(为方便展示与基础学习,前端未拆分样式)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    
    <!-- tools:context 是指你 src -> main -> java 中对应的代码文件 -->

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="#CC0000"
        android:paddingLeft="56dp"
        android:paddingRight="56dp">
        <TextView
            android:id="@+id/nav_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="人脸识别"
            android:textColor="#FDFDFD"
            android:textSize="16sp" />
    </FrameLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="30dp">

        <EditText
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:background="@drawable/ic_launcher_background"
            android:backgroundTint="#C52424"
            android:textColor="#ffffff"
            android:focusable="false"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:text="NeedQualityDetection"
            android:textSize="18dp" />

        <EditText
            android:id="@+id/needQualityDetection"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:hint="0 or 1"
            android:text="1"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"/>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="30dp">
        <!--android:layout_gravity="center_vertical"-->

        <EditText
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:background="@drawable/ic_launcher_background"
            android:backgroundTint="#C52424"
            android:textColor="#ffffff"
            android:focusable="false"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:text="FaceModelVersion"
            android:textSize="18dp" />

        <EditText
            android:id="@+id/paramFaceModel"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:hint="2.0  or 3.0"
            android:text="3.0"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp" />
    </LinearLayout>

    <ImageView
        android:id="@+id/photoView"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:layout_margin="20dp"
        />
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="10dp">

        <Button
            android:id="@+id/albumBtnEvt"
            android:layout_width="wrap_content"
            android:layout_height="44dp"
            android:background="#FF5722"
            android:text="打 开 相 册"
            android:textColor="#FFFEFE"
            android:onClick="getImageFromAlbum"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="44dp"
            android:background="#FF5722"
            android:text="拍 照"
            android:textColor="#FFFEFE"
            android:layout_marginLeft="20dp"
            android:onClick="toCamera"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:background="#A52A2A"
            android:text="发 送 请 求"
            android:textColor="#FFFEFE"
            android:layout_marginLeft="20dp"
            android:onClick="onFormBtnClick"/>
    </LinearLayout>


    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="5dp">
        <!--android:layout_gravity="center_vertical"-->

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@null"
            android:text="年 龄"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>

        <EditText
            android:id="@+id/ageEvt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>
    </LinearLayout>

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="5dp">
        <!--android:layout_gravity="center_vertical"-->

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@null"
            android:text="性 别"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>

        <EditText
            android:id="@+id/sexEvt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>
    </LinearLayout>


    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="5dp">
        <!--android:layout_gravity="center_vertical"-->

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@null"
            android:text="装 束"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>

        <EditText
            android:id="@+id/markEvt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>
    </LinearLayout>


    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:layout_marginTop="5dp">
        <!--android:layout_gravity="center_vertical"-->

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@null"
            android:text="表 情"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>

        <EditText
            android:id="@+id/ExpressionEvt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:paddingLeft="12dp"
            android:paddingRight="12dp"
            android:textSize="18dp"
            android:focusable="false"/>
    </LinearLayout>

</LinearLayout>

三、后端响应代码 (src > main > java > com.xxxxx 下)

package com.example.faceiai;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends Activity {
    //申明前端布局属性
    private TextView needQualityDetectionEvt, paramFaceModelVersionEvt, markEvt, ageEvt, sexEvt, ExpressionEvt;
    //设置需要的拍照、相册权限
    private static String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA };//权限
    //申明前端图片展示属性
    private ImageView photoViewEvt;
    //申明人脸检测属性
    private String response;

    final Handler handler = new Handler();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    /**
     * 加载初始化
     */
    private void init(){
        // android 7.0系统解决拍照的问题
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure();

        //初始化控件 findViewById 用R.id到前端通过android:id指定的id 获取当前选中属性
        needQualityDetectionEvt =   findViewById(R.id.needQualityDetection);
        paramFaceModelVersionEvt = findViewById(R.id.paramFaceModel);
        photoViewEvt    =     findViewById(R.id.photoView);

        sexEvt     =     findViewById(R.id.sexEvt);
        ageEvt     =     findViewById(R.id.ageEvt);
        markEvt    =     findViewById(R.id.markEvt);
        ExpressionEvt    =     findViewById(R.id.ExpressionEvt);
    }

    /**
     * 表单提交点击事件
     * 前端通过android:onClick 指定点击时间触发方法
     * @param v
     */
    public void onFormBtnClick(View v){
        //android4.0 以后不能在主线程发起网络请求,该异步网络请求
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                //获取前端 needQualityDetection 的 value
                int needQualityDetection = Integer.parseInt(needQualityDetectionEvt.getText().toString());
                //获取前端 paramFaceModelVersion 的 value
                String paramFaceModelVersion = paramFaceModelVersionEvt.getText().toString();
                //获取前端相册或者拍照时间生成的缩略图的Bitmap值
                Bitmap photoBitmap = ((BitmapDrawable) photoViewEvt.getDrawable()).getBitmap();
                //图片Bitmap -转化-> base64
                String photoBase64 = bitmapToBase64(photoBitmap);

                Map<String, Object> params = new HashMap<>();
                params.put("Image", photoBase64);
                params.put("FaceModelVersion", paramFaceModelVersion);
                params.put("NeedFaceAttributes", 1);
                params.put("NeedQualityDetection", needQualityDetection);
                Gson gson = new Gson();
                String param = gson.toJson(params);
                Log.e("request", param);
                // TODO Auto-generated method stub

                //发送请求 本地封装的https 请求
                response = AuthFace.getAuthTC3("DetectFace", param, AuthFace.Version);
                //response = "{\"Response\":{\"ImageWidth\":1080,\"ImageHeight\":1440,\"FaceInfos\":[{\"X\":119,\"Y\":18,\"Width\":558,\"Height\":721,\"FaceAttributesInfo\":{\"Gender\":1,\"Age\":24,\"Expression\":28,\"Hat\":false,\"Glass\":false,\"Mask\":true,\"Hair\":{\"Length\":3,\"Bang\":0,\"Color\":0},\"Pitch\":5,\"Yaw\":17,\"Roll\":2,\"Beauty\":79,\"EyeOpen\":true},\"FaceQualityInfo\":{\"Score\":29,\"Sharpness\":43,\"Brightness\":34,\"Completeness\":{\"Eyebrow\":98,\"Eye\":99,\"Nose\":94,\"Cheek\":64,\"Mouth\":71,\"Chin\":35}}}],\"FaceModelVersion\":\"3.0\",\"RequestId\":\"4c1ebdb5-7a96-405e-a9bc-0a329ea26cda\"}}";
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //处理json 数据 把JSON数据转化为对象
                            JSONObject jsonObject = new JSONObject(response.toString());
                            //返回错误提示
                            if (jsonObject.getJSONObject("Response").has("Error")){
                                String Message = jsonObject.getJSONObject("Response").getJSONObject("Error").getString("Message");
                                String RequestId = jsonObject.getJSONObject("Response").getString("RequestId");
                                String Code = jsonObject.getJSONObject("Response").getJSONObject("Error").getString("Code");
                                //给前端设置值
                                sexEvt.setText(Message);
                                ageEvt.setText(Message);
                                markEvt.setText(Code);
                                ExpressionEvt.setText(RequestId);
                            } else {
                                //正确的人脸检测反馈
                                JSONObject FaceAttributesInfo = jsonObject.getJSONObject("Response").getJSONArray("FaceInfos").getJSONObject(0).getJSONObject("FaceAttributesInfo");
                                int Gender = FaceAttributesInfo.getInt("Gender");
                                int Age = FaceAttributesInfo.getInt("Age");
                                int Expression = FaceAttributesInfo.getInt("Expression");
                                Boolean Mark = FaceAttributesInfo.getBoolean("Mask");
                                //给前端设置值
                                sexEvt.setText(FaceUtilsJson.getGenderName(Gender));
                                ageEvt.setText(String.valueOf(Age));
                                markEvt.setText(FaceUtilsJson.getMask(Mark));
                                ExpressionEvt.setText(FaceUtilsJson.getExpressionName(Expression));
                            }
                        }catch (JSONException e){
                            Log.e("JSON error", e.getMessage());
                        }
                    }
                });
            }
        }).start();

    }

    /**
     * 获取相册事件
     * @param v
     */
    public void getImageFromAlbum(View v) {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(intent, 100);// 100: 相册的返回码参数(随便一个值就行,只要不冲突就好)
    }

    //跳转相机
    public void toCamera(View v) {
        //查看相机权限 用户手动授权
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)  != PackageManager.PERMISSION_GRANTED ){
            Log.e("camera","提示是否要授权");
            ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, 3);
        }else{
            Log.e("camera","直接打开相机");
            //已经有权限
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  //跳转到 ACTION_IMAGE_CAPTURE
            //判断内存卡是否可用,可用的话就进行存储
            //putExtra:取值,Uri.fromFile:传一个拍照所得到的文件,fileImg.jpg:文件名
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),"fileImg.jpg")));
            startActivityForResult(intent,101); // 101: 相机的返回码参数(随便一个值就行,只要不冲突就好)
        }
    }


    /**
     * 自带方法,相册更相机时间确定后,返回回来指定的方法
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        switch (requestCode){
            case 100:   //相册返回的数据(相册的返回码)
                if (data == null) {
                    Log.e("onActivityResult", "暂无内容");
                    return;
                }
                Uri uriPath = data.getData();
                //方案一
                //photoViewEvt.setImageURI(uriPath);
                //方案二
                try {
                    //获取相册的图片bitmap值
                    Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriPath));
                    //质量压缩
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                    byte[] bytes = bos.toByteArray();
                    Bitmap mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                    photoViewEvt.setImageBitmap(mSrcBitmap);
                } catch (IOException e) {
                    Log.e("onActivityResult 100",  e.getMessage());
                    e.printStackTrace();
                }
                break;
            case 101:  //相机返回的数据(相机的返回码)
                try {
                    File tempFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),"fileImg.jpg");  //相机取图片数据文件
                    Uri uri = Uri.fromFile(tempFile);   //图片文件
                    Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
                    //质量压缩
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                    byte[] bytes = bos.toByteArray();
                    Bitmap mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                    photoViewEvt.setImageBitmap(mSrcBitmap);
                } catch (IOException e) {
                    Log.e("onActivityResult", e.getMessage());
                    e.printStackTrace();
                }
                break;
        }
    }


    /*
     * bitmap转base64
     * */
    private static String bitmapToBase64(Bitmap bitmap) {
        String result = null;
        ByteArrayOutputStream baos = null;
        try {
            if (bitmap != null) {
                baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

                baos.flush();
                baos.close();

                byte[] bitmapBytes = baos.toByteArray();
                result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null) {
                    baos.flush();
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

人脸识别V3鉴权接口请求代码示例 (src > main > java > com.xxx > AuthFace.java)

package com.example.faceiai;

import android.text.TextUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * 人脸识别V3鉴权
 */
public class AuthFace {

    private static String SecretId  = "";
    private static String SecretKey = "";

    private static String Url       = "https://iai.tencentcloudapi.com";
    //规范请求串
    private static String  HTTPRequestMethod     = "POST";
    private static String  CanonicalURI          = "/";
    private static String  CanonicalQueryString  = "";
    private static String  CanonicalHeaders      = "content-type:application/json; charset=utf-8\nhost:iai.tencentcloudapi.com\n";
    private static String  SignedHeaders         = "content-type;host";//参与签名的头部信息

    //签名字符串
    private static String  Algorithm             = "TC3-HMAC-SHA256";
    private static String  Service               = "iai";
    private static String  Stop                  = "tc3_request";

    //版本
    public static String  Version               = "2018-03-01";

    /**
     * v3鉴权
     * @param action  人脸识别接口名 例如 DetectFace
     * @param paramJson json化的参数
     * @param version  版本号 2018-03-01
     * @return
     */
    public static String getAuthTC3(String action, String paramJson, String version){
        try{
            String hashedRequestPayload   =   HashEncryption(paramJson);
            String CanonicalRequest =
                    HTTPRequestMethod + '\n' +
                            CanonicalURI + '\n' +
                            CanonicalQueryString + '\n' +
                            CanonicalHeaders + '\n' +
                            SignedHeaders + '\n' +
                            hashedRequestPayload;
            //时间戳
            Date date = new Date();
            //微秒->秒
            String timestamp    =   String.valueOf(date.getTime() / 1000);

            //格林威治时间转化
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            formatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
            String dateString = formatter.format(date.getTime());

            //签名字符串
            String credentialScope = dateString + "/" + Service + "/" + Stop;
            String hashedCanonicalRequest =   HashEncryption(CanonicalRequest);
            String stringToSign           =   Algorithm + "\n" +
                    timestamp + "\n" +
                    credentialScope + "\n" +
                    hashedCanonicalRequest;

            //计算签名
            byte[] secretDate             =   HashHmacSha256Encryption(("TC3" + SecretKey).getBytes("UTF-8"), dateString);
            byte[] secretService          =   HashHmacSha256Encryption(secretDate, Service);
            byte[] secretSigning          =   HashHmacSha256Encryption(secretService, Stop);

            //签名字符串
            byte[] signatureHmacSHA256    =   HashHmacSha256Encryption(secretSigning, stringToSign);
            //替换java DatatypeConverter.printHexBinary(d).toLowerCase()

            StringBuilder builder = new StringBuilder();
            for (byte b : signatureHmacSHA256) {
                String hex = Integer.toHexString(b & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                builder.append(hex);
            }
            String signature = builder.toString().toLowerCase();
            //组装签名字符串
            String authorization          =   Algorithm + ' ' +
                                            "Credential=" + SecretId + '/' + credentialScope + ", " +
                                            "SignedHeaders=" + SignedHeaders + ", " +
                                            "Signature=" + signature;

            //创建header 头部
            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", authorization);
            headers.put("Host", "iai.tencentcloudapi.com");
            headers.put("Content-Type", "application/json; charset=utf-8");
            headers.put("X-TC-Action", action);
            headers.put("X-TC-Version", version);
            headers.put("X-TC-Timestamp", timestamp);
            //headers.put("X-TC-Region", "ap-chengdu");

            //request 请求
            String response = resquestPostData(Url, paramJson, headers);
            return response;
        }catch(Exception e){
            return e.getMessage();
        }
    }

    /*
     * Function  :   发送Post请求到服务器
     * Param     :   params请求体内容,encode编码格式
     */
    public static String resquestPostData(String strUrlPath, String data, Map<String, String> headers) {
        try {
            URL url = new URL(strUrlPath);
            HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
            httpURLConnection.setConnectTimeout(3000);            //设置连接超时时间
            httpURLConnection.setDoInput(true);                  //打开输入流,以便从服务器获取数据
            httpURLConnection.setDoOutput(true);                 //打开输出流,以便向服务器提交数据
            httpURLConnection.setRequestMethod("POST");          //设置以Post方式提交数据
            httpURLConnection.setUseCaches(false);               //使用Post方式不能使用缓存
            //设置header
            if (headers.isEmpty()) {
                //设置请求体的类型是文本类型
                httpURLConnection.setRequestProperty("Content-Type", "application/json");
            } else {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    httpURLConnection.setRequestProperty(key, value);
                }
            }
            //设置请求体的长度
            httpURLConnection.setRequestProperty("Content-Length", String.valueOf(data.length()));

            //获得输出流,向服务器写入数据
            if (data != null && !TextUtils.isEmpty(data)) {
                byte[] writebytes = data.getBytes();
                // 设置文件长度
                OutputStream outputStream = httpURLConnection.getOutputStream();
                outputStream.write(data.getBytes());
                outputStream.flush();
            }
            int response = httpURLConnection.getResponseCode();            //获得服务器的响应码
            if(response == HttpURLConnection.HTTP_OK) {
                InputStream inptStream = httpURLConnection.getInputStream();
                return dealResponseResult(inptStream);                     //处理服务器的响应结果
            }
        } catch (IOException e) {
            return "err: " + e.getMessage().toString();
        }
        return "-1";
    }

    /*
     * Function  :   封装请求体信息
     * Param     :   params请求体内容,encode编码格式
     */
    public static StringBuffer getRequestData(Map<String, String> params, String encode) {
        StringBuffer stringBuffer = new StringBuffer();        //存储封装好的请求体信息
        try {
            for(Map.Entry<String, String> entry : params.entrySet()) {
                stringBuffer.append(entry.getKey())
                        .append("=")
                        .append(URLEncoder.encode(entry.getValue(), encode))
                        .append("&");
            }
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);    //删除最后的一个"&"
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stringBuffer;
    }

    /*
     * Function  :   处理服务器的响应结果(将输入流转化成字符串)
     * Param     :   inputStream服务器的响应输入流
     */
    public static String dealResponseResult(InputStream inputStream) {
        String resultData = null;      //存储处理结果
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int len = 0;
        try {
            while((len = inputStream.read(data)) != -1) {
                byteArrayOutputStream.write(data, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        resultData = new String(byteArrayOutputStream.toByteArray());
        return resultData;
    }


    /**
     *
     */
    private static String  HashEncryption(String s)  throws Exception {
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        sha.update(s.getBytes());
        //替换java DatatypeConverter.printHexBinary(d).toLowerCase()
        StringBuilder builder = new StringBuilder();
        for (byte b : sha.digest()) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            builder.append(hex);
        }
        return builder.toString().toLowerCase();
    }

    private static byte[] HashHmacSha256Encryption(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes("UTF-8"));
    }

}

人脸检测数据分析接口(src > main > java > com.xxx > FaceUtilsJson.java)

package com.example.faceiai;

/**
 * 人脸检测与分析
 */
public class FaceUtilsJson{

    public static String getGenderName(int Age) {
        //性别处理
        if (Age > 49) {
            return "男性";
        } else {
            return "女性";
        }
    }

    public static String getExpressionName(int Expression) {
        if (Expression < 50) {
            return "正常";
        } else {
            return "微笑";
        }
    }

    public static String getMask(Boolean Mask){
        if (Mask) {
            return "佩戴口罩";
        } else {
            return "未佩戴口罩";
        }
    }
}

四、权限赋予 (src > main > AndroidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.faceiai">

    <!--网络权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--相册及拍照权限-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="com.miui.whetstone.permission.ACCESS_PROVIDER"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

五、调用及其展示

调用方法
效果展示图

搭建成功的话,记得给个赞,支持下小编 !!!!!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 初级篇,利用 Android 搭建一个简易的文字识别APP

    前端学习参考:https://www.runoob.com/w3cnote/android-tutorial-linearlayout.html

    袁伦桥
  • 从诺基亚 X6 聊人脸解锁:安全基础是TEE

    前不久,诺基亚发布了其全新X系列智能手机诺基亚X6,官网上也打出了:AI面部识别,基于TEE移动安全解决方案指纹解锁之外,更享安全便捷的宣传。由于其采用的是高通...

    安智客
  • 详解android 人脸检测你一定会遇到的坑

    笔者今年做了一个和人脸有关的android产品,主要是获取摄像头返回的预览数据流,判断该数据流是否包含了人脸,有人脸时显示摄像头预览框,无人脸时摄像头预览框隐藏...

    砸漏
  • Android Ndk and Opencv Development 3

    本节包括下面几个方面的内容: 1.如何实现Static Initialization从而不需要安装OpenCV Manager运行含OpenCV librar...

    宅男潇涧
  • 我为什么要写《OpenCV Android 开发实战》这本书

    2015年我出版了个人第一本关于图像处理方面的书籍《Java图像处理-编程技巧与应用实践》,这本书主要是从理论与编码上面详细阐述了图像处理基础算法以...

    OpenCV学堂
  • 华为麒麟的AI性能是高通的3.5倍?这是所有手机运行神经网络的能力

    高通、华为、联发科有什么共通点?这三家厂商都做加速手机、平板等移动设备中计算机视觉、NLP 以及其他机器学习任务的硬件架构。然而,这存在一个问题,即开发者难以判...

    机器之心
  • 开发工具总结(7)之多年珍藏的Android开发必备网站和工具

    版权声明:本文为博主原创文章,未经博主允许不得转载。https://www.jianshu.com/p/781c1b56bc5b

    AWeiLoveAndroid
  • 基于FPGA Facenet 与物联网的智能门锁

    随着 5G 时代的到来,万物互联唱响了这个时代的主题曲,物联网日新月异 的发展,社会的信息程度显著提升。其次,人工智能技术的发展,大量人工智能技术走出实验室,以...

    FPGA技术江湖
  • 从架构分析到代码,Amazon无人超市是这样诞生的|附教程

    首先我们先看一段预览视频,了解一下无人超市的整个销售与运作过程。 视频内容 无人超市,未来趋势。 上面这段视频,展示了逛亚马逊的Amazon Go无人超市是种怎...

    BestSDK
  • 36小时,造一个亚马逊无人商店 | 实战教程+代码

    夏乙 问耕 编译整理 量子位 出品 | 公众号 QbitAI 无人超市,未来趋势。 上面这段视频,展示了逛亚马逊的Amazon Go无人超市是种怎样的体验。毫无...

    量子位
  • 互联网寒冬下那些 Android 开发高手,都在研究什么技术?

    近半年来,很多人都问过我这样的问题。大家对于职业的未来,都有一些迷茫和焦虑,其实我也有,为什么会这样呢?

    Android技术干货分享
  • 【大咖来了】有道周枫:苹果Core ML对移动端深度学习的意义

    【新智元导读】在WWDC2017(全球开发者大会)上,苹果发布了支持移动端深度学习的 CoreML 框架。网易有道 CEO 周枫指出,这个新框架能够解决以往云端...

    新智元
  • Android Startup最新进展(内含抽奖)

    今天介绍一个有关启动优化的开源库android-startup。这个开源库主要是做什么的呢?

    Rouse
  • 技术简单且有效提升购买转化率,微信小程序的“AR功能”神在哪里?

    7月5日,微信小程序正式支持实现AR效果,并向品牌商户、AR引擎服务商开放接入。首个支持AR动态试妆的美妆品牌小程序“阿玛尼美妆官方精品商城”,也在7月5日同步...

    VRPinea
  • 微信七年回顾:历经多少质疑和差评,才配拥有今天的强大

    不知不觉,微信已经诞生七年了。 从第一版到现在,微信的演变史,很像一部创业史,很好地诠释了创业者能经得起多少质疑和差评,才配拥有多大的成功。

    JackJiang
  • 前端的世界里没有“容易”二字

    这半年,你过得怎么样?新的热点技术学会了吗?写的代码还有bug吗?头发还好吗?还记得年初的 Flag 吗?

    桃翁
  • 温故而知新:周末复习一下 Android & Java 面试题

    当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就叫做冷启动((后台不存在该应用进程) 冷启动因为系统会重新创建一个...

    Android技术干货分享
  • 抖音上好看的小姐姐,Python给你都下载了

    如果一条条去刷确实很耗时间,如果 Python 能帮忙筛选出颜值高的小姐姐那就省了很多事。

    Python数据科学
  • Python实现AI人脸识别抖音上颜值高的小姐姐,批量下载视频!

    如果一条条去刷确实很耗时间,如果 Python 能帮忙筛选出颜值高的小姐姐那就省了很多事。

    一墨编程学习

扫码关注云+社区

领取腾讯云代金券