踩了不少坑,终于把这个扫描版的身份证识别做出来了,图片识别引擎用的是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();

上述就是整个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;            }        }    };

我们把图片处理,图片识别放在了异步任务中,因为该接口是不停的回调的、每次传进一张照片进入后,把标志改为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("\n", "");            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. 深入探究Android的WebView下载网络文件的盗链问题
  2. Android 获取远程图片与本地图片缓存
  3. android 异步获取图片
  4. android R.java资源文件不自动生成的原因
  5. Android 读取app内json配置文件
  6. Android Studio 学习笔记(一)环境搭建、文件目录等相关说明
  7. Android大图片内存清理
  8. Android context 文件模式

随机推荐

  1. Android(安卓)SDK 开发纪要
  2. android中借助高德地图sdk实现基本的定位
  3. android中的所谓观察者模式
  4. Android系统架构学习笔记
  5. Android加载图片防止OOM
  6. Android有那些好的进阶书籍呢
  7. Android中使用SurfaceView的方法
  8. Android中的WebView的使用
  9. Android实现电话拨号器和短信发送器
  10. Android(安卓)Studio 2.3 打包apk