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

