版權(quán)聲明:本文為elecdog原創(chuàng)文章,可以轉(zhuǎn)載,但必須在明確位置注明出處!謝謝合作。

關(guān)于語音識別,國內(nèi)已經(jīng)有比較好的公司推出相關(guān)的服務(wù)了,比如百度免費的離在線融合語音識別以及訊飛收費的在線和離線語音識別服務(wù)。這里不作過多介紹,需要的同學(xué)可以直接去官網(wǎng)閱讀接入文檔。這里要介紹的是一個離線語音識別的開源項目——CMU PocketSpinnx,在安卓開發(fā)中的使用。在智能家居 APP 開發(fā)中常需要在沒法聯(lián)網(wǎng)的設(shè)備識別一些比較簡單的命令詞,百度的離在線融合語音識別識別率還不錯,不過在設(shè)備連接局域網(wǎng)的情況下仍然優(yōu)先使用在線識別,導(dǎo)致識別時間太長或者轉(zhuǎn)換不過來,訊飛離線語音識別沒有使用過,因為是收費的,而且對于個人開發(fā)者價格也不便宜,相比較之下,PocketSpinnx則是完全離線的語音識別,只要按照要求部署項目,識別率也差強(qiáng)人意。

首先我們可以通過 PocketSpinnx 官網(wǎng)的介紹來了解該離線語音識別項目的工作原理,能夠加深對項目使用的理解。下面我們一步一步來把PocketSpinnx的離線語音識別功能引入到我們自己的項目中來。

獲取語言模型

首先我們需要把想要識別的命令詞編寫成命令集,打開 Sublime Text 新建 txt 文件,編碼采用 utf-8,每一行寫一個命令詞,如圖所示:

然后訪問網(wǎng)址 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 生成語言模型(國內(nèi)訪問不穩(wěn)定,需自備梯子),點擊選擇文件,選擇剛才編寫的命令集文件 command.txt,然后點擊COMPILE KNOWLEDGE BASE按鈕就可以生成語言模型,如圖:

這里生成了好幾個文件,我們可以把整個 .tgz 文件下載下來解壓縮,其中得到的 .lm 文件就是我們需要的語言模型。

獲取字典模型

字典模型的作用,就是告訴語音識別器中文的發(fā)音,這樣他才能認(rèn)得中文,字典模型很簡單,首先到 PocketSpinnx 的資源網(wǎng)盤,進(jìn)入 Mandarin 文件夾,下載一個后綴為 .dic 的文件,里面涵蓋了很多普通話的發(fā)音,查找到我們的命令詞,找不到完整命令詞的也可以找單個字的發(fā)音,然后參考這個 .dic 文件的格式,在上一步獲取到的 .lm 語言模型文件中還有一個 .dic 文件,補(bǔ)充完整這個 .dic 文件的發(fā)音,如圖所示:

這樣字典模型就算是完成了。

獲取聲學(xué)模型

同樣是在資源網(wǎng)盤的 Mandarin 文件夾下,下載 .tar.bz2 的壓縮文件解壓后,得到如下聲學(xué)模型文件:

在項目中導(dǎo)入接口

在以上必要文件都準(zhǔn)備好之后,我們可以在 PocketSpinnx 開源的安卓 demo中直觀地了解具體的用法,項目結(jié)構(gòu)如圖所示:

我們可以把 demo 中的 aars 和 models 導(dǎo)入到我們自己的項目中,快速集成相關(guān)接口。demo 中 en-us-ptm 中的是英文的聲學(xué)模型,為了能夠識別中文,我們可以依樣畫葫蘆新建一個 ptm-zh 文件夾,放入我們前面獲取的普通話聲學(xué)模型。同時,還需要把我們的語言模型和地點模型放進(jìn)來,準(zhǔn)備工作算是完成了。

獲取識別器

現(xiàn)在我們已經(jīng)可以在項目中調(diào)用相關(guān)的 API 了,首先需要獲取最重要的語音識別器類SpeechRecognizer,如demo中的代碼:

    private void runRecognizerSetup() {        // Recognizer initialization is a time-consuming and it involves IO,
        // so we execute it in async task
        new AsyncTask<Void, Void, Exception>() {            @Override
            protected Exception doInBackground(Void... params) {                try {
                    Assets assets = new Assets(PocketSphinxActivity.this);
                    File assetDir = assets.syncAssets();                    setupRecognizer(assetDir);
                } catch (IOException e) {                    return e;
                }                return null;
            }            @Override
            protected void onPostExecute(Exception result) {                if (result != null) {
                    ((TextView) findViewById(R.id.caption_text))
                            .setText("Failed to init recognizer " + result);
                } else {                    switchSearch(KWS_SEARCH);
                }
            }
        }.execute();
    }    private void setupRecognizer(File assetsDir) throws IOException {        // The recognizer can be configured to perform multiple searches
        // of different kind and switch between them

        recognizer = SpeechRecognizerSetup.defaultSetup()
                .setAcousticModel(new File(assetsDir, "en-us-ptm"))//設(shè)置聲學(xué)模型的文件夾
                .setDictionary(new File(assetsDir, "cmudict-en-us.dict"))//設(shè)置字典模型
                .setRawLogDir(assetsDir) // To disable logging of raw audio comment out this call (takes a lot of space on the device)

                .getRecognizer();
        recognizer.addListener(this);        /** In your application you might not need to add all those searches.         * They are added here for demonstration. You can leave just one.         */

        // 創(chuàng)建短語監(jiān)聽
        recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);        //創(chuàng)建命令文件監(jiān)聽
        File menuGrammar = new File(assetsDir, "menu.gram");
        recognizer.addGrammarSearch(MENU_SEARCH, menuGrammar);        // Create grammar-based search for digit recognition
        File digitsGrammar = new File(assetsDir, "digits.gram");
        recognizer.addGrammarSearch(DIGITS_SEARCH, digitsGrammar);        // Create language model search
        File languageModel = new File(assetsDir, "weather.dmp");
        recognizer.addNgramSearch(FORECAST_SEARCH, languageModel);        // Phonetic search
        File phoneticModel = new File(assetsDir, "en-phone.dmp");
        recognizer.addAllphoneSearch(PHONE_SEARCH, phoneticModel);
    }    private void switchSearch(String searchName) {
        recognizer.stop();        // If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
        if (searchName.equals(KWS_SEARCH))
            recognizer.startListening(searchName);        else
            recognizer.startListening(searchName, 10000);
    }

這里需要注意設(shè)置聲學(xué)模型文件夾的時候不需要寫成 sync/ptm-zh,sync不需要寫,否則會報錯找不到文件。到這里按照 demo 的示例代碼基本可以學(xué)會重要的方法調(diào)用了,如開始監(jiān)聽和結(jié)束監(jiān)聽等。

這里再提一下我們創(chuàng)建命令文件監(jiān)聽的時候需要使用的 .gram 文件,其實看一下 demo 中的 .gram 文件我們也知道該如何編寫自己的命令文件了

#JSGF V1.0;grammar menu;public <item> = 命令詞1 | 命令詞2 | 命令詞3;

一旦開始命令文件監(jiān)聽,則監(jiān)聽器就會監(jiān)聽命令文件中的命令詞,當(dāng)監(jiān)聽到語音的時候就會取出發(fā)音最相似的那個命令詞返回到監(jiān)聽結(jié)果。請注意,這里意思是取出發(fā)音最相近的,這導(dǎo)致的時候也許你并沒有說這些命令詞的任何一個,只是監(jiān)聽器同樣會取出他認(rèn)為最相近的一個返回給結(jié)果,也就是表現(xiàn)的識別過于敏感,我在使用過程中還是屬于可接受范圍內(nèi)。可以根據(jù)自己需求選擇短語監(jiān)聽或者命令文件監(jiān)聽。

結(jié)束

到這里基本就可以使用 PocketSpinnx 離線語音識別了,一些細(xì)節(jié)的處理還需要自己閱讀 demo 中的代碼,代碼不多而且容易理解,可以加深對使用的理解,另外,推薦閱讀官方文檔,能夠詳細(xì)知道項目的運行原理,以及文中沒有提到的一些內(nèi)容,雖然是英文的,但通過單詞翻譯也不難理解。

原文鏈接:http://electhuang.com/2017/04/27/android-pocketSphinx/

http://www.cnblogs.com/elecdog/p/7235235.html