zoukankan      html  css  js  c++  java
  • Android tesseract-orc之扫描身份证号码

    踩了不少坑,终于把这个扫描版的身份证识别做出来了,图片识别引擎用的是tesseract,在已经训练好样本的情况下,感觉识别率还是一般般~ 
    下面说一说大概几个坑、

    一、 编译tesseract-orc Android版本 
    首先你需要Android-ndk工具,Android ndk开发,我们这里不做开发,只需要编译tesseract变成so文件、tesseract Android版下载地址,这里只需要编译tesseract-two这个项目、编译方法在那篇博客说的很清楚了,编译时间有点久(耐心等待,并且大部分人在这里会扑街)

    二、测试是否编译成功 
    新建一个项目,用引用类库的方式引用tesseract-two,API的调用方法也很简单:

    TessBaseAPI baseApi=new TessBaseAPI();
    //这里进行初始化,第一个参数是训练语言的路径,第二个参数的语言名字,后面我们的训练文件都要放在这里面,这里可以先用eng代替下测试、
    baseApi.init(TESSBASE_PATH, DEFAULT_LANGUAGE);  
    baseApi.setPageSegMode(TessBaseAPI.PSM_AUTO); 
    //这里把图片放进去进行了
    baseApi.setImage(bitmap);         
    final String outputText = baseApi.getUTF8Text(); 
    Log.i(TAG, "识别结果:" + outputText);
    baseApi.end();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上述就是整个API的调用流程,值得注意的是,该流程是一个耗时操作,不可在UI线程中调用、 
    若没有闪退,并且有大概的识别文字出来,表示编译成功,接下来就开始训练新语言。

    三、训练新语言,提高识别率 
    说到训练语言这个问题,网上的文章是非常多的,不过我却在这里卡了很久,原因是因为网上大部分文章是针对3.01版本训练,而现在版本是3.02,有几个地方死活报错过不去、寻其原因在于文件名的问题!tesseract-orc3.02训练方法 这篇博客已经说的很清楚啦,我再把精简一下: 
    图片名字的格式 一定需要按照 [lang].[fontname].exp[num].tif 该格式!用id.custom.exp0.tif 作为示范

    1).转成 tif 格式图片,用jTessBoxEditor工具 合成为一张tif图片

    2).生成box文件,调用命令行: 
    tesseract id.custom.exp0.tif id.custom.exp0 batch.nochop makebox

    3).利用jTessBoxEditor工具,对文件进行编辑,校正,得到新的box文件

    4).生成.tr文件,调用命令行: 
    tesseract id.custom.exp0.tif id.custom.exp0 nobatch box.train

    5).生成字符集,调用命令行: 
    unicharset_extractor id.custom.exp0.box

    6).设置字体,新建文本文件font_properties,里面输入字体信息,内容格式为: 
    第一个fontname 一定要对应之前文件的名字, 这里输入 custom 0 1 0 0 0 ,表示是加粗字体格式

    7).接下来,进行聚合,分别调用三句命令: 
    shapeclustering -F font_properties -U unicharset id.custom.exp0.tr 
    mftraining -F font_properties -U unicharset -O id.unicharset id.custom.exp0.tr 
    cntraining id.custom.exp0.tr id.custom.exp0.tr

    8).把生成第7步生成的4个文件加入前缀“id.”(),调用命令行,生成最终数据 
    combine_tessdata id. (别漏掉了.) 
    type 1,type3, type4, type5对应的后面数据如果不是-1,就表示这次训练成功!

    9).进行测试: 
    tesseract id1.jpg output -l id

    四、集成到项目中实现拍照识别 
    如果上述训练没有问题,那么可以将训练文件 id.traineddata 放在assert文件夹中,当应用程序启动时,将其拷贝到sd卡,这里值得注意的是,拍照返回的图片都比较大,是需要进行压缩的、最终大小尽量和你训练时的大小一致,然后图片进行灰度处理,再调用API来识别。

    五、扫描识别 
    由于拍照后再识别的准确率实在是低,和拍照的角度,光线,以及拍照时身份证没有填满照片等等因素,很难做到高准确率的识别、于是我就仿造扫描二维码(支付宝扫描银行卡号)的方式,来增加识别次数提高识别率。扫描界面我是借鉴二维码扫描的代码、大致流程: 
    需要一个Camera对象来获取相机资源,用一个SurfaceView来显示相机预览,surfaceview启动时获取相机资源,并且实现自动对焦和预览回调接口,自动对焦是定时的,每过1.5秒对焦一次、而在预览回调接口中:

        /**
         * 拍照回调
         */
        PreviewCallback previewCallback = new PreviewCallback() {
            @Override
            public void onPreviewFrame(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
                if (isChoice) {
                    new MyOrcTask().execute(data);
                    isChoice = false;
                }
    
            }
        };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们把图片处理,图片识别放在了异步任务中,因为该接口是不停的回调的、每次传进一张照片进入后,把标志改为false,当异步任务执行完后,把标志改为true,这样就是单线程异步的执行图片识别任务、

    /**
         * 图片解析的异步任务!
         * 
         * @author kaifa
         * 
         */
        class MyOrcTask extends AsyncTask<byte[], Void, Void> {
    
            String text = "";
    
            @Override
            protected void onPreExecute() {
                // TODO Auto-generated method stub
                super.onPreExecute();
            }
    
            @Override
            protected Void doInBackground(byte[]... params) {
                // TODO Auto-generated method stub
                byte[] data = params[0];
    
                Size size = camera.getParameters().getPreviewSize();
                try {
                    YuvImage image = new YuvImage(data, ImageFormat.NV21,
                            size.width, size.height, null);
                    if (image != null) {
                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
                        image.compressToJpeg(
                                new Rect(0, 0, size.width, size.height), 80, stream);
                        Bitmap bitmap = BitmapFactory.decodeByteArray(
                                stream.toByteArray(), 0, stream.size());
    
                        bitmap = Bitmap.createBitmap(bitmap, x, y, width, height);
                        // 去解析
                        if (bitmap != null) {
    
                            bitmap = comp(bitmap);
                            bitmap = ImageFilter.grayScale(bitmap);
                            TessBaseAPI baseAPI = new TessBaseAPI();
                            // 初始化
                            baseAPI.init(TESSBASE_PATH, DEFAULT_LANGUAGE);
                            baseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO);
    
                            baseAPI.setImage(bitmap);
    
                            text = baseAPI.getUTF8Text();
    
                            baseAPI.end();
                        }
    
                        stream.close();
    
                    }
                } catch (Exception ex) {
                    Log.e("Sys", "Error:" + ex.getMessage());
                }
    
                return null;
            }
    
            @Override
            protected void onPostExecute(Void result) {
                // TODO Auto-generated method stub
                super.onPostExecute(result);
                text.replaceAll("
    ", "");
                text = text.trim();
                if (text.length() > 18) {
                    text = text.substring(text.length() - 18, text.length());
                    if (IDcheckClassUtil.validateIdCard18(text)) {
                        Toast.makeText(ScanActivity.this, "成功!请核对", 0).show();
                        isChoice = false;
                        textView.setText(text);
                        } else {
                        // Toast.makeText(ScanActivity.this, "就差一点点啦!", 0).show();
                        isChoice = true;
                    }
    
                } else {
                    // Toast.makeText(ScanActivity.this, "请再对齐一点点哦!", 0).show();
                    // 继续去选择图片
                    isChoice = true;
                }
    
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    很多人都要代码,源码在这~

  • 相关阅读:
    react-webpack
    react
    重置手机过程
    运用 Node-RED 开发 LoRa 应用
    IBM Node-RED 安装与使用教程
    Node-RED 入门教程:简介
    Thingsboard 3.0 修改 Title、默认语言、主题颜色
    Thingsboard 3.0 通过 tb-gateway 网关接入 MQTT 设备教程
    Thingsboard 入门学习笔记:属性
    ThingsBoard 3.0 版本发布
  • 原文地址:https://www.cnblogs.com/Alex80/p/9074948.html
Copyright © 2011-2022 走看看