说明:
此文使用华为SDK、百度SDK、百度在线API三种方式实现。
一、使用华为SDK实现身份证识别:
说明:免费,不需要联网。
1.AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
2. 工程根目录build.gradle:
buildscript {repositories {//...省略之前配的其他地址maven { url "https://developer.huawei.com/repo/" }}dependencies {//...省略之前配的其他地址classpath 'com.huawei.agconnect:agcp:1.6.0.300'}
}allprojects {repositories {//...省略之前配的其他地址maven { url "https://developer.huawei.com/repo/" }}
}
3.工程/app/build.gradle:
//...省略之前配的其他地址
apply plugin: 'com.huawei.agconnect'
android {//...省略之前配的其他地址packagingOptions {exclude 'lib/arm64-v8a/libmsoptimize.so'}
}
dependencies {//...省略之前配的其他地址implementation 'com.huawei.hms:ml-computer-card-icr-cn:2.0.3.303'implementation 'com.huawei.hms:ml-computer-vision-ocr:2.0.5.301'implementation 'com.huawei.hms:ml-computer-vision-ocr-cn-model:2.0.5.301'implementation 'com.huawei.hms:ml-computer-vision-ocr-jk-model:2.0.5.301'implementation 'com.huawei.hms:ml-computer-vision-ocr-latin-model:2.0.5.301'implementation 'com.huawei.hms:ml-computer-card-gcr-plugin:2.0.1.301'
}
4.调用身份证SDK:
(1)身份证识别业务类:
public class HWIDCardScanBiz {private static HWIDCardScanBiz I;public static HWIDCardScanBiz I() {if (I == null) {I = new HWIDCardScanBiz();}return I;}/*** 打开身份证识别页,调用之前要申请蓝牙动态权限*/public void startIDCardScanActivity(Context context, IDCardScanCallback callback) {MLCnIcrCaptureConfig config = new MLCnIcrCaptureConfig.Factory().setFront(true).create();MLCnIcrCapture icrCapture = MLCnIcrCaptureFactory.getInstance().getIcrCapture(config);icrCapture.capture(new HWIDScanCallback(callback), context);}private class HWIDScanCallback implements MLCnIcrCapture.CallBack {private IDCardScanCallback callback;public HWIDScanCallback(IDCardScanCallback callback) {this.callback = callback;}/*** 识别成功回调*/@Overridepublic void onSuccess(MLCnIcrCaptureResult result) {if (result == null) {Toast.makeText(XApp.Companion.getContext(), "识别失败", Toast.LENGTH_SHORT);return;}if (callback != null) {IDCardInfo info = new IDCardInfo(result.name, result.sex, result.nation, result.birthday, result.address, result.idNum, result.authority, result.validDate, result.sideType);callback.onIDCardResult(info);}}@Overridepublic void onCanceled() {}@Overridepublic void onFailure(int retCode, Bitmap bitmap) {Toast.makeText(XApp.Companion.getContext(), "识别失败", Toast.LENGTH_SHORT);}@Overridepublic void onDenied() {}}public interface IDCardScanCallback {void onIDCardResult(IDCardInfo result);}
}
(2)调用识别业务类,更新UI:
HWIDCardScanBiz.I().startIDCardScanActivity(context, new HWIDCardScanBiz.IDCardScanCallback() {@Overridepublic void onIDCardResult(IDCardInfo result) {/*
result.name: 姓名
result.sex:性别
result.nation:国籍
result.birthday:生日
result.address:地址
result.idNum:身份证号
result.validDate:有效期*/}
});
二、使用百度云SDK实现身份证识别:
说明:免费额度个人帐户每月1千次/企业帐户每月2千次,要联网。
1.添加配置: (1)AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
(2)proguard-rules.pro添加混淆例外:
-keep class com.baidu.ocr.sdk.**{*;}
-dontwarn com.baidu.ocr.**
(3)将ocrsdk.aar放入工程根\app\libs目录下。(4)将ocr_ui模块工程放入工程根目录下,在settings.gradle中添加导入模块工程的配置:
include ':app', 'ocrsdk', 'ocr_ui'
(5)工程/app/build.gradle,导入ocrsdk.aar与ocr_ui模块工程:
//...省略之前配的其他地址
dependencies {//...省略之前配的其他地址implementation(name: 'ocrsdk', ext: 'aar')implementation project(path: ':ocr_ui')
}
2.调用代码实现身份证识别: 第1步:调用SDK方法获取token; 第2步:调用SDK方法打开身份证识别界面; 第3步,在调用的Activity的onActivityResult中调用SDK识别图片中的身份信息。调用身份证SDK业务类:
public class BDIDCardScanBiz {public static final int REQUEST_CODE_CAMERA = 100;private boolean mHasGotToken;private static BDIDCardScanBiz I;public static BDIDCardScanBiz I() {if (I == null) {I = new BDIDCardScanBiz();}return I;}/*** 第1步:调用此方法获取token*/public void getAccessToken() {OCR.getInstance(App.getContext()).initAccessTokenWithAkSk(new OnResultListener() {@Overridepublic void onResult(Object o) {AccessToken accessToken = (AccessToken) o;String token = accessToken.getAccessToken();mHasGotToken = true;}@Overridepublic void onError(OCRError error) {// 调用失败,返回OCRError子类SDKError对象}}, App.getContext(), "Zxuz7GjLGsjBna44UjOQPVJv", "teLf4S7EjI5fjIshagZoovRSKlZSfPwM");}/*** 第2步:调用此方法打开身份证识别界面*/public void startIDCardScanActivity(Activity act) {if (!PermissionUtil.checkCameraPermission(act)) return;Intent intent = new Intent(act, CameraActivity.class);intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH,FileUtils.getSaveFile(App.getContext()).getAbsolutePath());intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, CameraActivity.CONTENT_TYPE_ID_CARD_FRONT);act.startActivityForResult(intent, REQUEST_CODE_CAMERA);}/*** 第3步,在onActivityResult中调此方法,获取身份证信息*/public void handlerData(String contentType) {if (TextUtils.isEmpty(contentType)) return;String filePath = FileUtils.getSaveFile(App.getContext()).getAbsolutePath();if (CameraActivity.CONTENT_TYPE_ID_CARD_FRONT.equals(contentType)) {recIDCard(IDCardParams.ID_CARD_SIDE_FRONT, filePath);} else if (CameraActivity.CONTENT_TYPE_ID_CARD_BACK.equals(contentType)) {recIDCard(IDCardParams.ID_CARD_SIDE_BACK, filePath);}}private void recIDCard(String idCardSide, String filePath) {IDCardParams param = new IDCardParams();param.setImageFile(new File(filePath));// 设置身份证正反面param.setIdCardSide(idCardSide);// 设置方向检测param.setDetectDirection(true);// 设置图像参数压缩质量0-100, 越大图像质量越好但是请求时间越长。 不设置则默认值为20param.setImageQuality(20);param.setDetectRisk(true);OCR.getInstance(App.getContext()).recognizeIDCard(param, new OnResultListener<IDCardResult>() {@Overridepublic void onResult(IDCardResult result) {if (result != null) {//获取身份信息}}@Overridepublic void onError(OCRError error) {}});}
}
三、使用百度云API实现身份证识别:
说明:免费额度个人帐户每月1千次/企业帐户每月2千次,要联网。
1.AndroidManifest.xml添加权限:
同上面两种
2.调用代码实现身份证识别:
第1步,调用百度API获取token:
public static String getToken() {String url = "https://aip.baidubce.com/oauth/2.0/token?client_id=" + Config.CLIENT_ID + "&client_secret=" + Config.CLIENT_SECRET + "&grant_type=client_credentials";if (!NetHelp.getCurConnectStatus()) {return null;}Response response = null;try {Request request = new Request.Builder().url(url).get().build();LogUtils.d("getToken request url :" + url);response = httpClient.newCall(request).execute();} catch (IOException e) {LogUtils.d("getToken request exception: " + e.getMessage());e.printStackTrace();return null;}LogUtils.d("getToken response isSuccessful: " + response.isSuccessful());if (response != null && response.isSuccessful()) {try {String strRecData = response.body().string();LogUtils.d("getToken response body: " + strRecData);if (strRecData != null && strRecData.length() > 2) {JSONObject jsonObj = new JSONObject(strRecData);if (jsonObj != null && !jsonObj.isNull("access_token")) {return jsonObj.getString("access_token");}}return response.body().string();} catch (Exception e) {LogUtils.d("getToken response parse exception: " + e.getMessage());e.printStackTrace();return null;}}return null;}
第2步,调用系统相机拍身份证照片(可以自已实现带身份证头像框的拍照功能,界面更加美观):
/*** 调起系统相机进行拍照,此步可以自已实现拍照功能,界面更美观*/public void startIDCardScanActivity(Activity act) {Log.i("IDCard", "IDCard startIDCardScanActivity >>>");Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (intent.resolveActivity(act.getPackageManager()) != null) {act.startActivityForResult(intent, REQUEST_CODE_CAMERA);}}
第3步,在onActivityResult中,将图片上传百度API获取身份证信息:
/*** 解析身份证图片,获取身份信息*/public void handlerData(Activity act, Intent data, IDCardScanCallback callback) {new Thread() {@Overridepublic void run() {Bundle bundle = data.getExtras();Log.i("IDCard", "IDCard handlerData bundle >>> : " + bundle);if (bundle == null) return;Bitmap bitmap = (Bitmap) bundle.get("data");Log.i("IDCard", "IDCard handlerData bitmap >>> : " + bitmap);if (bitmap == null) return;File file = new File(App.getContext().getCacheDir(), System.currentTimeMillis() + ".jpeg");if (!file.getParentFile().exists()) file.mkdirs();try {FileOutputStream out = new FileOutputStream(file);bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);out.flush();out.close();Log.i("IDCard", "IDCard saveBitmap filePath: " + file.getAbsolutePath() + " fileLen: " + file.length());} catch (IOException ex) {ex.printStackTrace();}String filePath = file.getAbsolutePath();Log.i("IDCard", "IDCard parseImage >>> filePath: " + filePath);final IDCardInfo info = HttpHelp.getIDCard(new File(filePath));if (info != null && callback != null) {act.runOnUiThread(() -> callback.onIDCardResult(info));}}}.start();}
public static IDCardInfo getIDCard(File file){if (!NetHelp.getCurConnectStatus() || file == null || file.length() <= 0){return null;}String accessToken = getToken();if (TextUtils.isEmpty(accessToken)) return null;String url = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=" + accessToken;String img = null;try {img = FileUtils.base64File(file);} catch (Exception e) {e.printStackTrace();}if (TextUtils.isEmpty(img)) return null;Response response = null;try {FormBody.Builder builder = new FormBody.Builder().add("id_card_side", "front").add("image", img);Request request = new Request.Builder().url(url).post(builder.build()).build();LogUtils.d("getIDCard request url :" + url + " img: " + img);response = httpClient.newCall(request).execute();} catch (IOException e) {LogUtils.d("getIDCard request exception: " + e.getMessage());e.printStackTrace();return null;}LogUtils.d("getIDCard response isSuccessful: " + response.isSuccessful());if (response != null && response.isSuccessful()) {try {String strRecData = response.body().string();LogUtils.d("getIDCard response body: " + strRecData);if (strRecData != null && strRecData.length() > 2) {JSONObject rootObj = new JSONObject(strRecData);if (rootObj != null && !rootObj.isNull("words_result")) {JSONObject wordObj = rootObj.getJSONObject("words_result");IDCardInfo info = new IDCardInfo();if (!wordObj.isNull("姓名") && !wordObj.getJSONObject("姓名").isNull("words")) {info.name = wordObj.getJSONObject("姓名").getString("words");}if (wordObj != null && !wordObj.isNull("民族") && !wordObj.getJSONObject("民族").isNull("words")) {info.nation = wordObj.getJSONObject("民族").getString("words");}if (wordObj != null && !wordObj.isNull("住址") && !wordObj.getJSONObject("住址").isNull("words")) {info.address = wordObj.getJSONObject("住址").getString("words");}if (wordObj != null && !wordObj.isNull("公民身份号码") && !wordObj.getJSONObject("公民身份号码").isNull("words")) {info.idNum = wordObj.getJSONObject("公民身份号码").getString("words");}if (wordObj != null && !wordObj.isNull("出生") && !wordObj.getJSONObject("出生").isNull("words")) {info.birthday = wordObj.getJSONObject("出生").getString("words");}if (wordObj != null && !wordObj.isNull("性别") && !wordObj.getJSONObject("性别").isNull("words")) {info.sex = wordObj.getJSONObject("性别").getString("words");}LogUtils.d("getIDCard response parse IDCardInfo: " + info.toString());return info;}}} catch (Exception e) {LogUtils.d("getIDCard response parse exception: " + e.getMessage());e.printStackTrace();return null;}}return null;}