Android tesseract-orc之扫描身份证号码

【Android tesseract-orc之扫描身份证号码】学向勤中得,萤窗万卷书。这篇文章主要讲述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 = https://www.songbingjia.com/android/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
  • 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
很多人都要代码,源码在这~

    推荐阅读