溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android ORC文字識(shí)別之識(shí)別身份 證號(hào)等(附源碼)

發(fā)布時(shí)間:2020-07-27 16:59:42 來(lái)源:網(wǎng)絡(luò) 閱讀:3572 作者:qq5a70561b29238 欄目:移動(dòng)開發(fā)

項(xiàng)目地址
https://github.com/979451341/OrcTest

我們說說實(shí)現(xiàn)這個(gè)項(xiàng)目已實(shí)現(xiàn)的功能,能夠截圖手機(jī)界面的某一塊,將這個(gè)某一塊圖片的Bitmap傳給tess-two的代碼來(lái)獲取掃描結(jié)果

我這里在貼出tess-two這個(gè)專為Android而創(chuàng)建的文字識(shí)別框架的地址
https://github.com/rmtheis/tess-two

接下來(lái)我就說我如何一步一步的實(shí)現(xiàn)項(xiàng)目

1.實(shí)現(xiàn)基礎(chǔ)界面,我這里貼出已完成的界面

這樣是為了模仿掃描二維碼的界面,因?yàn)閽呙琛痢痢撂?hào)碼或者是手機(jī)號(hào)那樣長(zhǎng)條的數(shù)字,就將掃描區(qū)域也做成長(zhǎng)條狀,這個(gè)掃描區(qū)域是有意義的,因?yàn)榈綍r(shí)候截圖會(huì)只將掃描區(qū)域里的圖片信息拿去掃描,這也是為了提高掃描速度和精度。

首先要實(shí)現(xiàn)這個(gè)界面,我們需要畫出四個(gè)灰色長(zhǎng)方體的位置大小,上下左右。

left是掃描區(qū)域左邊離手機(jī)屏幕左邊的距離是手機(jī)屏幕寬度的1/10,right就是掃描區(qū)域右邊離手機(jī)屏幕左邊的距離是手機(jī)屏幕寬度的9/10,top是掃描區(qū)域頂部離手機(jī)屏幕頂部的距離是手機(jī)屏幕寬度的1/3,bottom是掃描區(qū)域底部離手機(jī)屏幕頂部的距離是手機(jī)屏幕寬度的4/9

    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = manager.getDefaultDisplay();

    PMwidth = display.getWidth();
    PMheight = display.getHeight();

    left = PMwidth/10;
    top = PMheight/3;
    right = PMwidth*9/10;
    bottom = PMheight*4/9;
    mFrameRect = new Rect(left,top,right,bottom);

畫畫

@Override
public void onDraw(Canvas canvas) {
    int width = PMwidth;
    int height = PMheight;
    Rect frame = mFrameRect;

    // 繪制焦點(diǎn)框外邊的暗色背景
    mPaint.setColor(mMaskColor);
    canvas.drawRect(0, 0, width, frame.top, mPaint);
    canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, mPaint);
    canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, mPaint);
    canvas.drawRect(0, frame.bottom + 1, width, height, mPaint);

}

還沒有完,還有布局文件放SurfaceView和按鈕,還有剛才做的自定義View

2.顯示Camera預(yù)覽和Camera拍攝

這里SurfaceView如何顯示Camera我不多說,只說如何把Camera預(yù)覽變清晰,這里是通過循環(huán)自動(dòng)對(duì)焦來(lái)完成。

設(shè)置自動(dòng)對(duì)焦接口

mCamera.autoFocus(autoFocusCallback);

這個(gè)接口初始化傳入了Handler

autoFocusCallback.setHandler(handler,MSG_AUTOFUCS);

然后這個(gè)接口實(shí)現(xiàn)類里,當(dāng)完成自動(dòng)對(duì)焦,會(huì)通過handler發(fā)送一個(gè)消息

@Override
public void onAutoFocus(boolean success, Camera camera) {
    Log.v("zzw", "autof focus "+success);
    if (mAutoFocusHandler != null) {
        mAutoFocusHandler.sendEmptyMessageDelayed(mAutoFocusMessage,AUTO_FOCUS_INTERVAL_MS);

// mAutoFocusHandler = null;
} else {
Log.v(TAG, "Got auto-focus callback, but no handler for it");
}
}

然后handler如何執(zhí)行以下代碼,再進(jìn)行一次自動(dòng)對(duì)焦,這樣就完成了循環(huán)

                case MSG_AUTOFUCS:
                    cameraUtil.autoFocus();
                    break;

然后給按鈕賦予拍攝功能,拍攝的還要停止聚焦

            handler.removeCallbacksAndMessages(null);
            cameraUtil.takePicture(TwoActivity.this,TwoActivity.this,TwoActivity.this);

這個(gè)函數(shù)會(huì)被調(diào)用,data就是圖片數(shù)據(jù)

@Override
public void onPictureTaken(byte[] data, Camera camera) 

這里要注意一件事,拍攝后Camera預(yù)覽界面就會(huì)停止,因?yàn)樗V咕劢沽?,我們需要重新設(shè)置自動(dòng)對(duì)焦,并開啟預(yù)覽

// 刷新相機(jī)
public void refreshCamera(){
    if (surfaceHolder.getSurface() == null){
        // preview surface does not exist
        return;
    }

    // stop preview before making changes
    try {
        mCamera.stopPreview();
    } catch(Exception e){
        // ignore: tried to stop a non-existent preview
    }

    // set preview size and make any resize, rotate or
    // reformatting changes here
    // start preview with new settings

    try {
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.startPreview();
        mCamera.autoFocus(autoFocusCallback);
    } catch (Exception e) {

    }
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

3.處理圖片數(shù)據(jù),完成局部截圖

繼續(xù)在onPictureTaken函數(shù)的data數(shù)據(jù)處理

因?yàn)樘幚韴D片是耗時(shí)任務(wù),所以開啟子線程完成

這里先開啟一個(gè)等待對(duì)話框

    if(!mypDialog.isShowing())
    mypDialog.show();

然后開啟子線程

    if(data != null){
        new Thread(new BitmapThread(bitmap,data,handler,TwoActivity.this)).start();

    }

將data轉(zhuǎn)換為Bitmap數(shù)據(jù)

    bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

將圖片旋轉(zhuǎn)90度

    bitmap = rotateBitmap(bitmap,90);

這是旋轉(zhuǎn)Bitmap的函數(shù)

public static Bitmap rotateBitmap(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

切割Bitmap,將掃描區(qū)域的圖片切割出來(lái)

    int PMwidth = bitmap.getWidth(); // 得到圖片的寬,高
    int PMheight = bitmap.getHeight();

    int left = PMwidth/10;
    int top = PMheight/3;
    int right = PMwidth*9/10;
    int bottom = PMheight*4/9;
    int width = right - left;
    int height = bottom - top;

    Log.v("zzw",PMheight+" "+PMwidth);

    bitmap = Bitmap.createBitmap(bitmap, left, top, width, height, null,
            false);

4.掃描出結(jié)果

其實(shí)tess-two框架的使用很簡(jiǎn)單,但是使用這個(gè)框架需要依靠訓(xùn)練文件來(lái)完成掃描,我在res目錄下放了raw文件夾,里面的eng_traineddata文件就是這個(gè)用途,但是我們不能直接使用它們,我們需要將他們復(fù)制到手機(jī)存儲(chǔ)里

下面的代碼意思是在應(yīng)用私有路徑里創(chuàng)建tesseract/tessdata/eng.traineddata相關(guān)路徑的文件并使用輸入流將文件的數(shù)據(jù)讀出來(lái),然后使用輸出流將數(shù)據(jù)傳入eng.traineddata文件

public static void initTessTrainedData(Context context){

    if(initiated){
        return;
    }

    File appFolder = context.getFilesDir();
    File folder = new File(appFolder, tessdir);
    if(!folder.exists()){
        folder.mkdir();
    }

    tesseractFolder = folder.getAbsolutePath();

    File subfolder = new File(folder, subdir);
    if(!subfolder.exists()){
        subfolder.mkdir();
    }

    File file = new File(subfolder, filename);
    trainedDataPath = file.getAbsolutePath();
    Log.d(TAG, "Trained data filepath: " + trainedDataPath);

    if(!file.exists()) {

        try {
            FileOutputStream fileOutputStream;
            byte[] bytes = readRawTrainingData(context);
            if (bytes == null){
                return;
            }

            fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(bytes);
            fileOutputStream.close();
            initiated = true;
            Log.d(TAG, "Prepared training data file");
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Error opening training data file\n" + e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, "Error opening training data file\n" + e.getMessage());
        }
    }
    else{
        initiated = true;
    }
}

好了再說說tess-two框架的使用

創(chuàng)建TessBaseAPI

    TessBaseAPI tessBaseAPI = new TessBaseAPI();

關(guān)閉測(cè)試

    tessBaseAPI.setDebug(true);

設(shè)置訓(xùn)練數(shù)據(jù)路徑和識(shí)別文字是英文

    tessBaseAPI.init(path, "eng");

設(shè)置白名單

    tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");

設(shè)置黑名單

    tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_+=-[]}{;:'\"\\|~`,./<>?"); 

設(shè)置識(shí)別模式

    tessBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO_OSD);

傳入bitmap數(shù)據(jù)

    tessBaseAPI.setImage(bitmap);

獲取掃描結(jié)果

    String inspection = tessBaseAPI.getHOCRText(0);

結(jié)束TestBaseAPI的使用

    tessBaseAPI.end();

實(shí)現(xiàn)掃描×××號(hào)碼,這里是通過正則表達(dá)式來(lái)判斷掃描出的結(jié)果是否有×××號(hào)碼,也就是說tess-two其實(shí)是只是掃描出Bitmap文件里面有哪些文字,然后使用正則表達(dá)式來(lái)篩選出我們需要的數(shù)據(jù)。也就是說我們通過換取正則表達(dá)式就能做到掃描手機(jī)號(hào)等,帶有某種規(guī)律的數(shù)字或者字母

這是正則表達(dá)式的線上工具地址,大家可以自己試試 http://tool.oschina.net/regex/#

private static Pattern pattern = Pattern.compile("\\d{17}[\\d|x]|\\d{15}");
public static String getTelNum(String sParam){
    if(TextUtils.isEmpty(sParam)){
        return "";
    }

    Matcher matcher = pattern.matcher(sParam);
    StringBuilder bf = new StringBuilder();
    while (matcher.find()) {
        bf.append(matcher.group()).append(",");
    }
    int len = bf.length();
    if (len > 0) {
        bf.deleteCharAt(len - 1);
    }
    return bf.toString();
}

然后通過handler返回結(jié)果

    Message message = Message.obtain();
    message.what = 1;
    Bundle bundle = new Bundle();
    bundle.putString("decode",strDecode);
    message.setData(bundle);
    message.what = TwoActivity.MSG_BITMAP;
    handler.sendMessage(message);

取消加載框,并將局部截圖的圖像和掃描的結(jié)果通過DialogFragment顯示出來(lái)

                    mypDialog.dismiss();
                    String strDecode = msg.getData().getString("decode","掃描失敗");

                    if(strDecode == null ||strDecode.equals(""))
                        strDecode = "掃描失敗";

                    imageDialogFragment.setImage(bitmap);
                    imageDialogFragment.setText(strDecode);
                    imageDialogFragment.show(getFragmentManager(), "ImageDialogFragment");

5.結(jié)論

其實(shí)還沒有結(jié)束因?yàn)槲冶鞠胱龀鲆粋€(gè)能夠掃描整張×××的項(xiàng)目,我看一下網(wǎng)上有很多API都能實(shí)現(xiàn)這個(gè)功能,但都要錢,如果要是能夠?qū)崿F(xiàn)這個(gè)功能,并發(fā)到github,我豈不是成為大神了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

博客首發(fā)地址
http://blog.csdn.net/z979451341

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI