android ocr——身份證識別的功能實現(xiàn)
ocr OpenCV 想必做過程圖像識別的同學(xué)們都對這兩個詞不陌生吧。
ocr (optical character recognition ,光學(xué)字符識別) 是指電子設(shè)備(例如掃描儀或數(shù)碼相機)檢查紙上的字符,通過檢測暗,亮的模式確定其形狀,然后用字符識別方法將形狀翻譯成計算機文字的過程。 這樣就給我編程提供了接口,我們可以識別圖片的文字了 (有些文檔我們通過手機拍照的,直接生成word )身份證識別,銀行卡識別等。
opencv 是什么呢
OpenCV的全稱是:Open Source Computer Vision Library。OpenCV是一個基于BSD許可(開源)發(fā)行的跨平臺計算機視覺庫,可以運行在Linux、Windows和Mac OS操作系統(tǒng)上。它輕量級而且高效——由一系列 C 函數(shù)和少量 C++ 類構(gòu)成,同時提供了Python、Ruby、MATLAB等語言的接口,實現(xiàn)了圖像處理和計算機視覺方面的很多通用算法。
上面是 百度百科給出的定義說白了就是給我們編程提供的類庫而已
Android 如果想使用OCR
我們可以使用google 開源的項目tesseract-ocr
github 下載地址:https://github.com/justin/tesseract-ocr
今天我不講如何編譯 ocr 這個東西
主要說下,識別二維碼的這個項目和tesseract-ocr 整合成一個識別身份證號碼的 過程
后面我會把他們編譯成類庫供大家使用的
ORC 識別方法已經(jīng)封裝成一個簡單的類 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 * 這個類 就是調(diào)用 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(); } }
方法很簡單 :
創(chuàng)建對象,調(diào)用getOcrResult方法就行了,注意這個識別過程是耗時,放到線程去操作。避免ANR問題
然后我們需要把識別集成到二維碼掃描里面
下面這個對二維碼掃描這個項目介紹的比較詳細(xì)
http://www.dbjr.com.cn/article/53487.htm
下面給大家介紹一下,ZXing庫里面主要的類以及這些類的作用:
- CaptureActivity。這個是啟動Activity 也就是掃描器。
- CaptureActivityHandler 解碼處理類,負(fù)責(zé)調(diào)用另外的線程進(jìn)行解碼。
- DecodeThread 解碼的線程。
- com.google.zxing.client.android.camera 包,攝像頭控制包。
- ViewfinderView 自定義的View,就是我們看見的拍攝時中間的框框了。
我可以簡單考慮一下 圖片識別,我們需要先獲取圖片才能識別,當(dāng)識別成功以后應(yīng)該將數(shù)據(jù)返回 并反饋給用戶我們已經(jīng)完成了識別。
第一首先 我們?nèi)绾潍@取圖像 即 bitmap 從上面主要功能的類可以看出來。
我應(yīng)該去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; } } }
當(dāng)解析成功的時候就將結(jié)果通過handler 返回到UI 線程中去了,對于 掃描框我們可以響應(yīng)調(diào)節(jié)。
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目錄 以及下面的文件 如果沒有存在說明 應(yīng)用沒有獲取到存儲權(quán)限。
原文鏈接:http://blog.csdn.net/tiandiyinghun/article/details/50985961
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android 新手引導(dǎo)蒙層效果實現(xiàn)代碼示例
本篇文章主要介紹了Android 新手引導(dǎo)蒙層效果實現(xiàn)代碼示例,具有一定的參考價值,有興趣的可以了解一下。2017-01-01解決Android studio 2.3升級到Android studio 3.0 后apt報錯問題
原來項目在Android studio 2.3一切正常,升級到了3.0之后報錯,不支持apt了,其實解決這個問題很簡單,只需要修改兩點內(nèi)容就可以,下面腳本之家小編帶領(lǐng)大家通過本文學(xué)習(xí)吧2017-12-12Android的OkHttp包處理用戶認(rèn)證的代碼實例分享
OkHttp包(GitHub主頁github.com/square/okhttp)是一款高人氣安卓HTTP支持包,這里我們來看一下Android的OkHttp包處理用戶認(rèn)證的代碼實例分享:2016-07-07Android 利用ViewPager+GridView實現(xiàn)首頁導(dǎo)航欄布局分頁效果
用ViewPager+GridView實現(xiàn)首頁導(dǎo)航欄布局分頁效果來實現(xiàn)的效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-10-10rxjava+retrofit實現(xiàn)多圖上傳實例代碼
本篇文章主要介紹了rxjava+retrofit實現(xiàn)多圖上傳實例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06解決Android從相冊中獲取圖片出錯圖片卻無法裁剪問題的方法
這篇文章主要介紹了解決Android從相冊中獲取圖片出錯圖片卻無法裁剪問題的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-01-01Android自定義ListView實現(xiàn)下拉刷新
這篇文章主要為大家詳細(xì)介紹了Android自定義ListView實現(xiàn)下拉刷新的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07Android實現(xiàn)動態(tài)自動匹配輸入內(nèi)容功能
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)動態(tài)自動匹配輸入內(nèi)容功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06Android實現(xiàn)音頻條形圖效果(仿音頻動畫無監(jiān)聽音頻輸入)
這篇文章主要介紹了Android實現(xiàn)音頻條形圖效果(仿音頻動畫無監(jiān)聽音頻輸入)的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09