python nn 聲音辨識 -1 傅立葉轉換

辨識環境聲音判斷出這是甚麼聲音

主要想法是用頻域赫茲分布做判斷
首先先錄三種聲音的聲音檔,我每一種聲音重複錄製九個sample,所以有27個wav檔

讀取wav檔案,作正規劃處理,在做傅立葉轉換
一開始由於自己做的fft_domyself() function跟其他聲音處理軟體做出來的不一樣,數值非常小,之後找到有內建的spectrogram() function,使用也是數值很小,最後才想到要20log(),但由於內建的覺得不適合自由調整,所以還是修改自己的function。
import matplotlib.pyplot as plt
from scipy.fftpack import fft
from scipy.io import wavfile # get the api
import numpy as np
from scipy import signal
import math
import pyaudio
import wave
import time
#buf=[]

#讀入單一檔案測試
def init():
    fs, data = wavfile.read('0106.wav') # load the data
    b=[(ele/2**16.) for ele in data] # this is 16-bit track, b is now normalized on [-1,1)
    c = fft(b) # calculate fourier transform (complex numbers list)
    d = len(c)/2  # you only need half of the fft list (real signal symmetry)
    plt.subplot(211)
    plt.plot(b,'b')
    plt.title('time line')
    plt.subplot(212)
    plt.plot(abs(c[:(d-1)]),'r')
    plt.title('fft') 
    plt.show()
    return fs, b    #取樣率 data

#轉成頻域
def fft_domyself(b, fs, plotshow=None):
    global ftmap
    #set value
    one_second_block_num = 21*2                   #one second 擷取次數
    second = len(b)/44100
    second_len = int(one_second_block_num*second)
    fft_num = int(fs/one_second_block_num)        #one second 間格點數 #2100
    ftmap = np.zeros((int(fft_num/2),second_len)) #建立fft陣列的圖片大小  
    startime = 0#362000

    #轉成fft 並儲存在fft陣列
    for i in range(0,second_len):
        endtime = startime + fft_num
        ffttemp = abs(fft(b[startime:endtime]))
        ftmap[0:fft_num,i] = ffttemp[0:int(len(ffttemp)/2)]
        startime = endtime

    #做20log()
    for i in range(0, len(ftmap[:,1])):
        for j in range(0, len(ftmap[1,:])):
            try:
                #ftmap[i,j] = 20*math.log10(ftmap[i,j])
                ftmap[i,j] = 20*math.log(ftmap[i,j], 10)
            except:
                #由於 log(0) 輸入0會error
                print('log error with ftmap: ', ftmap[i,j])
        
    ftmap = ftmap + abs(np.min(ftmap))  
    lineft = np.median(ftmap, axis=1)   #用二維的方式,查看頻域的整體資料情況
    
    if plotshow:
        if plotshow == 'linefft':
            plt.plot(lineft, 'b')
            plt.show()
        else:
            
            plt.plot(lineft,'g') 
            plt.show()
            plt.imshow(ftmap)
            plt.xlabel('Frequency [Hz]')
            plt.ylabel('Time [sec]')
            plt.colorbar()
            plt.show()
     
    return ftmap, lineft

#使用內建的function將聲音轉成頻域
def spectrogram_(b, fs, plotshow=None):
    #x1 = np.array(b, dtype = float)
    f, t, Sxx = signal.spectrogram(b, fs)    #output 時頻譜
    
    for i in range(0, len(Sxx[:,1])):
        for j in range(0, len(Sxx[1,:])):
            Sxx[i,j] = 20*math.log10(Sxx[i,j])
            #print('do')
    Sxx = Sxx + abs(np.min(Sxx))
    
    if plotshow: 
        plt.pcolormesh(t, f, Sxx)             #draw 時頻譜
        plt.ylabel('Frequency [Hz]')
        plt.xlabel('Time [sec]')
        plt.colorbar()
        plt.show()
    return Sxx
if __name__ == '__main__':
    
    fs, b = init()
    fftmap = fft_domyself(b, fs, plotshow = True)   #自己做的
    sx = spectrogram_(b, fs, plotshow = True)       #內建function
2018/2/24更新(添加註解)
img
lineft
ftmap


spectrogram_()



讀多個聲音檔,並轉換成fft矩陣

我是用三個鈴鐺做的,無聲音檔就是沒有鈴鐺聲只有背景聲
  2018/2/24更新
輸出:
linef = [0~20000Hz頻域的大小 ,  0~20000Hz頻域的大小 ,  0~20000Hz頻域的大小 ......]
answer = [ [0.0 , 1.0 , 0.0 , 0.0] , [0.0 , 0.0 , 1.0 , 0.0] , [0.0 , 0.0 , 0.0 , 1.0] ......]
                [0.0 , 1.0 , 0.0 , 0.0] 代表是第一類聲音
def read_file(filename, plotshow=None):
    fs, data = wavfile.read(filename) # load the data
    b=[(ele/2**16.) for ele in data] # this is 16-bit track
    if plotshow:
        plt.plot(b,'b')
        plt.title('time line')
        plt.show()
    return b, fs

def read_to_mat():
    file_road = './train_data/'
    filehead = ['00', '01', '02', '03']
    linef = []
    answer = []
    for i in range(1, 10):                  #last two number
        for j in range(0, len(filehead)):   #first two number
            readURL = file_road + filehead[j] + '%02d'%(i) + '.wav'
            x = [0.0, 0.0, 0.0, 0.0]
            x[int(filehead[j])] = 1.0
            
            audio_value, fs = read_file(readURL)
            fftmap, linefft = fft_domyself(audio_value, fs)
            normalized_linefft = (linefft - np.mean(linefft)) / np.std(linefft)
            
            answer.append(x)
            linef.append(normalized_linefft.tolist())
            print(readURL, x)
    return linef, answer
錄製聲音


def recoder(recoder_second, filename = None, plotshow=None):
    NUM_SAMPLES  =  100       # pyAudio內部緩存的塊的大小
    SAMPLING_RATE = 44100     #取樣頻率
    #recoder_second = 1       
    '''
    LEVEL  =  1500             #聲音保存的閾值
    COUNT_NUM  =  20           # NUM_SAMPLES個取樣之內出現COUNT_NUM個大於LEVEL的取樣則記錄聲音
    SAVE_LENGTH  =  8          #聲音記錄的最小長度:SAVE_LENGTH * NUM_SAMPLES個取樣
    skip_second = 0.07
    swidth = 2
    '''
    #開啟聲音輸入

    save_buffer = []
    pa  =  pyaudio.PyAudio ()  
    stream  =  pa . open ( format = pyaudio.paInt16 ,  channels = 1 ,  rate = SAMPLING_RATE ,  input = True ,  #output = True,
                    frames_per_buffer = NUM_SAMPLES ) #,stream_callback = callback 

    time.sleep(0.1)
    stream.start_stream()
    skip_buffer = []
    num = 3500 / NUM_SAMPLES#SAMPLING_RATE / NUM_SAMPLES * skip_second
    for i in range(0, int(num)): 
        string_audio_data  =  stream.read ( NUM_SAMPLES) 
        skip_buffer.append(np.fromstring(string_audio_data, np.int16))
        #stream.read ( NUM_SAMPLES) 
    
    print('star recoder')
    
    num = SAMPLING_RATE / NUM_SAMPLES * recoder_second
    for i in range(0, int(num)): 
        #讀入NUM_SAMPLES個取樣
        string_audio_data  =  stream.read ( NUM_SAMPLES ) 
        save_buffer.append(np.fromstring(string_audio_data, np.int16))
        #stream.write(string_audio_data, NUM_SAMPLES)
    print("* done recording")

    stream.stop_stream()
    stream.close()
    pa.terminate()
    
    numpydata = np.hstack(save_buffer)
    skipnp = np.hstack(skip_buffer).tolist()
    
    '''
    #直接存檔
    WAVE_OUTPUT_FILENAME = filename + ".wav"
    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(1)
    wf.setsampwidth(pa.get_sample_size(pyaudio.paInt16))
    wf.setframerate(SAMPLING_RATE)
    wf.writeframes(b''.join(save_buffer))
    wf.close()
    '''
    if plotshow:
        if plotshow == 'line':
            plt.plot(numpydata,'r')
            plt.show()
        else:    
            plt.subplot(311)
            plt.plot(skipnp,'g')
            
            plt.subplot(312)
            plt.plot(save_buffer,'b')
            
            plt.subplot(313)
            plt.plot(numpydata,'r')
            plt.show()
    
    return numpydata, SAMPLING_RATE

if __name__ == '__main__':
    buffer = []
    recoder_second = 0.5
    buffer, fs = recoder(recoder_second, plotshow=True)
    fttmap, linef = fft_domyself(buffer, fs, plotshow = True)

開始錄音時發現會有一個雜訊,那個雜訊值非常大,以下是跳過雜訊再錄音0.05秒,green line是跳過的雜訊,blue line是接續綠色後面的0.05秒錄音
green line:跳過的雜訊
blue line:原始錄到的資料,每一點的資料量跟 (NUM_SAMPLES  =  100 # pyAudio內部緩存的塊的大小) 有關
red line:將blue line的資料串接起來







留言

  1. 請問如何辨識出來~然後自動分類....
    我到第25行的地方就不太行了
    求高手教QAQ

    回覆刪除
    回覆
    1. 自動分類?! 類神經網路就直接讓他學習就好 他會自己處理
      但是你給他訓練的資料(聲音檔)要自己分類喔
      抱歉!!~我不知道你指的第25行是指哪一段的25行

      刪除
    2. 作者已經移除這則留言。

      刪除
    3. 我是不知道轉速的聲音有沒有差別 但最好能擷取較無雜訊的音訊
      其實我真正用程式錄音方式下去做其實誤差非常大 我是用鈴鐺聲音 用程式錄音就沒辦法把前後雜音的部份去掉 我先前的錄音檔是有經過去除無鈴鐺聲音的部分 所以成功機率較高
      所以基本上要將聲音去頭去尾 只留下真正需要的部分

      我這個其實是很陽春版的 只是用來實驗 聲音也能分類
      其實他主要還是用圖像的方式做神經網路 你可以以圖像的角度去看待它會比較清楚 網上有很多對於圖像的範例

      刪除
  2. 有沒有不經過圖像,直接用FFT轉換後數字化,用全數字的檔案辨識的方法

    回覆刪除
    回覆
    1. 你所說的全數字的檔案跟我說的圖像意思其實是一樣的,一張黑白照片其實就是一個全數字檔
      FFT轉換後一定會轉成我第四張圖那樣,一個二維的圖每一個像素點就是一個數字啊!!

      刪除
  3. 版主你好~想請問第一段第二張做出來紅色的圖的Y軸的數值代表什麼意思?為什麼需要透過abs讓他全部都為正的?還有第三張綠色的圖的X軸和Y軸又分別代表什麼意思呢?謝謝

    回覆刪除
    回覆
    1. 有點久了幾乎忘記當初在做甚麼,回答可能不一定正確先跟你說聲抱歉了。
      我想紅色的Y軸應該是傅立葉轉換出來的大小,abs轉正是為了視覺上的習慣,像是音樂撥放器或是收音設備上的頻譜都沒有顯示負值,所以就習慣看沒有負值。
      綠色的圖 (lineft) 應該跟 ftmap 是一樣的意思,在ftmap上有軸的說明,所以綠色的圖 (lineft) X軸應該是Hz吧!? Y軸應該就是值的大小了。

      刪除
    2. 最近研究雜音捕捉看到這篇,16-bit wav 有一位是符號,b=ele/2^16結果應該是-0.5~0.5才對,才會有數值非常小的問題。
      20log()是音壓,據我理解,如果是處理錄製結果,資料數值應該是變回音強。

      刪除

張貼留言

這個網誌中的熱門文章

C# 模擬鍵盤滑鼠控制電腦

python pyautogui 簡介

android 定時通知(永久長期的) 本篇只講AlarmManager使用

raspberrypi 開機自動執行程式 與 在terminal開啟第二個terminal執行python