最近项目需求需要输入用户真实姓名 身份证号 再去调用人脸识别 效验是否是本人 所以就首先关注了Face++(旷视)的人脸识别,听说是 正确率很高.....所以就集成了.
创建好应用以后需要上传你项目的包名
您可以按照您项目的需求集成不同的SDK(这里只集成人脸识别SDK)
建议您,先把申请应用完以后的AppKey 和secret 配置到Face++的Demo中 跑完这个流程
点击sdk文件夹
并在app下 build.gradle文件下 dependencies中写入
implementation(name: 'meglive_still', ext: 'aar')
在repositories中写入
flatDir { dirs 'libs' }
(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());
}
}
}
(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++会调取身份证和我们的人脸进行比对