SSブログ

スペクトログラムの学習用データの生成を自動化する [AI]

スペクトログラムの学習用データの生成の続きです。前回、一つのスペクトログラムのデータから複数の学習用データを生成する基本的な処理が出来上がりましたので、今回は学習用データを量産してみたいと思います。


前回と同じくデータは、”イチ”、”二”、”サン”の3つ。それに無音も認識が必要なので無音のノイズデータの4点です。学習用の画像データは32x32の大きさにしました。結果から出してしまいますが、次のようなデータが生成されました。


Spectgram_training.png


Python のスケッチを次にあげておきます。見通しをよくするために幾つか関数に分離をしていますが、前回のアルゴリズムをそのまま踏襲しています。追加した処理は、次の3点です。

(1) 無音時のノイズ処理(スペクトラムの範囲を決められない為)
(2) ランダムにシフト量とゲイン量を算出する処理
(3) スペクトラムをリサイズして画像として保存する処理

import numpy as np
import cv2 as cv
import random
import os
import scipy.io.wavfile as wav
from matplotlib import pyplot as plt
from numpy.lib import stride_tricks
from numpy.lib.stride_tricks import as_strided
from numpy.fft import rfft

""" short time fourier transform of audio signal """
def stft(samples, frameSize, overlapFac = 0.5, window = np.hanning):
    win = window(frameSize)
    hopSize = int(frameSize - np.floor(overlapFac * frameSize))
    
    # cols for windowing
    cols = np.floor(len(samples) / hopSize) - 1
    # print("cols: ", cols)
    
    # make an array in 2nd dimension with hopSize
    frames = as_strided(samples, shape=(int(cols), frameSize), strides = (samples.strides[0] * hopSize, samples.strides[0])).copy()
    frames = frames * win

    return rfft(frames)    

def spectrogram_detector(spectrogram):
    spectro_mean = np.mean(spectrogram, axis=1)
    threshold = np.ceil(np.mean(spectro_mean)) 
    threshold = np.ceil(np.mean(spectro_mean[spectro_mean <= threshold]))  # pick up only noise signals
    print("threshold: ", threshold)
    offset = 5
    up_trigger = 0
    down_trigger = 0
    for i in range(offset, spectro_mean.shape[0]-offset):
        cur_floor = int(np.mean(spectro_mean[i:i+offset-1]))
        pre_floor = int(np.mean(spectro_mean[i-offset:i-1]))
        if ((cur_floor > threshold) and (pre_floor <= threshold) and (up_trigger == 0)):
            up_trigger = i
        if ((cur_floor <= threshold) and (pre_floor > threshold)):
            down_trigger = i
    print("up_trigger  : ", up_trigger)
    print("down_trigger: ", down_trigger)
    return up_trigger, down_trigger
    

def augmentator(spectrogram, left, right, signal):
    axis_max = spectrogram.shape[0]
    if (signal == 0): # in case of zero1.wav
        left = random.randint(0, np.floor(axis_max/2))
        right = random.randint(np.ceil(axis_max/2), axis_max-1)
    left_or_right = random.randint(0,1)
    if (left_or_right > 0): # 1 is right, 0 is left
        move = random.randint(right, axis_max-1)
        move = move - right
    else:
        move = random.randint(0,left)
        move = -move
    print("move: ", move)
    
    spectrogram_result = spectrogram.copy()
    spectrogram_body = spectrogram[left:right, :] # extract spectoram data
    
    #if the shift is left, the value is minus, the shift is right, tha value is plus
    if (move < 0):
        spectrogram_noise = spectrogram[left+move:left,:]
        spectrogram_result[left+move:right+move,:] = spectrogram_body
        spectrogram_result[right+move:right,:] = spectrogram_noise
    elif (move > 0):
        spectrogram_noise = spectrogram[right:right+move,:]
        spectrogram_result[left+move:right+move,:] = spectrogram_body
        spectrogram_result[left:left+move,:] = spectrogram_noise

    max_gain = 255 / np.max(spectrogram)   # maximum gain
    gain = random.uniform(0.67, max_gain)
    print("gain: ", gain)
    return spectrogram_result * gain

def plot_spectrogram(spectrogram, time_axis, freq_axis, time_interval, frequency_map):
    plt.figure(figsize=(16, 8))
    plt.imshow(np.transpose(spectrogram), origin="lower", aspect="auto", cmap="jet", interpolation="none")
    plt.colorbar()

    plt.xlabel("time (s)")
    plt.ylabel("frequency (hz)")
    plt.xlim([0, time_axis-1])
    plt.ylim([0, freq_axis-1])

    xlocs = np.float32(np.linspace(0, time_axis-1, 5))
    plt.xticks(xlocs, ["%.02f" % i for i in (xlocs * time_interval)])
    ylocs = np.int16(np.floor(np.linspace(0, freq_axis-1, 10)))
    plt.yticks(ylocs, ["%.02f" % frequency_map[i] for i in ylocs])

    plt.show()
    
def gen_spectrogram(wavfile, frameSize):
    sample_rate, samples = wav.read(wavfile)
    print("sample_rate:", sample_rate)
    print("num_of_samples:", len(samples))

    spectrogram = stft(samples, frameSize)
    frequency_map = np.fft.fftfreq(frameSize, 1. / sample_rate)
    spectrogram = 20. * np.log10(np.abs(spectrogram)) # amplitude to decibel

    # spectrogaram has an extra colum, so slice it
    spectrogram = spectrogram[:,:int(np.floor(frameSize/2))]
    spectrogram -= np.min(spectrogram) # normalize
    
    return spectrogram, sample_rate, frequency_map
    
def main():
    frameSize = 256
    output_img_size = 32
    num_of_augdata = 5 
    wavfiles = ['./wav/zero1.wav', './wav/ichi1.wav', './wav/ni1.wav', './wav/san1.wav']
    
    for i in range(len(wavfiles)):
        print(wavfiles[i])
        spectrogram, sample_rate, frequency_map = gen_spectrogram(wavfiles[i], frameSize)
        time_dim, freq_dim = np.shape(spectrogram)
        time_interval = frameSize / sample_rate 
        plot_spectrogram(spectrogram, time_dim, freq_dim, time_interval, frequency_map)

        # detect the range of spectrogram signal
        left, right = spectrogram_detector(spectrogram)

        data_dir = "./" + str(i)
        if not os.path.exists(data_dir):
            os.mkdir(data_dir)
        for n in range(num_of_augdata):
            # spectrogram data augmentator
            spectro_aug = augmentator(spectrogram, left, right, i)
            # plot_spectrogram(spectro_aug, time_dim, freq_dim, time_interval, frequency_map)
            
            spectro_aug.astype(np.uint8)
            spectro_img = cv.resize(spectro_aug, (output_img_size, output_img_size))
            spectro_img = np.rot90(spectro_img)
            data_path = data_dir + "/" + str(n) + ".bmp"
            cv.imwrite(data_path, spectro_img)
    
if __name__ == "__main__":
    main()




おそらく実機では、マイクのノイズやゲイン量が大きく影響するので作り直しになると思いますが、このスケッチそのものはパラメータなどを変更することで流用できそうです。
(^^)/~





Pythonで学ぶ実践画像・音声処理入門

Pythonで学ぶ実践画像・音声処理入門

  • 出版社/メーカー: コロナ社
  • 発売日: 2018/04/04
  • メディア: 単行本






Pythonデータサイエンスハンドブック ―Jupyter、NumPy、pandas、Matplotlib、scikit-learnを使ったデータ分析、機械学習

Pythonデータサイエンスハンドブック ―Jupyter、NumPy、pandas、Matplotlib、scikit-learnを使ったデータ分析、機械学習

  • 出版社/メーカー: オライリージャパン
  • 発売日: 2018/05/26
  • メディア: 単行本(ソフトカバー)




nice!(22)  コメント(0) 
共通テーマ:趣味・カルチャー

nice! 22

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。