Android:身份证识别功能实现

说明:

此文使用华为SDK、百度SDK、百度在线API三种方式实现。

一、使用华为SDK实现身份证识别:

说明:免费,不需要联网。

复制代码
1.AndroidManifest.xml添加权限:
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" />
  1. 工程根目录build.gradle:
Groovy 复制代码
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:
Groovy 复制代码
//...省略之前配的其他地址
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)身份证识别业务类:

java 复制代码
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;
        }

        /**
         * 识别成功回调
         */
        @Override
        public 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);
            }
        }

        @Override
        public void onCanceled() {
        }

        @Override
        public void onFailure(int retCode, Bitmap bitmap) {
            Toast.makeText(XApp.Companion.getContext(), "识别失败", Toast.LENGTH_SHORT);
        }

        @Override
        public void onDenied() {
        }
    }

    public interface IDCardScanCallback {
        void onIDCardResult(IDCardInfo result);
    }
}

(2)调用识别业务类,更新UI:

java 复制代码
HWIDCardScanBiz.I().startIDCardScanActivity(context, new HWIDCardScanBiz.IDCardScanCallback() {
    @Override
    public 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添加权限:
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添加混淆例外:
java 复制代码
-keep class com.baidu.ocr.sdk.**{*;}
-dontwarn com.baidu.ocr.**
复制代码
(3)将ocrsdk.aar放入工程根\app\libs目录下。

(4)将ocr_ui模块工程放入工程根目录下,在settings.gradle中添加导入模块工程的配置:
Groovy 复制代码
include ':app', 'ocrsdk', 'ocr_ui'
复制代码
(5)工程/app/build.gradle,导入ocrsdk.aar与ocr_ui模块工程:
Groovy 复制代码
//...省略之前配的其他地址
dependencies {
    //...省略之前配的其他地址
    implementation(name: 'ocrsdk', ext: 'aar')
    implementation project(path: ':ocr_ui')
}
复制代码
2.调用代码实现身份证识别:
第1步:调用SDK方法获取token;
第2步:调用SDK方法打开身份证识别界面;
第3步,在调用的Activity的onActivityResult中调用SDK识别图片中的身份信息。

调用身份证SDK业务类:
java 复制代码
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() {

            @Override
            public void onResult(Object o) {
                AccessToken accessToken = (AccessToken) o;
                String token = accessToken.getAccessToken();
                mHasGotToken = true;
            }

            @Override
            public 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, 越大图像质量越好但是请求时间越长。 不设置则默认值为20
        param.setImageQuality(20);
        param.setDetectRisk(true);
        OCR.getInstance(App.getContext()).recognizeIDCard(param, new OnResultListener<IDCardResult>() {
            @Override
            public void onResult(IDCardResult result) {
                if (result != null) {//获取身份信息
                }
            }

            @Override
            public void onError(OCRError error) {
            }
        });
    }
}

三、使用百度云API实现身份证识别:

说明:免费额度个人帐户每月1千次/企业帐户每月2千次,要联网。

复制代码
1.AndroidManifest.xml添加权限:

同上面两种

2.调用代码实现身份证识别:

第1步,调用百度API获取token:

java 复制代码
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步,调用系统相机拍身份证照片(可以自已实现带身份证头像框的拍照功能,界面更加美观):

java 复制代码
    /**
     * 调起系统相机进行拍照,此步可以自已实现拍照功能,界面更美观
     */
    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获取身份证信息:

java 复制代码
    /**
     * 解析身份证图片,获取身份信息
     */
    public void handlerData(Activity act, Intent data, IDCardScanCallback callback) {
        new Thread() {
            @Override
            public 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();
    }
java 复制代码
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;
    }
相关推荐
m0_748235953 小时前
CentOS 7使用RPM安装MySQL
android·mysql·centos
ac-er88886 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie8 小时前
uniapp 在线更新应用
android·uniapp
zhangphil10 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲11 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥12 小时前
python操作mysql
android·python
Couvrir洪荒猛兽12 小时前
Android实训十 数据存储和访问
android
五味香15 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录15 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽17 小时前
Android实训九 数据存储和访问
android