android ocr——身份证识别的功能实现
ocr opencv 想必做过程图像识别的同学们都对这两个词不陌生吧。
ocr (optical character recognition ,光学字符识别) 是指电子设备(例如扫描仪或数码相机)检查纸上的字符,通过检测暗,亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程。 这样就给我编程提供了接口,我们可以识别图片的文字了 (有些文档我们通过手机拍照的,直接生成word )身份证识别,银行卡识别等。
opencv 是什么呢
opencv的全称是:open source computer vision library。opencv是一个基于bsd许可(开源)发行的跨平台计算机视觉库,可以运行在linux、windows和mac os操作系统上。它轻量级而且高效——由一系列 c 函数和少量 c++ 类构成,同时提供了python、ruby、matlab等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
上面是 百度百科给出的定义说白了就是给我们编程提供的类库而已
android 如果想使用ocr
我们可以使用google 开源的项目tesseract-ocr
github 下载地址:
今天我不讲如何编译 ocr 这个东西
主要说下,识别二维码的这个项目和tesseract-ocr 整合成一个识别身份证号码的 过程
后面我会把他们编译成类库供大家使用的
orc 识别方法已经封装成一个简单的类 ocr
package com.dynamsoft.tessocr; import android.content.context; import android.content.res.assetmanager; import android.graphics.bitmap; import android.os.environment; import com.googlecode.tesseract.android.tessbaseapi; import java.io.file; /** * created by cyl on 2016/3/26. * email:670654904@qq.com * 这个类 就是调用 ocr 的接口 * 这个是识别过程是耗时的 操作 请放到线程 操作 */ public class ocr { private tessbaseapi mtess; private boolean flag; private context context; private assetmanager assetmanager; public ocr() { // todo auto-generated constructor stub mtess = new tessbaseapi(); string datapath = environment.getexternalstoragedirectory() + "/tesseract/"; string language = "eng"; //请将你的语言包放到这里 sd 的 tessseract 下的tessdata 下 file dir = new file(datapath + "tessdata/"); if (!dir.exists()) dir.mkdirs(); flag = mtess.init(datapath, language); } /** * 识别出来bitmap 上的文字 * @param bitmap 需要识别的图片 * @return */ public string getocrresult(bitmap bitmap) { string result = "dismiss langues"; if(flag){ mtess.setimage(bitmap); result = mtess.getutf8text(); } return result; } public void ondestroy() { if (mtess != null) mtess.end(); } }
方法很简单 :
创建对象,调用getocrresult方法就行了,注意这个识别过程是耗时,放到线程去操作。避免anr问题
然后我们需要把识别集成到二维码扫描里面
下面这个对二维码扫描这个项目介绍的比较详细
下面给大家介绍一下,zxing库里面主要的类以及这些类的作用:
- captureactivity。这个是启动activity 也就是扫描器。
- captureactivityhandler 解码处理类,负责调用另外的线程进行解码。
- decodethread 解码的线程。
- com.google.zxing.client.android.camera 包,摄像头控制包。
- viewfinderview 自定义的view,就是我们看见的拍摄时中间的框框了。
我可以简单考虑一下 图片识别,我们需要先获取图片才能识别,当识别成功以后应该将数据返回 并反馈给用户我们已经完成了识别。
第一首先 我们如何获取图像 即 bitmap 从上面主要功能的类可以看出来。
我应该去captureactivityhandler 解码处理处理中去找,不管识别二维码还是图片,身份证啊。最终都是识别bitmap
所以我们这里可以找到相机捕捉到的图像;
decodehandler
/* * copyright (c) 2010 zxing authors * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package com.sj.app.decoding; import android.graphics.bitmap; import android.os.bundle; import android.os.handler; import android.os.looper; import android.os.message; import android.util.log; import com.dynamsoft.tessocr.ocr; import com.google.zxing.binarybitmap; import com.google.zxing.decodehinttype; import com.google.zxing.multiformatreader; import com.google.zxing.readerexception; import com.google.zxing.result; import com.google.zxing.common.hybridbinarizer; import com.sj.app.camera.cameramanager; import com.sj.app.camera.planaryuvluminancesource; import com.sj.app.utils.idmatch; import com.sj.erweima.mipcaactivitycapture; import com.sj.erweima.r; import java.util.hashtable; import java.util.list; final class decodehandler extends handler { private static final string tag = decodehandler.class.getsimplename(); private final mipcaactivitycapture activity; private final multiformatreader multiformatreader; decodehandler(mipcaactivitycapture activity, hashtable<decodehinttype, object> hints) { multiformatreader = new multiformatreader(); multiformatreader.sethints(hints); this.activity = activity; } @override public void handlemessage(message message) { switch (message.what) { case r.id.decode: // log.d(tag, "got decode message"); decode((byte[]) message.obj, message.arg1, message.arg2); break; case r.id.quit: looper.mylooper().quit(); break; } } /** * decode the data within the viewfinder rectangle, and time how long it * took. for efficiency, reuse the same reader objects from one decode to * the next. * * @param data * the yuv preview frame. * @param width * the width of the preview frame. * @param height * the height of the preview frame. */ private void decode(byte[] data, int width, int height) { long start = system.currenttimemillis(); result rawresult = null; // modify here byte[] rotateddata = new byte[data.length]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) rotateddata[x * height + height - y - 1] = data[x + y * width]; } int tmp = width; // here we are swapping, that's the difference to #11 width = height; height = tmp; planaryuvluminancesource source = cameramanager.get() .buildluminancesource(rotateddata, width, height); binarybitmap bitmap = new binarybitmap(new hybridbinarizer(source)); try { //相机中捕捉到的 bitmap image = source.rendercroppedgreyscalebitmap(); doorc(source); rawresult = multiformatreader.decodewithstate(bitmap); } catch (readerexception re) { // continue } finally { multiformatreader.reset(); } if (rawresult != null) { long end = system.currenttimemillis(); log.d(tag, "found barcode (" + (end - start) + " ms):\n" + rawresult.tostring()); message message = message.obtain(activity.gethandler(), r.id.decode_succeeded, rawresult); bundle bundle = new bundle(); bundle.putparcelable(decodethread.barcode_bitmap, source.rendercroppedgreyscalebitmap()); message.setdata(bundle); // log.d(tag, "sending decode succeeded message..."); message.sendtotarget(); } else { message message = message.obtain(activity.gethandler(), r.id.decode_failed); message.sendtotarget(); } } private handler handler = new handler(){ public void handlemessage(message msg) { cardid cardid = (cardid) msg.obj; if(cardid != null){ message message = message.obtain(activity.gethandler(), r.id.decode_succeeded, cardid.id); bundle bundle = new bundle(); bundle.putparcelable(decodethread.barcode_bitmap, cardid.bitmap); message.setdata(bundle); // log.d(tag, "sending decode succeeded message..."); message.sendtotarget(); } }; }; private void doorc(final planaryuvluminancesource source) { new thread(new runnable() { @override public void run() { bitmap bitmap = source.rendercroppedgreyscalebitmap(); string id = new ocr().getocrresult(bitmap); if(id != null){ list<string> list = idmatch.machid(id); if(list!= null && list.size()>0){ string cardid = list.get(0); if(cardid != null){ message msg = message.obtain(); cardid cardid2 = new cardid(cardid, bitmap); msg.obj = cardid2; handler.sendmessage(msg); } } } } }).start(); } public class cardid{ private string id; private bitmap bitmap; public cardid(string id, bitmap bitmap) { super(); this.id = id; this.bitmap = bitmap; } public string getid() { return id; } public void setid(string id) { this.id = id; } public bitmap getbitmap() { return bitmap; } public void setbitmap(bitmap bitmap) { this.bitmap = bitmap; } } }
当解析成功的时候就将结果通过handler 返回到ui 线程中去了,对于 扫描框我们可以响应调节。
cameramanager 这个类 控制扫描框的大小。
public rect getframingrect() { point screenresolution = configmanager.getscreenresolution(); if (framingrect == null) { if (camera == null) { return null; } int width = screenresolution.x * 7 / 8; if (width < min_frame_width) { width = min_frame_width; } else if (width > max_frame_width) { // width = max_frame_width; } int height = screenresolution.y * 3 / 4; if (height < min_frame_height) { height = min_frame_height; } else if (height > max_frame_height) { height = max_frame_height; } int leftoffset = (screenresolution.x - width) / 2; int topoffset = (screenresolution.y - height) / 2; framingrect = new rect(leftoffset, topoffset, leftoffset + width, topoffset + height); log.d(tag, "calculated framing rect: " + framingrect); } return framingrect; }
改变这个方法就可以改变这个扫描框的大小了。
需要提示的是 如果您的手机是android 6.0以上 请查看 sd卡根目录是否存在tesseract/tessdata目录 以及下面的文件 如果没有存在说明 应用没有获取到存储权限。
原文链接:http://blog.csdn.net/tiandiyinghun/article/details/50985961
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: Java实现动态代理