【ひらけごま】STFT,VGG16-like,そしてPyaudioで「エッジ音声認識アプリ」を作ってみた♬垂れ流し改善編

昨夜のアプリ、どんどん垂れ流しアプリなので、今後の利用のために少し改善して待てるようにしました。
つまり、ファイルの格納ディレクトリから一度だけ読み込むように、
①ファイルの存在チェックを実施
②読込んだらファイル消去
というのを追加して、新規に声を取得したら反応するように改善しました。
合わせて流れを変更して読込初期の揺らぎを改善しました。
###やったこと
・ファイル存在チェックの追加
・読み込んだらファイル消去
・コード解説
###・ファイル存在チェックの追加
【参考】
・[pythonでファイルの存在を確認する](https://qiita.com/tortuepin/items/4a0669d8f275e966229e)
参考のとおりに以下で実施できます。

```py
import os
path = "./out_test/figure.jpg"
os.path.exists(path)
```
これで./out_test/にfigure.jpgが存在するかどうかの判定ができます。
Trueで存在、Falseで存在しない。
ということで、存在を確認してから読み込むというシーケンスが作成できます。
###・読み込んだらファイル消去
上記と対の機能がファイルを読み込んだら、二度目は読み込まないように消去しておきたいところです。
ということで、以下の参考が役に立ちます。
【参考】
・[ファイルやフォルダーを削除する](http://gesource.jp/programming/python/code/0025.html)
つまり、参考のとおり、以下で実施できました。

```py
import os
path = "./out_test/figure.jpg"
os.remove(path)
```
ちなみに、参考ではディレクトリの削除やディレクトリツリーごと削除も掲載されています。
###・コード解説
上記二つの機能を盛り込んで以下のように変更しました。
学習などは変わらないので[昨夜の記事を見てください](https://qiita.com/MuAuan/items/633060820bd3c9acd441)。
import osを追記しています。
昨夜のコード
[hirakegoma / out_onsei.py ](https://github.com/MuAuan/hirakegoma/blob/master/out_onsei.py)
[hirakegoma / pyaudio_realtime_last.py ](https://github.com/MuAuan/hirakegoma/blob/master/pyaudio_realtime_last.py)

```out_onsei_improve.py
#-*- cording: utf-8 -*-
import numpy as np
#np.random.seed(1337) # for reproducibility
import wave
import pyaudio
from vgg16_like import model_family_cnn
from keras.preprocessing import image
import matplotlib.pyplot as plt
import keras
from keras.preprocessing import image
import os
```
predictionに変更はありません。

```out_onsei_improve.py
def prediction(imgSrc):
    img = np.array(imgSrc)
    img = img.reshape(1, img_rows,img_cols,3)
    img = img.astype('float32')
    img /= 255
    t0=time.time()
    y_pred = model.predict(img)
    return y_pred
```
while前に
`path = "./out_test/figure.jpg"`
を定義しました。

```out_onsei_improve.py
num_classes = 2
img_rows,img_cols=128, 128
input_shape = (img_rows,img_cols,3)   #224, 224, 3)
model = model_family_cnn(input_shape, num_classes = num_classes)
# load the weights from the last epoch
model.load_weights('params_hirakegoma-61.hdf5', by_name=True)
print('Model loaded.')
path = "./out_test/figure.jpg"
```
while True:の中で最初に
`if os.path.exists(path)==True:`
として、存在すれば以下を実行するように変更しました。
また、音声を新たに入力してpathに格納するとき、タイミングを計るために
`for i in range(100000): i += 1`
を入れました。
また、最後のところでファイルの削除を実施しています。

```py
if input==b'':
    os.remove(path)
    break
```
それ以外は変更していません。

```out_onsei_improve.py
while True:
    imgSrc=[]
    if os.path.exists(path)==True:
        for i in range(100000):
            i += 1
        
        imgSrc = image.load_img("./out_test/figure.jpg", target_size=(img_rows,img_cols))
        plt.imshow(imgSrc)
        plt.pause(1)
        plt.close()
        
        pred = prediction(imgSrc)
        print(pred[0])

        if pred[0][0]>=0.5:
            filename = "ohayo.wav"
            print("ohayo")
        else:
            filename = "hirakegoma.wav"
            print("hirakegoma")    
        # チャンク数を指定
        CHUNK = 1024
        #filename = "hirakegoma.wav"
        wf = wave.open(filename, "rb")
        # PyAudioのインスタンスを生成
        p = pyaudio.PyAudio()
        # Streamを生成
        stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)
        # データを1度に1024個読み取る
        input = wf.readframes(CHUNK)
        # 実行
        while stream.is_active():    
            output = stream.write(input)
            input = wf.readframes(CHUNK)
            #print(input)
            if input==b'':
                os.remove(path)
                break
        
# ファイルが終わったら終了処理
stream.stop_stream()
stream.close()
p.terminate()
```
また、音声録音側のアプリは以下のように変更しました。
実は、前回のコードだと測定開始時にミスって、変なデータを格納してから正しいデータを格納していました。
そこで、start_measure():の変数にp1のように1をつけました。
※これ最終的には不要でした
ということで、基本的に最初の部分は変更なしです。

```pyaudio_realtime.py
# -*- coding:utf-8 -*-
import pyaudio
import time
import matplotlib.pyplot as plt
import numpy as np
import wave
from scipy.fftpack import fft, ifft
from scipy import signal
import os

def start_measure():
    CHUNK=1024
    RATE=44100 #11025 #22050  #44100
    p1=pyaudio.PyAudio()
    input1 = []
    stream1=p1.open(format = pyaudio.paInt16,
                  channels = 1,
                  rate = RATE,
                  frames_per_buffer = CHUNK,
                  input = True) 
    input1 =stream1.read(CHUNK)
    sig1 = np.frombuffer(input1, dtype="int16")/32768.0
    while True:
        if max(sig1) > 0.001:
            break
        input1 =stream1.read(CHUNK)
        sig1 = np.frombuffer(input1, dtype="int16")/32768.0
    stream1.stop_stream()
    stream1.close()
    p1.terminate()
    return
```
以下のコードも基本はあまり変更なく、
以下の設定を毎回初期化するように`for s in range(0,900,1):`
の中に入れました。これで最初に変な録音が入るバグが解消しました。
※昨夜のバージョンだと垂れ流しで変な録音が入ってもファイル取得前に上書きされてノイズが見えていませんでしたが、今回は一個一個取得ー消去をするのでより鮮明になってバグが発見できました

```py
    stream=p.open(format = pyaudio.paInt16,
              channels = 1,
              rate = RATE,
              frames_per_buffer = CHUNK,
              input = True)
```
したがって、以下のパラメータ部分のみ外だししています。

```pyaudio_realtime.py
fig = plt.figure(figsize=(6, 5))
ax2 = fig.add_subplot(111)
N=50
CHUNK=1024*N
RATE=44100 #11025 #22050  #44100
p=pyaudio.PyAudio()
fr = RATE
fn=51200*N/50
fs=fn/fr

for s in range(0,900,1):
    print(s)
    start_measure()
    stream=p.open(format = pyaudio.paInt16,
              channels = 1,
              rate = RATE,
              frames_per_buffer = CHUNK,
              input = True)
    input = []
    start_time=time.time()
    input = stream.read(CHUNK)
    stop_time=time.time()

    sig =[]
    sig = np.frombuffer(input, dtype="int16")  /32768.0

    nperseg = 1024
    f, t, Zxx = signal.stft(sig, fs=fn, nperseg=nperseg)
    ax2.pcolormesh(fs*t, f/fs/2, np.abs(Zxx), cmap='hsv')
    ax2.set_xlim(0,fs)
    ax2.set_ylim(20,20000)
    ax2.set_yscale('log')
    ax2.set_axis_off()
    plt.pause(0.01)
    plt.savefig('out_test/figure.jpg')
    #output = stream.write(input)

stream.stop_stream()
stream.close()
p.terminate()
print( "Stop Streaming")
```
###結果
こうして以下のとおり、録音と読込の記録の数が一致するようになりました。

```
>python pyaudio_realtime.py
0
1
2
3
4
5
6
7
8
```
ちょっと１より小さい数字が出ているところは「おはー」とか録音している部分です。
※0は録音前に表示されているので、以下の方が一個少ないです

```
Model loaded.
2019-03-10 17:07:16.052326: W ．．．
[  2.01997850e-07   9.99999762e-01]
hirakegoma
[  1.00000000e+00   8.13874673e-33]
ohayo
[ 0.00337285  0.99662721]
hirakegoma
[  1.00000000e+00   1.21284272e-29]
ohayo
[  1.00000000e+00   1.55338318e-34]
ohayo
[  1.00000000e+00   6.55481190e-15]
ohayo
[  4.60651776e-25   1.00000000e+00]
hirakegoma
[  1.00000000e+00   1.52230811e-10]
ohayo
forrtl: error (200): program aborting due to control-C event
```
###まとめ
・録音データ（スペクトログラム）の保存ー消去を実施したので、余裕をもって一つずつ処理ができるようになった
・読み込むデータの時間軸の初期値が安定した

・少し長めの文章に挑戦したいと思う