百度文字識(shí)別(OCR)接口使用

動(dòng)機(jī)&成品展示

最近蚯舱,家里老人有需要把圖片里的文字識(shí)別讀取的功能英支,想要教他們用市面上功能齊全的 App佩憾,因?yàn)楣δ芡?fù)雜的,他們又不太會(huì)操作潭辈,就有了寫一個(gè)簡(jiǎn)單的應(yīng)用的想法鸯屿。
到網(wǎng)上參考了下澈吨,看到有人用 Tesseract 識(shí)別引擎,自己試了下寄摆,發(fā)現(xiàn) Tesseract 識(shí)別準(zhǔn)確率不太高谅辣,不太好用。而百度提供的這個(gè) OCR 接口挺不錯(cuò)的婶恼,雖然免費(fèi)版本有使用次數(shù)限制桑阶,但因?yàn)楸旧硇枨蟛惶螅R(shí)別挺準(zhǔn)確勾邦,就打算拿來用用蚣录。

成品差不多這樣,由于是初版眷篇,之后還會(huì)修一修萎河,先趁著記憶還是熱的分享一下自己開發(fā)過程。


SDK介紹

OCR(Optical Character Recognition蕉饼,光學(xué)字符識(shí)別)虐杯,顧名思義…… 好了,知道來到這里的小伙伴應(yīng)該不是想來這里看這些的了昧港,我就移到最后再提吧擎椰。。创肥。


基本環(huán)境準(zhǔn)備

提醒:這里采用百度云提供的 通用文字識(shí)別 作為范例达舒,如果想換為 含位置版高精度版 等等,可以自行修改參數(shù)叹侄。另外巩搏,我看到有人是利用請(qǐng)求API來完成的,不過我采用的是調(diào)用 Android SDK

為了完成這個(gè)接口的使用圈膏,可以參考下面三個(gè)資源:

  • 以下本文的介紹
  • 百度AI開放平臺(tái)的OCR開發(fā)者文檔
  • 參考下載SDK提供的DEMO

給幾個(gè)官方網(wǎng)站:

我的Android Studio Project配置:

  • Android Studio 版本:3.5.0
  • Gradle 版本:5.4.1
  • Minimum SDK 版本:21 (5.0 Lollipop)

那么塔猾,就開始吧~


創(chuàng)建新應(yīng)用

首先要建立一個(gè) Android Studio 新 Project

Image.png

接著要在百度云控制臺(tái)創(chuàng)建新應(yīng)用接口:
先給應(yīng)用起個(gè)響亮的名字(并沒有

記得一定要設(shè)定文字識(shí)別包名為需要,同時(shí)包名為 Android Studio 創(chuàng)建 Project 的包的名字(Package name)

創(chuàng)建完后下載SDK

另外稽坤,為了安全考慮丈甸,建議采用安全模式設(shè)置。此種身份驗(yàn)證方案使用授權(quán)文件獲得AccessToken尿褪,緩存在本地睦擂。

由于在您的 App 分發(fā)出去之后,存在被反編譯的可能杖玲,所以直接將 Api Key(簡(jiǎn)稱AK)和 Secret Key(簡(jiǎn)稱SK)置于APP源碼之中顿仇,存在被盜取的風(fēng)險(xiǎn)。采用授權(quán)文件的身份驗(yàn)證方法,可有效保護(hù) AK/SK 在移動(dòng)設(shè)備中的安全臼闻。攻擊者即使攔截了流量鸿吆,盜取了授權(quán)文件,也難以盜用您的配額述呐。(引用自官方說明文檔)

所以也要在控制臺(tái)下載 License 文件


文件惩淳、環(huán)境配置

在下載完相關(guān)文件后:
將下載的 aip-ocr-android-sdk-1.4.4 文件夾 libs 子文件夾里 arm64-v8a, armeabi, armeabi-v7a, x86 放到如下圖 main 里新創(chuàng)建的 jniLibs 文件夾中,ocr-sdk.jar 放入 libs
將下載的 aip.license 證書放到如下圖 main 里的 assets 子文件夾中

如果有需要引用百度已經(jīng)完成的圖片獲取功能乓搬,可以在 Project 中引進(jìn):
提供功能:

  • 相機(jī)拍照(可調(diào)閃光燈)
  • 手機(jī)圖庫(kù)導(dǎo)入
  • 導(dǎo)入圖片截圖(可旋轉(zhuǎn))

需要文件:

  • aip-ocr-android-sdk-1.4.4 文件夾里的 ocr_ui 文件夾

步驟


代碼完成

在完成上面的動(dòng)作后思犁,先試著 Build 一下 Project,看看有沒有遇到下面的問題进肯,或者有關(guān) android.support 的問題

經(jīng)過一番搜尋才發(fā)現(xiàn)激蹲,在更新過的 Android Studio 版本,android.support.xxx 包經(jīng)過整理后統(tǒng)一成 AndroidX江掩,而百度提供的獲取圖片模組還有使用 android.support学辱,現(xiàn)在想用需要修改取代 android.support.xxx 為 androidx.xxx。
有需要修改的 java 文件大概有這些(這是我遇到的如圖標(biāo)紅的文件)

修改如圖:

動(dòng)作 修改1 修改2
修改前
修改后

(其實(shí)沒用到的包就刪掉更方便哈~)
有關(guān)AndroidX可以看看這篇文章或直接看官方文檔:http://www.reibang.com/p/41de8689615d

那么下面就是真正完成 App 的主要代碼了~

先添加幾個(gè)使用者權(quán)限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

再在 app 的 build.gradle 添加依賴:

implementation files("libs/ocr-sdk.jar")

再完成 MainActivity 代碼:
onCreate方法內(nèi)新建指令

alertDialog = new AlertDialog.Builder(this);

//初始化token license
initAccessTokenLicenseFile();

/**
* 打開相機(jī)頁(yè)面利用 百度OCR 提供之范例
*/
// 通用文字識(shí)別
Button basicButton = (Button) findViewById(R.id.imgGet);
basicButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (!checkTokenStatus()) {
            return;
        }
        Intent intent = new Intent(MainActivity.this, CameraActivity.class);
        intent.putExtra(CameraActivity.KEY_OUTPUT_FILE_PATH,
                FileUtil.getSaveFile(getApplication()).getAbsolutePath());
        intent.putExtra(CameraActivity.KEY_CONTENT_TYPE,
                CameraActivity.CONTENT_TYPE_GENERAL);
        startActivityForResult(intent, REQUEST_CODE_GENERAL_BASIC);
    }
});

onCreate外指令

/**
* 回傳代碼集
*/
private static final int REQUEST_CODE_GENERAL = 105;
private static final int REQUEST_CODE_GENERAL_BASIC = 106; //這個(gè)代碼是我這次用到的 通用文字識(shí)別
private static final int REQUEST_CODE_ACCURATE_BASIC = 107;
private static final int REQUEST_CODE_ACCURATE = 108;
private static final int REQUEST_CODE_GENERAL_ENHANCED = 109;
private static final int REQUEST_CODE_GENERAL_WEBIMAGE = 110;
private static final int REQUEST_CODE_BANKCARD = 111;
private static final int REQUEST_CODE_VEHICLE_LICENSE = 120;
private static final int REQUEST_CODE_DRIVING_LICENSE = 121;
private static final int REQUEST_CODE_LICENSE_PLATE = 122;
private static final int REQUEST_CODE_BUSINESS_LICENSE = 123;
private static final int REQUEST_CODE_RECEIPT = 124;
private static final int REQUEST_CODE_PASSPORT = 125;
private static final int REQUEST_CODE_NUMBERS = 126;
private static final int REQUEST_CODE_QRCODE = 127;
private static final int REQUEST_CODE_BUSINESSCARD = 128;
private static final int REQUEST_CODE_HANDWRITING = 129;
private static final int REQUEST_CODE_LOTTERY = 130;
private static final int REQUEST_CODE_VATINVOICE = 131;
private static final int REQUEST_CODE_CUSTOM = 132;
/**
* 提前宣告幾個(gè)變量
*/
private boolean hasGotToken = false;
private AlertDialog.Builder alertDialog;

我自己第一遍完成 App 后下面一直報(bào)錯(cuò)频敛,檢查好久才發(fā)現(xiàn)我的證書忘記放到 assets项郊,當(dāng)時(shí)發(fā)現(xiàn)叫一個(gè)崩潰

/**
* 以license文件自定位置方式初始化token
*/
private void initAccessTokenLicenseFile() {
    OCR.getInstance(this).initAccessToken(new OnResultListener<AccessToken>() {
        @Override
        public void onResult(AccessToken accessToken) {
            String token = accessToken.getAccessToken();
            hasGotToken = true;
        }


        @Override
        public void onError(OCRError error) {
            error.printStackTrace();
            alertText("自定義文件路徑licence方式獲取token失敗", error.getMessage());
        }
    }, "aip.license", getApplicationContext());
}
/**
* 警示訊息,在初始化失敗時(shí)呼叫
*/
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();
        }
    });
}
/**
* 檢查token狀態(tài)
*/
private boolean checkTokenStatus() {
    if (!hasGotToken) {
        Toast.makeText(getApplicationContext(), "token還未成功獲取", Toast.LENGTH_LONG).show();
    }
    return hasGotToken;
}
/**
* 讀取文件臨時(shí)儲(chǔ)存
*/
public static class FileUtil {
    public static File getSaveFile(Context context) {
        File file = new File(context.getFilesDir(), "pic.jpg");
        return file;
    }
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // 識(shí)別成功回調(diào)斟赚,通用文字識(shí)別
    if (requestCode == REQUEST_CODE_GENERAL_BASIC && resultCode == Activity.RESULT_OK) {
        RecognizeService.recGeneralBasic(this, FileUtil.getSaveFile(getApplicationContext()).getAbsolutePath(),
                new RecognizeService.ServiceListener() {
                    @Override
                    public void onResult(String result) {
                        infoPopText(result);
                    }
                });
    }
}
/**
* 識(shí)別結(jié)果顯示
*/
private void infoPopText(final String result) {

    //顯示結(jié)果以 JSON 格式呈現(xiàn)
    TextView basicResult = (TextView) findViewById(R.id.resultShow);
    basicResult.setText(result);
}
/**
* 識(shí)別功能類
* 功能:將拍攝或者圖庫(kù)中獲得的圖片進(jìn)行識(shí)別,返回JSON格式的字符串
* 建議放到新的.java文件里
*/
public static class RecognizeService {

    interface ServiceListener {
        public void onResult(String result);
    }

    //通用識(shí)別
    public static void recGeneralBasic(Context ctx, String filePath, final ServiceListener listener) {
        GeneralBasicParams param = new GeneralBasicParams();
        param.setDetectDirection(true);
        param.setImageFile(new File(filePath));

        //百度核心 OCR 功能
        OCR.getInstance(ctx).recognizeGeneralBasic(param, new OnResultListener<GeneralResult>() {
            @Override
            public void onResult(GeneralResult result) {
                StringBuilder sb = new StringBuilder();
                for (WordSimple wordSimple : result.getWordList()) {
                    WordSimple word = wordSimple;
                    sb.append(word.getWords());
                    sb.append("\n");
                }
                listener.onResult(result.getJsonRes());
            }

            @Override
            public void onError(OCRError error) {
                listener.onResult(error.getMessage());
            }
        });
    }
}

XML 頁(yè)面長(zhǎng)這樣


SDK介紹

OCR(Optical Character Recognition差油,光學(xué)字符識(shí)別)拗军,顧名思義,就是針對(duì)印刷體字符蓄喇,采用光學(xué)的方式將紙質(zhì)文檔中的文字轉(zhuǎn)換成為黑白點(diǎn)陣的圖像文件发侵,并通過識(shí)別軟件將圖像中的文字轉(zhuǎn)換成文本格式,供文字處理軟件進(jìn)一步編輯加工的技術(shù)妆偏。
詳見度娘: https://baike.baidu.com/item/%E5%85%89%E5%AD%A6%E5%AD%97%E7%AC%A6%E8%AF%86%E5%88%AB/4162921?fromtitle=OCR&fromid=25995&fr=aladdin

百度AI開放平臺(tái)的文字識(shí)別:提供多種場(chǎng)景下精準(zhǔn)的圖像文字識(shí)別技術(shù)服務(wù)刃鳄,讓您的應(yīng)用看圖識(shí)字,提升輸入效率钱骂,優(yōu)化用戶體驗(yàn)叔锐。提供如下圖各種識(shí)別工具接口

詳見官方網(wǎng)站: https://cloud.baidu.com/product/ocr/


????完????結(jié)????撒????花????

~第一次寫 blog 還有點(diǎn)小激動(dòng)呢~
~想必錯(cuò)誤、表述不準(zhǔn)確在所難免见秽,還請(qǐng)各位大神及時(shí)指出~
~有什么想法可以留言哦愉烙,我會(huì)盡量回答的~
~近期還未打算把源代碼發(fā)布,若有需求解取,我再放到Github上~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末步责,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔓肯,老刑警劉巖遂鹊,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蔗包,居然都是意外死亡稿辙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門气忠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來邻储,“玉大人,你說我怎么就攤上這事。” “怎么了峡懈?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵牛郑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我胸嘴,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任勾扭,我火速辦了婚禮,結(jié)果婚禮上铁瞒,老公的妹妹穿的比我還像新娘妙色。我一直安慰自己,他們只是感情好慧耍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布身辨。 她就那樣靜靜地躺著,像睡著了一般芍碧。 火紅的嫁衣襯著肌膚如雪煌珊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天泌豆,我揣著相機(jī)與錄音定庵,去河邊找鬼。 笑死踪危,一個(gè)胖子當(dāng)著我的面吹牛蔬浙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陨倡,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼敛滋,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了兴革?” 一聲冷哼從身側(cè)響起绎晃,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蜜唾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后庶艾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袁余,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年咱揍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颖榜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煤裙,死狀恐怖掩完,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情硼砰,我是刑警寧澤且蓬,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站题翰,受9級(jí)特大地震影響恶阴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜豹障,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一冯事、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧血公,春花似錦昵仅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至薛夜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間版述,已是汗流浹背梯澜。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渴析,地道東北人晚伙。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俭茧,于是被迫代替她去往敵國(guó)和親咆疗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容