Python簡單語音識別並響應


起因是一個工作中喜歡說口頭禪的同事,昨天老說“你看看你看看 操不操心”。說了幾次之后我就在他說完“你看看”后面續上,“操不操心”。往復多次后,我就想,為啥不用Python識別語音並作出響應,正好沒弄過語音識別。

1. 語音轉文字

參考Python語音識別終極指南,吐槽一句:質量太差,是最爛的無審查的機翻。引模塊中間都沒空格importspeech_recognitionassr 應該是import speech_recognition as sr 並創建識一個別器類的例子應該是並創建一個識別器類的例子 這塊都不僅僅是機翻了吧,怎么會拆了詞。但是為了了解API足夠了。

語音轉文字使用谷歌雲平台的語音轉文字服務[Google Cloud Speech API ](https://cloud.google.com/speech-to-text/),因為是不需要API密鑰的。其實是因為有默認密鑰:

def recognize_google(self, audio_data, key=None, language="en-US", show_all=False):
	...
	if key is None: key = "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw
	...

通過另外兩個函數參數還可以了解到:lanauage (指定識別的語言),show_all(False返回識別率最高的一條結果,True返回所有識別結果的 json串 字典數據)

安裝pip install SpeechRecognition

1.1 本地語音文件識別測試

# coding:utf-8

"""
本地語音文件識別測試
"""
import speech_recognition as sr
import sys

say = '你看看'
r = sr.Recognizer()

# 本地語音測試
harvard = sr.AudioFile(sys.path[0]+'/youseesee.wav')
with harvard as source:
    # 去噪
    r.adjust_for_ambient_noise(source, duration=0.2)
    audio = r.record(source)

# 語音識別
test = r.recognize_google(audio, language="cmn-Hans-CN", show_all=True)
print(test)

# 分析語音
flag = False
for t in test['alternative']:
    print(t)
    if say in t['transcript']:
        flag = True
        break
if flag:
    print('Bingo')

自己錄了一段語音youseesee.wav (內容為輕輕(類似悄悄話,聲帶不強烈震動)說的你看看你看看,持續兩秒)。音頻文件格式可以是WAV/AIFF/FLAC

AudioFile instance given a WAV/AIFF/FLAC audio file

去噪函數adjust_for_ambient_noise()在音頻中取一段噪聲(duration時間范圍,默認1s),來優化識別。因為原音頻很短,所以這里只取了 0.2s 噪聲。

轉換函數recognize_google()lanaguage參數范圍可以從 Cloud Speech-to-Text API 語言支持 處了解到,「中文、普通話(中國簡體)」 為 cmn-Hans-CN

show_all前面有介紹,當上例中該參數為False時語音識別結果test輸出呵呵你看看,為True時輸出所有可能的識別結果:

{
    'alternative':[
        {
            'transcript':'呵呵你看看',
            'confidence':0.87500638
        },
        {
            'transcript':'呵呵你看'
        },
        {
            'transcript':'哥哥你看'
        },
        {
            'transcript':'哥哥你看看'
        },
        {
            'transcript':'呵呵你看咯'
        }
    ],
    'final':True
}

之后分析語音,只是簡單找了識別結果是否包含期待值你看看,找出一個則表示正確識別並匹配,輸出Bingo!

上例完整輸出為:

{'alternative': [{'transcript': '呵呵你看看', 'confidence': 0.87500668}, {'transcript': '呵呵你看'}, {'transcript': '哥哥你看'}, {'transcript': '哥哥你看看'}, {'transcript': '呵呵你看咯'}], 'final': True}
{'transcript': '呵呵你看看', 'confidence': 0.87500668}
Bingo

注:如果發生異常:

speech_recognition.RequestError
recognition connection failed: [WinError 10060] 由於連接方在一段時間后沒有正確答復或連接的主機沒有反應,連接嘗試失敗。

是因為(梯子)沒有設置全局代理。

1.2 實時語音識別測試

把音頻數據來源,從上面的音頻文件,改為創建一個麥克風實例,並錄音。

需要安裝pyAudio,如果pip install pyAudio不能安裝,可以去Python Extension Packages下載安裝。

# coding:utf-8

"""
實時語音識別測試
"""
import speech_recognition as sr
import logging
logging.basicConfig(level=logging.DEBUG)

while True:
    r = sr.Recognizer()
    # 麥克風
    mic = sr.Microphone()

    logging.info('錄音中...')
    with mic as source:
        r.adjust_for_ambient_noise(source)
        audio = r.listen(source)
    logging.info('錄音結束,識別中...')
    test = r.recognize_google(audio, language='cmn-Hans-CN', show_all=True)
    print(test)
    logging.info('end')

listen()函數將監聽錄音,並等到靜音時停止。

until it encounters recognizer_instance.pause_threshold seconds of non-speaking or there is no more audio input.

等到顯示錄音中,開始說話,沉默后錄音結束。試驗中說了兩次:一次是你看看你看看,二次是你再看看。結果打印如下:

INFO:root:錄音中...
INFO:root:錄音結束,識別中...
{'alternative': [{'transcript': '你看看你看看', 'confidence': 0.97500247}], 'final': True}
INFO:root:end
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
{'alternative': [{'transcript': '你再看看', 'confidence': 0.91089392}, {'transcript': '你在看看'}, {'transcript': '你猜看看'}, {'transcript': '你再敢看'}, {'transcript': '你在感慨'}], 'final': True}
INFO:root:end
INFO:root:錄音中...

識別率挺高,(還試過百度的baidu-aip,因我的音頻沒識別出來作罷),語音轉文字就完成了。

2. 文字轉語音

使用pyttsx 模塊很簡單,python3下為pyttsx3

import pyttsx3
engine = pyttsx3.init()
engine.say("風飄盪,雨濛茸,翠條柔弱花頭重")
engine.runAndWait()

如此簡單即可聽到語音朗讀了。

3. 識別並響應

將上面的組合起來即可完成識別語音並響應了。

  • 語音識別轉文字
  • 文字正則匹配並找出對應的響應文字
  • 響應(朗讀文字)
# coding:utf-8

"""
語音識別並響應。使用谷歌語音服務,不需要KEY(自帶測試KEY)。https://github.com/Uberi/speech_recognition
"""
import speech_recognition as sr
import pyttsx3
import re
import logging
logging.basicConfig(level=logging.DEBUG)

resource = {
    r"(你看看?){1}.*\1": "我不看,再讓我看打死你",
    r"(你看看?)+": "你看看你看看,操不操心",
    r"(你.+啥)+": "咋地啦",
    r"(六六六|666)+": "要不說磐石老弟六六六呢?",
    r"(磐|石|老|弟)+": "六六六",
}

engine = pyttsx3.init()

while True:
    r = sr.Recognizer()
    # 麥克風
    mic = sr.Microphone()

    logging.info('錄音中...')
    with mic as source:
        r.adjust_for_ambient_noise(source)
        audio = r.listen(source)
    logging.info('錄音結束,識別中...')
    test = r.recognize_google(audio, language='cmn-Hans-CN', show_all=True)

    # 分析語音
    logging.info('分析語音')
    if test:
        flag = False
        message = ''
        for t in test['alternative']:
            logging.debug(t)
            for r, c in resource.items():
                # 用每個識別結果來匹配資源文件key(正則),正確匹配則存儲回答並退出
                logging.info(r)
                if re.search(r, t['transcript']):
                    flag = True
                    message = c
                    break
            if flag:
                break
        # 文字轉語音
        if message:
            logging.info('bingo....')
            logging.info('say: %s' % message)
            engine.say(message)
            engine.runAndWait()
            logging.info('ok')
    logging.info('end')

對應的資源文字為

resource = {
    r"(你看看?){1}.*\1": "我不看,再讓我看打死你",
    r"(你看看?)+": "你看看你看看,操不操心",
    r"(你.+啥)+": "咋地啦",
    r"(六六六|666)+": "要不說磐石老弟六六六呢?",
    r"(磐|石|老|弟)+": "六六六",
}

這里剛好用到正則,其實剛開始沒打算用正則,想匹配兩次你看看的時候就想起回溯,就用正則了。

很方便:比如磐石老弟不好識別,就用(磐|石|老|弟)+找出一個匹配即可;你看看你看看用回溯\1。因為匹配時候發現說的快了有時匹配一個看,就用了你看看?來匹配你看,其實后面的看?要不要都可以,但為了說明目的,還是沒有去掉。

(你看看?){1}.*\1能匹配

你看看你看看
你看看你看
你看你看看看...

這樣識別率就高了。因為識別結果匹配時候從頭往后匹配每個正則,遇到則完成,所以(你看看?){1}.*\1需放在(你看看?)+前面。不然語音識別到你看看你看看就只能觸發(你看看?)+了。

運行識別結果:

語音說了六次:

你看看,你看看你看看,你瞅啥,磐石,666,哈哈哈 (文字為了說明形象化,傳輸過去只是音頻)

INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '你看看', 'confidence': 0.97500253}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:bingo....
WARNING:root:say: 你看看你看看,操不操心
INFO:root:ok
INFO:root:end
--------------------------------------------------------------------
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '你看看你看看', 'confidence': 0.97500247}
INFO:root:(你看看?){1}.*\1
INFO:root:bingo....
WARNING:root:say: 我不看,再讓我看打死你
INFO:root:ok
INFO:root:end
--------------------------------------------------------------------
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '你瞅啥', 'confidence': 0.958637}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:bingo....
WARNING:root:say: 咋地啦
INFO:root:ok
INFO:root:end
--------------------------------------------------------------------
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '磐石', 'confidence': 0.80128425}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:(六六六|666)+
INFO:root:(磐|石|老|弟)+
INFO:root:bingo....
WARNING:root:say: 六六六
INFO:root:ok
INFO:root:end
--------------------------------------------------------------------
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '666', 'confidence': 0.91621482}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:(六六六|666)+
INFO:root:bingo....
WARNING:root:say: 要不說磐石老弟六六六呢?
INFO:root:ok
INFO:root:end
--------------------------------------------------------------------
INFO:root:錄音中...
INFO:root:錄音結束,識別中...
INFO:root:分析語音
DEBUG:root:{'transcript': '哈哈哈', 'confidence': 0.97387952}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:(六六六|666)+
INFO:root:(磐|石|老|弟)+
DEBUG:root:{'transcript': '哈哈哈哈'}
INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:(六六六|666)+
INFO:root:(磐|石|老|弟)+
INFO:root:end
INFO:root:錄音中...

一共六次,前5次都可以識別並匹配到,第6次測試期待之外的,不響應。INFO為一般輸出,DEBUG輸出google服務識別到的結果(不是所有結果,第一條匹配則忽略后面識別的多條結果),WARNING輸出響應的語音(因為沒有錄在文章里聽不到,所以輸出看看說了什么)

分析第一次和最后一次:

第一次,說你看看識別出來的第一條結果是{'transcript': '你看看', 'confidence': 0.97500253},匹配第一條正則(你看看?){1}.*\1失敗,接着匹配第二條(你看看?)+成功,break正則,並break識別結果test['alternative']循環。之后語音輸出你看看你看看,操不操心

INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:bingo....
WARNING:root:say: 你看看你看看,操不操心

最后一次,說哈哈哈共識別出來兩條結果哈哈哈哈哈哈哈,

{'transcript': '哈哈哈', 'confidence': 0.97387952}
{'transcript': '哈哈哈哈'}

各自嘗試匹配所有正則均以失敗告終

INFO:root:(你看看?){1}.*\1
INFO:root:(你看看?)+
INFO:root:(你.+啥)+
INFO:root:(六六六|666)+
INFO:root:(磐|石|老|弟)+

沒有bingo只有end,然后本次識別以未響應結束。

到這里 用不到60行代碼 就實現了語音識別並響應的功能。(我不喜歡這樣說XX行代碼就實現了XXX功能,公眾號里網絡上各種關於Python文章充斥着這種標題,很令人反感。代碼短是Python那些模塊寫得好,應該感謝的是各位前輩們,而不是沾沾自喜到起噱頭標題並吸引一些浮躁的人前來。告誡自己。)

p.s. 寫代碼兩個多小時,寫文章大半天,從一團模糊的概念到語義化,也需得經過思考、組織、融合。有待改進的地方,還請多多指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM