AndroidStudio使用百度SDK完成OCR文字識別

前言

OCR 是 Optical Character Recognition 的縮寫,翻譯爲光學字符識別,指的是針對印刷體字符,採用光學的方式將紙質文檔中的文字轉換成爲黑白點陣的圖像文件,通過識別軟件將圖像中的文字轉換成文本格式,供文字處理軟件進一步編輯加工的技術(當然,這是我抄別人的原話)。

先安利一下百度智能雲,有大佬建好的輪子,咱們申請一下就可以用了,審覈很快。當然也可以使用騰訊雲和阿里雲。

接入百度SDK

申請免費試用之後(當然要申請免費的,學習成本低),下載(下載地址)解壓,會有這樣的文件夾:
在這裏插入圖片描述
百度老哥已經給我們詳細的提示了,這裏可以瞭解下各個包的用法,簡單過一下。如果不需要使用百度給的UI的話,只導入libs下的文件到項目中的lib中就可以了,其中libs中有四個文件夾和一個jar包,四個文件夾中是so文件,導入so文件看這裏。

在build.gradle中的android下面添加

sourceSets {

    main {
        jniLibs.srcDirs= ['libs']
    }

}

加上這串代碼,就可以實現將module下libs中的so一起打包到apk中。

在這裏插入圖片描述
如果你想導入UI包怎麼辦?百度的4已經告訴大家最好的方法是以模塊方式導入,這裏也介紹一下模塊方式導入。
在這裏插入圖片描述
接下來選擇對應的文件夾就行了,當然沒那麼簡單,下面還需要在主工程下引入進去。
在bulid.gradle文件的dependencies里加入
implementation project(path: ‘:ocr_ui’)
在settings.gradle下加入
include ‘:app’, ':ocr_ui’
這樣就導入模塊成功了。

代碼部分

我是採用了導入明文ak,sk的方式,UI我也導入了,但是相機那裏出了問題,還不能使用。大家可以自己寫個啓動相機獲取圖片的方法,因爲還需要用到provider的關係,我並沒有使用,而是直接從圖庫選擇圖片這種方便的形式,有興趣的童鞋可以自己補上拍照的方式。

package com.kxqin.ocrtest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.widget.Toast;
import com.baidu.ocr.sdk.OCR;
import com.baidu.ocr.sdk.OnResultListener;
import com.baidu.ocr.sdk.exception.OCRError;
import com.baidu.ocr.sdk.model.AccessToken;

public class MainActivity extends AppCompatActivity {

    private boolean hasGotToken = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initAccessTokenWithAkSk();
        findViewById(R.id.btn_simple_text).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!checkTokenStatus()) {
                    return;
                }
                Intent intent = new Intent(MainActivity.this, SimpleTextActivity.class);
                startActivity(intent);
            }
        });
    }

    private boolean checkTokenStatus() {
        if (!hasGotToken) {
            Toast.makeText(getApplicationContext(), "token未獲取到", Toast.LENGTH_SHORT).show();
        }
        return hasGotToken;
    }

    /**
     * 使用明文方式初始化token
     */
    private void initAccessTokenWithAkSk() {
        OCR.getInstance(this).initAccessTokenWithAkSk(new OnResultListener<AccessToken>() {
            @Override
            public void onResult(AccessToken accessToken) {
                String token = accessToken.getAccessToken();
                hasGotToken = true;
            }

            @Override
            public void onError(OCRError error) {
                error.printStackTrace();
                Looper.prepare();
                Toast.makeText(MainActivity.this, "token獲取失敗.........", Toast.LENGTH_SHORT).show();
            }
        }, this, "此處應該填你的AK", "你的SK");
    }
}

主頁面現在只寫了一個按鈕,普通文本識別按鈕,因爲初步只完成了這個,後續有空我會把其他的識別都加上,畢竟百度送的免費的30天不用可就浪費了。initAccessTokenWithAkSk方法進行token通行證的確認ak和sk大家可以在百度控制檯那裏獲取,和使用其他sdk一樣都會創建應用,獲取明文密鑰的。

package com.kxqin.ocrtest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import com.baidu.ocr.sdk.OCR;
import com.baidu.ocr.sdk.OnResultListener;
import com.baidu.ocr.sdk.exception.OCRError;
import com.baidu.ocr.sdk.model.GeneralBasicParams;
import com.baidu.ocr.sdk.model.GeneralResult;
import com.baidu.ocr.sdk.model.WordSimple;
import com.baidu.ocr.ui.camera.CameraActivity;
import com.baidu.ocr.ui.camera.CameraNativeHelper;
import com.baidu.ocr.ui.camera.CameraView;

import java.io.File;
import java.util.List;

public class SimpleTextActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_CAMERA = 102;
    private int REQUEST_CODE_ALBUM = 2;
    private int REQUEST_ENHANCED_CODE_ALBUM = 3;

    private TextView infoTextView;
    private AlertDialog.Builder alertDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_simple_text);
        alertDialog = new AlertDialog.Builder(this);
        infoTextView = findViewById(R.id.info_text_view);
        CameraNativeHelper.init(this, OCR.getInstance(this).getLicense(),
                new CameraNativeHelper.CameraNativeInitCallback() {
                    @Override
                    public void onError(int errorCode, Throwable e) {
                        String msg;
                        switch (errorCode) {
                            case CameraView.NATIVE_SOLOAD_FAIL:
                                msg = "加載so失敗,請確保apk中存在ui部分的so";
                                break;
                            case CameraView.NATIVE_AUTH_FAIL:
                                msg = "授權本地質量控制token獲取失敗";
                                break;
                            case CameraView.NATIVE_INIT_FAIL:
                                msg = "本地質量控制";
                                break;
                            default:
                                msg = String.valueOf(errorCode);
                        }
                        infoTextView.setText("本地質量控制初始化錯誤,錯誤原因: " + msg);
                    }
                });
        findViewById(R.id.take_a_photo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (checkGalleryPermission()) {
                    Intent intent = new Intent(SimpleTextActivity.this, CameraActivity.class);
                    intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH, FileUtil.getSaveFile(getApplication()).getAbsoluteFile());
                    intent.putExtra(CameraActivity.KEY_CONTENT_TYPE, CameraActivity.CONTENT_TYPE_GENERAL);
                    startActivityForResult(intent, REQUEST_CODE_CAMERA);
                }
            }
        });
        findViewById(R.id.select_a_photo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(Intent.ACTION_PICK, null);
                intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                startActivityForResult(intent, REQUEST_CODE_ALBUM);
            }
        });
        findViewById(R.id.enhanced_photo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(Intent.ACTION_PICK, null);
                intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                startActivityForResult(intent, REQUEST_ENHANCED_CODE_ALBUM);
            }
        });

    }

    /**
     * 文字普通識別
     * @param filePath
     */
    private void recSimpleText(String filePath) {
        // 通用文字識別參數設置
        GeneralBasicParams param = new GeneralBasicParams();
        param.setDetectDirection(true);
        param.setImageFile(new File(filePath));

        // 調用通用文字識別服務
        OCR.getInstance(this).recognizeGeneralBasic(param, new OnResultListener<GeneralResult>() {
            @Override
            public void onResult(GeneralResult generalResult) {
                if (generalResult != null) {
                    //alertText("", parseResultBean(generalResult));
                    setInfoTextView(parseResultBean(generalResult));
                }
            }

            @Override public void onError(OCRError error) {
                // 調用失敗,返回OCRError對象
                alertText("調用失敗", error.getMessage());
                } });
    }

    /**
     * 文字高精度識別
     * @param filePath
     */
    private void recEnhancedText(String filePath) {
        GeneralBasicParams params = new GeneralBasicParams();
        params.setDetectDirection(true);
        params.setImageFile(new File(filePath));

        OCR.getInstance(this).recognizeAccurateBasic(params, new OnResultListener<GeneralResult>() {
            @Override
            public void onResult(GeneralResult generalResult) {
                if (generalResult != null) {
                    //alertText("", parseResultBean(generalResult));
                    setInfoTextView(parseResultBean(generalResult));
                }
            }

            @Override
            public void onError(OCRError ocrError) {
                // 調用失敗,返回OCRError對象
                alertText("調用失敗", ocrError.getMessage());
            }
        });
    }

    /**
     * 解析返回結果
     * @param generalResult
     * @return
     */
    private String parseResultBean(GeneralResult generalResult) {
        StringBuilder stringBuilder = new StringBuilder();
        List<?> list = generalResult.getWordList();
        for (Object o : list) {
            if (o instanceof WordSimple) {
                stringBuilder.append(((WordSimple) o).getWords());
                stringBuilder.append("\n\n");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 檢查是否有權限
     * @return
     */
    private boolean checkGalleryPermission() {
        int ret = ActivityCompat.checkSelfPermission(SimpleTextActivity.this, Manifest.permission
                .READ_EXTERNAL_STORAGE);
        if (ret != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(SimpleTextActivity.this,
                    new String[] {Manifest.permission.READ_EXTERNAL_STORAGE},
                    1000);
            return false;
        }
        return true;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) {
            if (data != null) {
                String contentType = data.getStringExtra(CameraActivity.KEY_CONTENT_TYPE);
                String filePath = FileUtil.getSaveFile(getApplicationContext()).getAbsolutePath();
                if (!TextUtils.isEmpty(contentType)) {
                    if (CameraActivity.CONTENT_TYPE_GENERAL.equals(contentType)) {
                        recSimpleText(filePath);
                    }
                }
            }
        }else if (requestCode == REQUEST_CODE_ALBUM) {
            // 從相冊返回的數據
            if (data != null) {
                // 得到圖片的全路徑
                Uri uri = data.getData();
                recSimpleText(getRealPathFromURI(uri));
            }
        }
        else if (requestCode == REQUEST_ENHANCED_CODE_ALBUM) {
            // 從相冊返回的數據
            if (data != null) {
                // 得到圖片的全路徑
                Uri uri = data.getData();
                recEnhancedText(getRealPathFromURI(uri));
            }
        }
    }

    /**
     * 錯誤彈窗
     * @param title
     * @param message
     */
    private void alertText(final String title, final String message) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                alertDialog.setTitle(title)
                        .setMessage(message)
                        .setPositiveButton("確定", null)
                        .show();
            }
        });
    }

    /**
     * 設置顯示結果
     * @param text
     */
    private void setInfoTextView(final String text) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                infoTextView.setText(text);
            }
        });
    }

    /**
     * 將URI轉化爲string格式
     * @param contentURI
     * @return
     */
    private String getRealPathFromURI(Uri contentURI) {
        String result;
        Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
        if (cursor == null) {
            // Source is Dropbox or other similar local file path
            result = contentURI.getPath();
        } else {
            cursor.moveToFirst();
            int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            result = cursor.getString(idx);
            cursor.close();
        }
        return result;
    }

    @Override
    protected void onDestroy() {
        // 釋放本地質量控制模型
        CameraNativeHelper.release();
        super.onDestroy();
    }
}

這裏面我也是仿照Demo寫的,但是不一樣的是我只是做了簡單的文字識別,而且相機拍照emmm還是壞的。原因也是我瞭解了一下才知道拍照所需要的東西並不止這些,需要另外準備,爲了先簡單實現識別,這個拍照之後再做吧。方法的註釋大概解釋了每個方法的作用,其實並沒有什麼好說的,因爲這都是根據百度API介紹寫的最簡單的,簡單來說就是打開相冊-獲取圖片地址-傳到百度服務器-返回結果類,然後自己解析結果類裏面的list即可。如果沒有使用到UI的話,那也用不到CameraActivity之類的。目前我也沒有使用。

結果

先來一個不是很清楚的渣手機拍照
在這裏插入圖片描述
在這裏插入圖片描述

昨晚的五殺拍照!你看文字還是識別的很準確的,但是帶了符號之類的會影響識別。
百度sdk也說了 最好控制圖片大小,因爲就算太大的圖片上傳也會被壓縮,影響質量。同時一個頁面中的字數建議小於70個字,字數過多過密也會影響識別結果,當然這是高精度下的識別結果,你問我普通識別呢?這。。。普通識別下建議只用於截屏或者文檔識別吧,要不然識別的內容慘不忍睹。

再來看看一個截屏下的識別結果:
在這裏插入圖片描述
在這裏插入圖片描述

我這算不算受到騷擾呢?報警可以嗎?退訂還沒用呢0_0

自己完成了之後也不知道該怎麼向別人講述,如果代碼片段中有不清楚的地方可以單獨問我吼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章