Android 集成Face++ 人脸识别(3.0+SDK)

写在前面

                    最近项目需求需要输入用户真实姓名 身份证号 再去调用人脸识别 效验是否是本人 所以就首先关注了Face++(旷视)的人脸识别,听说是 正确率很高.....所以就集成了.

 

1.首先去官网去创建应用(其实听说创建应用是比较麻烦的)

 

创建好应用以后需要上传你项目的包名

 

2.下载Demo Demo中会有一个扫描身份证的SDK和人脸识别的SDK 我这个需求呢是人脸识别 所以只集成人脸识别(如下图)

 

 

您可以按照您项目的需求集成不同的SDK(这里只集成人脸识别SDK)

 

3.上面的俩个文件夹都有扫描身份证和人脸识别的Demo

建议您,先把申请应用完以后的AppKey 和secret 配置到Face++的Demo中 跑完这个流程

 

4.开始我们的步骤

 

5.Face++给的Demo中 就会有 我们今天需要集成的SDK(如下图)

 

Android 集成Face++ 人脸识别(3.0+SDK)_第1张图片

点击sdk文件夹

 

Android 集成Face++ 人脸识别(3.0+SDK)_第2张图片

 

6.我们需要把meglive_still.aar 导入到 lib文件夹中

 

并在app下 build.gradle文件下 dependencies中写入

implementation(name: 'meglive_still', ext: 'aar')

 

在repositories中写入

flatDir { dirs 'libs' }

7.上代码

 

(1)需要调用人脸识别的Activity

private static final int CAMERA_PERMISSION_REQUEST_CODE = 100;
private static final int EXTERNAL_STORAGE_REQ_WRITE_EXTERNAL_STORAGE_CODE = 101;
private ProgressDialog mProgressDialog;
private MegLiveManager megLiveManager;
private static final String GET_BIZTOKEN_URL = 
			"https://api.megvii.com/faceid/v3/sdk/get_biz_token"; //Face++获取BizToken的url
private static final String VERIFY_URL = "https://api.megvii.com/faceid/v3/sdk/verify";   //Face++人脸效验的url
private static final String API_KEY = "您的AppKey";
private static final String SECRET = "您的Secret";
private String sign = "";
private static final String SIGN_VERSION = "hmac_sha1";
private byte[] imageRef;//底库图片
private int buttonType;

 

private void init() {
    megLiveManager=MegLiveManager.getInstance();
    mProgressDialog = new ProgressDialog(this);
    mProgressDialog.setCancelable(false);

    long currtTime = System.currentTimeMillis() / 1000;
    long expireTime = (System.currentTimeMillis() + 60 * 60 * 100) / 1000;
    sign = GenerateSign.appSign(API_KEY, SECRET, currtTime, expireTime);

    requestCameraPerm();

}

 

 

 

//获取BizToken的请求

private void getBizToken(String livenessType, int comparisonType, String idcardName, String idcardNum, String uuid, byte[] image) {
    mProgressDialog.show();
    HttpRequestManager.getInstance().getBizToken(this, GET_BIZTOKEN_URL, sign, SIGN_VERSION, livenessType, comparisonType, idcardName, idcardNum, uuid, image, new HttpRequestCallBack() {

        @Override
        public void onSuccess(String responseBody) {
            try {
                JSONObject json = new JSONObject(responseBody);
                String bizToken = json.optString("biz_token");
                megLiveManager.preDetect(FaceIdActivity.this, bizToken,FaceIdActivity.this);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(int statusCode, byte[] responseBody) {

        }
    });

}
@Override
public void onDetectFinish(String token, int errorCode, String errorMessage, String data) {
    if (errorCode == 1000) {
        verify(token, data.getBytes());
    }
}

 

@Override
public void onPreStart() {
    showDialogDismiss();
}

@Override
public void onPreFinish(String token, int errorCode, String errorMessage) {
    progressDialogDismiss();
    if (errorCode == 1000) {
        megLiveManager.startDetect(this);
    }
}

//人脸识别的url
private void verify(String token, byte[] data) {
    showDialogDismiss();
    HttpRequestManager.getInstance().verify(this, VERIFY_URL, sign, SIGN_VERSION, token, data, new HttpRequestCallBack() {
        @Override
        public void onSuccess(String responseBody) {
            Log.w("result", responseBody);
            progressDialogDismiss();

            gotoActivity(mContext,FaceIdVerifySuccessActivity.class,null);

        }

        @Override
        public void onFailure(int statusCode, byte[] responseBody) {
            Log.w("result", new String(responseBody));
            progressDialogDismiss();

            gotoActivity(mContext,FaceIdVerifyErrorActivity.class,null);

        }
    });
}

private void progressDialogDismiss() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (mProgressDialog != null) {
                mProgressDialog.dismiss();
            }
        }
    });
}

private void showDialogDismiss() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (mProgressDialog != null) {
                mProgressDialog.show();
            }
        }
    });

}

private void requestCameraPerm() {
    if (android.os.Build.VERSION.SDK_INT >= M) {
        if (checkSelfPermission(Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            //进行权限请求
            requestPermissions(
                    new String[]{Manifest.permission.CAMERA},
                    CAMERA_PERMISSION_REQUEST_CODE);
        } else {
            requestWriteExternalPerm();
        }
    } else {
        beginDetect();
    }
}

private void requestWriteExternalPerm() {
    if (android.os.Build.VERSION.SDK_INT >= M) {
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //进行权限请求
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    EXTERNAL_STORAGE_REQ_WRITE_EXTERNAL_STORAGE_CODE);
        } else {
            beginDetect();
        }
    } else {
        beginDetect();  
 
    }
}

private void beginDetect() {

    getBizToken("meglive", 1, "您的真实姓名", "您的身份证号", UUID.randomUUID().toString(), null);
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
        if (grantResults.length < 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            //拒绝了权限申请
        } else {
            requestWriteExternalPerm();
        }
    } else if (requestCode == EXTERNAL_STORAGE_REQ_WRITE_EXTERNAL_STORAGE_CODE) {
        if (grantResults.length < 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            //拒绝了权限申请
        } else {
            beginDetect();
        }
    }
}

(2) CV代码出来的错误

您的人脸识别的Activity需要实现  implements DetectCallback, PreCallback

 

(3)GenerateSign.java

 

public class GenerateSign {

    public static String appSign(String apiKey, String secret, long currtTime,long expireTime) {
        try {
            int rdm = Math.abs(new Random().nextInt());

            String plainText = String.format("a=%s&b=%d&c=%d&d=%d", apiKey, expireTime, currtTime,rdm);
            byte[] hmacDigest = HmacSha1(plainText, secret);
            byte[] signContent = new byte[hmacDigest.length + plainText.getBytes().length];
            System.arraycopy(hmacDigest, 0, signContent, 0, hmacDigest.length);
            System.arraycopy(plainText.getBytes(), 0, signContent, hmacDigest.length,
                    plainText.getBytes().length);
            return Base64Encode(signContent).replaceAll("[\\s*\t\n\r]", "");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成base64编码
     *
     * @param binaryData
     * @return
     */
    public static String Base64Encode(byte[] binaryData) {
        String encodedstr = Base64.encodeToString(binaryData,Base64.DEFAULT);
        return encodedstr;
    }

    /**
     * 生成hmacsha1签名
     *
     * @param binaryData
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] HmacSha1(byte[] binaryData, String key) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
        mac.init(secretKey);
        byte[] HmacSha1Digest = mac.doFinal(binaryData);
        return HmacSha1Digest;
    }

    /**
     * 生成hmacsha1签名
     *
     * @param plainText
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] HmacSha1(String plainText, String key) throws Exception {
        return HmacSha1(plainText.getBytes(), key);
    }
}
(4)HttpRequestCallBack
public interface HttpRequestCallBack {
    void onSuccess(String responseBody);

    void onFailure(int statusCode, byte[] responseBody);
}

(5)HttpRequestManager.java

public class HttpRequestManager {
    public final static int TIMEOUT = 10000;
    private static HttpRequestManager instance;

    public static HttpRequestManager getInstance() {
        if (instance == null) {
            instance = new HttpRequestManager();
        }
        return instance;
    }

    public void verify(Context context,String url,String sign,String signVersion,String bizToken,byte[] megLiveData,HttpRequestCallBack listener){
        MultipartEntity entity = new MultipartEntity();
        entity.addStringPart("sign",sign);
        entity.addStringPart("sign_version",signVersion);
        entity.addStringPart("biz_token",bizToken);
        entity.addBinaryPart("meglive_data",megLiveData);

        sendMultipartRequest(context,url,entity,new HashMap(),listener);

    }

    public void getBizToken(Context context,String url,String sign,String signVersoin,String livenessType,int comparisonType,String idcardName,String idcardNum,String uuid,byte[] image_ref1,HttpRequestCallBack listener){
        MultipartEntity entity = new MultipartEntity();
        entity.addStringPart("sign",sign);
        entity.addStringPart("sign_version", signVersoin);
        entity.addStringPart("liveness_type", livenessType);
        entity.addStringPart("comparison_type", ""+comparisonType);
        if (comparisonType==1){
            entity.addStringPart("idcard_name", idcardName);
            entity.addStringPart("idcard_number", idcardNum);
        }else if (comparisonType==0){
            entity.addStringPart("uuid", uuid);
            entity.addBinaryPart("image_ref1", image_ref1);
        }
        sendMultipartRequest(context,url,entity,new HashMap(),listener);
    }

    private void sendPostRequest(Context context, String url, final Map params, final Map header, final HttpRequestCallBack listener) {
        StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener() {
            @Override
            public void onResponse(String response) {
                if (listener != null)
                    listener.onSuccess(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (error == null) {
                    if (listener != null)
                        listener.onFailure(-1, "timeout exception".getBytes());
                } else if (error.networkResponse == null) {
                    if (listener != null)
                        listener.onFailure(-1, "timeout exception".getBytes());
                } else {
                    if (listener != null)
                        listener.onFailure(error.networkResponse.statusCode, error.networkResponse.data);
                }
            }
        }) {
            @Override
            protected Map getParams() throws AuthFailureError {
                return params;
            }

            @Override
            public Map getHeaders() throws AuthFailureError {
                return header;
            }
        };
        VolleyHelper.getInstance(context).addToRequestQueue(request);
    }

    private void sendGetRequest(Context context, String url, final Map header, final HttpRequestCallBack listener) {
        StringRequest request = new StringRequest(url, new Response.Listener() {
            @Override
            public void onResponse(String response) {
                listener.onSuccess(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (error == null) {
                    listener.onFailure(-1, "timeout exception".getBytes());
                } else if (error.networkResponse == null) {
                    listener.onFailure(-1, "timeout exception".getBytes());
                } else {
                    listener.onFailure(error.networkResponse.statusCode, error.networkResponse.data);
                }
            }
        }) {
            @Override
            public Map getHeaders() throws AuthFailureError {
                return header;
            }
        };
        VolleyHelper.getInstance(context).addToRequestQueue(request);
    }

    private void sendMultipartRequest(Context context, String url, MultipartEntity mult, final Map header, final HttpRequestCallBack listener) {
        MultipartRequest multipartRequest = new MultipartRequest(
                url, new Response.Listener() {
            @Override
            public void onResponse(String response) {
                listener.onSuccess(response);
            }

        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (error == null) {
                    listener.onFailure(-1, "timeout exception".getBytes());
                } else if (error.networkResponse == null) {
                    listener.onFailure(-1, "timeout exception".getBytes());
                } else {
                    listener.onFailure(error.networkResponse.statusCode, error.networkResponse.data);
                }
            }
        }) {
            @Override
            public Map getHeaders() throws AuthFailureError {
                return header;
            }
        };
        // 通过MultipartEntity来设置参数
        multipartRequest.setmMultiPartEntity(mult);

        VolleyHelper.getInstance(context).addToRequestQueue(multipartRequest);
    }

}
(6)MultipartEntity
public class MultipartEntity implements HttpEntity {

    private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            .toCharArray();
    /**
     * 换行符
     */
    private final String NEW_LINE_STR = "\r\n";
    private final String CONTENT_TYPE = "Content-Type: ";
    private final String CONTENT_DISPOSITION = "Content-Disposition: ";
    /**
     * 文本参数和字符集
     */
    private final String TYPE_TEXT_CHARSET = "text/plain; charset=UTF-8";

    /**
     * 字节流参数
     */
    private final String TYPE_OCTET_STREAM = "application/octet-stream";
    /**
     * 二进制参数
     */
    private final byte[] BINARY_ENCODING = "Content-Transfer-Encoding: binary\r\n\r\n".getBytes();
    /**
     * 文本参数
     */
    private final byte[] BIT_ENCODING = "Content-Transfer-Encoding: 8bit\r\n\r\n".getBytes();

    /**
     * 分隔符
     */
    private String mBoundary = null;
    /**
     * 输出流
     */
    ByteArrayOutputStream mOutputStream = new ByteArrayOutputStream();

    public MultipartEntity() {
        this.mBoundary = generateBoundary();
    }

    /**
     * 生成分隔符
     *
     * @return
     */
    private final String generateBoundary() {
        final StringBuffer buf = new StringBuffer();
        final Random rand = new Random();
        for (int i = 0; i < 30; i++) {
            buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
        }
        return buf.toString();
    }

    /**
     * 参数开头的分隔符
     *
     * @throws IOException
     */
    private void writeFirstBoundary() throws IOException {
        mOutputStream.write(("--" + mBoundary + "\r\n").getBytes());
    }

    /**
     * 添加文本参数
     *
     * @param paramName
     * @param value
     */
    public void addStringPart(final String paramName, final String value) {
        writeToOutputStream(paramName, value.getBytes(), TYPE_TEXT_CHARSET, BIT_ENCODING, "");
    }

    /**
     * 将数据写入到输出流中
     *
     * @param paramName
     * @param rawData
     * @param type
     * @param encodingBytes
     * @param fileName
     */
    private void writeToOutputStream(String paramName, byte[] rawData, String type,
                                     byte[] encodingBytes,
                                     String fileName) {
        try {
            writeFirstBoundary();
            mOutputStream.write((CONTENT_TYPE + type + NEW_LINE_STR).getBytes());
            mOutputStream
                    .write(getContentDispositionBytes(paramName, fileName));
            mOutputStream.write(encodingBytes);
            mOutputStream.write(rawData);
            mOutputStream.write(NEW_LINE_STR.getBytes());
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加二进制参数, 例如Bitmap的字节流参数
     *
     * @param paramName
     * @param rawData
     */
    public void addBinaryPart(String paramName, final byte[] rawData) {
        writeToOutputStream(paramName, rawData, TYPE_OCTET_STREAM, BINARY_ENCODING,paramName);
    }

    /**
     * 添加文件参数,可以实现文件上传功能
     *
     * @param key
     * @param file
     */
    public void addFilePart(final String key, final File file) {
        InputStream fin = null;
        try {
            fin = new FileInputStream(file);
            writeFirstBoundary();
            final String type = CONTENT_TYPE + TYPE_OCTET_STREAM + NEW_LINE_STR;
            mOutputStream.write(getContentDispositionBytes(key, file.getName()));
            mOutputStream.write(type.getBytes());
            mOutputStream.write(BINARY_ENCODING);

            final byte[] tmp = new byte[4096];
            int len = 0;
            while ((len = fin.read(tmp)) != -1) {
                mOutputStream.write(tmp, 0, len);
            }
            mOutputStream.flush();
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            closeSilently(fin);
        }
    }

    private void closeSilently(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    private byte[] getContentDispositionBytes(String paramName, String fileName) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(CONTENT_DISPOSITION + "form-data; name=\"" + paramName + "\"");
        // 文本参数没有filename参数,设置为空即可  
        if (fileName != null && !"".equals(fileName)) {
            stringBuilder.append("; filename=\""
                    + fileName + "\"");
        }

        return stringBuilder.append(NEW_LINE_STR).toString().getBytes();
    }

    @Override
    public long getContentLength() {
        return mOutputStream.toByteArray().length;
    }

    @Override
    public Header getContentType() {
        return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + mBoundary);
    }

    @Override
    public boolean isChunked() {
        return false;
    }

    @Override
    public boolean isRepeatable() {
        return false;
    }

    @Override
    public boolean isStreaming() {
        return false;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException {
        // 参数最末尾的结束符  
        final String endString = "--" + mBoundary + "--\r\n";
        // 写入结束符  
        mOutputStream.write(endString.getBytes());
        //  
        outstream.write(mOutputStream.toByteArray());
    }

    @Override
    public Header getContentEncoding() {
        return null;
    }

    @Override
    public void consumeContent() throws IOException,
            UnsupportedOperationException {
        if (isStreaming()) {
            throw new UnsupportedOperationException(
                    "Streaming entity does not implement #consumeContent()");
        }
    }

    @Override
    public InputStream getContent() {
        return new ByteArrayInputStream(mOutputStream.toByteArray());
    }
}  
(7)MultipartRequest
public class MultipartRequest extends Request {

    private MultipartEntity mMultiPartEntity;

    private Map mHeaders = new HashMap();

    private final Response.Listener mListener;

    /**
     * Creates a new request with the given url.
     *
     * @param url      URL to fetch the string at
     * @param listener Listener to receive the String response
     */
    public MultipartRequest(String url, Response.Listener listener) {
        this(url, listener, null);
    }

    /**
     * Creates a new POST request.
     *
     * @param url           URL to fetch the string at
     * @param listener      Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public MultipartRequest(String url, Response.Listener listener, Response.ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        mListener = listener;
    }

    /**
     * @return
     */
    public MultipartEntity getMultiPartEntity() {
        if (mMultiPartEntity == null) {
            mMultiPartEntity = new MultipartEntity();
        }
        return mMultiPartEntity;
    }

    public void setmMultiPartEntity(MultipartEntity mMultiPartEntity) {
        this.mMultiPartEntity = mMultiPartEntity;
    }

    @Override
    public String getBodyContentType() {
        return mMultiPartEntity.getContentType().getValue();
    }

    public void addHeader(String key, String value) {
        mHeaders.put(key, value);
    }

    @Override
    public Map getHeaders() throws AuthFailureError {
        return mHeaders;
    }

    @Override
    public byte[] getBody() {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            // multipart body  
            mMultiPartEntity.writeTo(bos);
        } catch (IOException e) {
            Log.e("", "IOException writing to ByteArrayOutputStream");
        }
        return bos.toByteArray();
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        String parsed = "";
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

}
(8)VolleyHelper   
public class VolleyHelper {
    private static VolleyHelper mInstance;
    private RequestQueue mRequestQueue;
    private static Context mCtx;

    private VolleyHelper(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();
    }

    public static synchronized VolleyHelper getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new VolleyHelper(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {   
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            if (mCtx==null){
                return null;
            }
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue; 
    }

    public  boolean addToRequestQueue(Request req) {
        if (getRequestQueue()==null){
            return false;
        }
        req.setRetryPolicy(new DefaultRetryPolicy(10000, 0, 1.0f));
        getRequestQueue().add(req);
        return true;
    }

    public void clearRequestQueue(){
        if (getRequestQueue()!=null){
            getRequestQueue().cancelAll(mCtx.getApplicationContext());
        }
    }

}

8.您可能遇到的问题

(1)

现象:

Face++ 底层使用的是Volley网络请求 我使用的时候第一个错就是 Face++SDK中关于Volley的包 全部报错

解决: 在我们的项目中 重新导入 Volley 的依赖

implementation 'com.mcxiaoke.volley:library:1.0.19'

(2)Face++SDK并没有把相关识别的方法弄成API 在CV的时候务必CV完整

(3)Face++集成从看Demo到看文档 集成起来也是蛮顺利的 祝快快集成

 

(4)我看了关于Face++的博客 本质是 我们用户输入真实姓名和身份证号以后 Face++会调取身份证和我们的人脸进行比对

 

Face++ 集成就到这里了,希望能给您带来帮助。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Android)