Posted at

Pythonで簡単な音声ノイズ除去してplotしてみた


はじめに

大学一回の初めての春休み。思ったよりやることがなくて随分と暇な生活です。サークルの活動ではpocketsphinx等の音声認識ソフトを使って開発していた折、具体的にソフトの仕組みがわかっていないのもなあ。。。と思ったので音声認識ソフトを暇つぶしに作ってみよう!ということで勉強をはじめました。

プログラムもQiitaも音声処理も初心者なので、アドバイスが貰えれば幸いです。


環境


  • ubuntu16.04

  • python2.7


使用したPythonのモジュール


  • Pyaudio

  • Numpy

  • Matplotlib

  • wave

  • time

  • sys


したいこと

音声認識ソフトの第一歩、データセットの作成をしたいです。録音した音声からノイズ除去した綺麗なデータがほしいので、今回は一番手軽な人間の声の周数帯以外の周波数のデシベルを0にしてノイズ除去を行う方法でやってみます。

視覚的にわかりやすくしたかったので、いろいろプロットして模索しました。


書いたクソコード


sound_plot.py

#! -*- coding:utf-8 -*-

import pyaudio
import sys
import time
import wave
import numpy as np
import matplotlib.pyplot as plt
CHUNK = 1024#一回あたり(1/RATE秒毎)に取るデータサイズ,2^10が多い
FORMAT = pyaudio.paInt16
CHANNELS = 1##モノラル入力
RATE = 44100 #サンプリングレート.....マイク性能に依存......
#### input overflow が出たらサンプリングレートを下げる(16000Hz推奨)

RECORD_SECONDS = 3 #計測時間

#######ノイズ除去周波数帯の設定#########
HIGH_CUT = 2400 ##HIGH_CUT周波数
LOW_CUT = 300 ##LOW_CUT周波数

###FFTしてplotするクラス
class FFT_plot:
def __init__(self,data):
self.data = data##録音したdata
self.fft = np.fft.fft(np.frombuffer(data,dtype="int16"))##FFTしたデータ
self.abs=np.abs(self.fft)##FFTを絶対値化したデータ
self.fq = np.linspace(0,len(self.fft),len(self.fft))##周波数軸
def wave_plot(self):##録音をそのままプロット
x = self.data

plt.figure(figsize=(15,3))
plt.plot(x)
plt.show()
def fft_plot(self):##FFTをプロット
x = self.fft

plt.figure(figsize=(15,3))
plt.plot(x.real[:int(len(x)/2)])
plt.show()

def abs_plot(self):##FFT絶対値化をプロット
x = self.abs
F_abs_amp = x/len(x)*2
fq = self.fq

plt.plot(fq,F_abs_amp)
plt.show()

class noise_delete_plot(FFT_plot,object):
def __init__(self,data):
super(noise_delete_plot,self).__init__(data)

def noise_delete(self):##noise除去後のFFTデータを返す
fq = self.fq
fh = HIGH_CUT
fl = LOW_CUT
delete_fft = self.fft
delete_fft[(fq>fh)]=0
delete_fft[(fq<fl)]=0

return delete_fft
def noise_delete_abs(self,delete_fft):##noise除去後のFFT絶対値化データをプロット
fq = self.fq
F2_abs = np.abs(delete_fft)
F2_abs_amp = F2_abs/len(F2_abs)*2

plt.plot(fq,F2_abs_amp)
plt.show()
def noise_delete_wave(self,delete_fft):##noise除去後の音声をプロット

F2_ifft = np.fft.ifft(delete_fft)
F2_ifft_real = F2_ifft.real * 2

plt.figure(figsize=(15,3))
plt.plot(F2_ifft_real)
plt.show()
def record():
p = pyaudio.PyAudio()#インスタンスの作成
#####streamを開く,input = Trueなのでデータを流し込む
stream = p.open(
format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = CHUNK
)

wav_data = []
######動的音源からの音声取得時の注意##########################
#forループが1フレーム(1/RATE秒毎)ごとにループではない
#すべてのchunkに対してループしてしまうのでCHUNKで割ってデータ数を合わせる
##########################################################33
print ("録音開始")
for i in range(0, int(RATE * RECORD_SECONDS/CHUNK)):
data = stream.read(CHUNK)
###streamから1024サイズのデータ(1frame)まるごと取得
###が、得たデータは2048になるのでreshapeする
###特に特殊な操作はせずint16型に治すと自動で戻る
wav_data.append(data)
print("録音終了")
##PyAudioのインスタンスとstreamを終了させる
stream.close()
p.terminate()
##int16型に治す
wav_data = np.array(wav_data)
wav_data = np.frombuffer(wav_data,dtype="int16")

print (wav_data.shape)
return wav_data

if __name__ =='__main__':
r = record()
n = noise_delete_plot(r)
delete_fft = n.noise_delete()
n.noise_delete_wave(delete_fft)


少し長いので分けて説明します。


Class FFT_plot


FFT_plot.py

###FFTしてplotするクラス

class FFT_plot:
def __init__(self,data):
self.data = data##録音したdata
self.fft = np.fft.fft(np.frombuffer(data,dtype="int16"))##FFTしたデータ
self.abs=np.abs(self.fft)##FFTを絶対値化したデータ
self.fq = np.linspace(0,len(self.fft),len(self.fft))##周波数軸
def wave_plot(self):##録音をそのままプロット
x = self.data

plt.figure(figsize=(15,3))
plt.plot(x)
plt.show()
def fft_plot(self):##FFTをプロット
x = self.fft

plt.figure(figsize=(15,3))
plt.plot(x.real[:int(len(x)/2)])
plt.show()
def abs_plot(self):##FFT絶対値化をプロット
x = self.abs
F_abs_amp = x/len(x)*2
fq = self.fq

plt.plot(fq,F_abs_amp)
plt.show()


録音したデータをそのままプロットする関数、FFTしたデータをプロットする関数、FFTしたデータの複素数成分を絶対値をとってプロットする関数からなるクラスです。アンプは交流成分をデータ数で割って2倍すれば作成可能です。


Class noise_delete_plot


noise_delete_plot.py

class noise_delete_plot(FFT_plot,object):

def __init__(self,data):
super(noise_delete_plot,self).__init__(data)

def noise_delete(self):##noise除去後のFFTデータを返す
fq = self.fq
fh = HIGH_CUT
fl = LOW_CUT
delete_fft = self.fft
delete_fft[(fq>fh)]=0
delete_fft[(fq<fl)]=0

return delete_fft
def noise_delete_abs(self,delete_fft):##noise除去後のFFT絶対値化データをプロット
fq = self.fq
F2_abs = np.abs(delete_fft)
F2_abs_amp = F2_abs/len(F2_abs)*2

plt.plot(fq,F2_abs_amp)
plt.show()
def noise_delete_wave(self,delete_fft):##noise除去後の音声をプロット

F2_ifft = np.fft.ifft(delete_fft)
F2_ifft_real = F2_ifft.real * 2

plt.figure(figsize=(15,3))
plt.plot(F2_ifft_real)
plt.show()


noise除去する関数、noise除去後のFFT絶対値化データをプロットする関数、noise除去したデータをプロットする関数からなるクラスです。

ノイズ除去した後、IFFTしています。


def record


record.py

def record():

p = pyaudio.PyAudio()#インスタンスの作成
#####streamを開く,input = Trueなのでデータを流し込む
stream = p.open(
format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = CHUNK
)

wav_data = []
######動的音源からの音声取得時の注意##########################
#forループが1フレーム(1/RATE秒毎)ごとにループではない
#すべてのchunkに対してループしてしまうのでCHUNKで割ってデータ数を合わせる
##########################################################33
print ("録音開始")
for i in range(0, int(RATE * RECORD_SECONDS/CHUNK)):
data = stream.read(CHUNK)
###streamから1024サイズのデータ(1frame)まるごと取得
###が、得たデータは2048になるのでreshapeする
###特に特殊な操作はせずint16型に治すと自動で戻る
wav_data.append(data)
print("録音終了")
##PyAudioのインスタンスとstreamを終了させる
stream.close()
p.terminate()
##int16型に治す
wav_data = np.array(wav_data)
wav_data = np.frombuffer(wav_data,dtype="int16")

print (wav_data.shape)
return wav_data


for文の条件に関して正確なデータ数かと言われればそうではありません。サンプリングレート44100Hzで計測時間が3秒ならば、44100*3=132300フレーム分のデータが得られますが、int(RATE * RECORD_SECONDS/CHUNK)で強制的に整数にしているので、その分のズレがでます。この例では132096フレーム分のデータになるので、少し気になります。。。現状、解決策としては、RATE * RECORD_SECONDSを、CHUNKの倍数にするくらいしか思いつきません。。実際はそんなに影響ないのかも??


ノイズ除去の性能


処理前


処理後



うーん、なにこれ()


反省、次にしてみたいこと

なんとなく綺麗になったような気がしなくもない。。。ですね。。0にするのが正しいのかも怪しいですし。。。(そもそも綺麗に見えるのが正しいわけではない)今回はフェルタリングをかける方法でやってみましたが、他の方法も試して比較、検討してデータセットを作る上で最適な方法を模索したいと思います。まだまだQiitaもプログラムも初心者なので、アドバイスがあれば嬉しいです。


参考にさせて頂いたサイト

http://takeshid.hatenadiary.jp/entry/2016/01/10/153503

http://hanpakousaku.tumblr.com/post/105771613672/raspberrypi-%E3%81%A8-pyaudio%E3%81%A7%E9%8C%B2%E9%9F%B3%E9%9F%B3%E5%A3%B0%E6%B3%A2%E5%BD%A2%E5%87%A6%E7%90%86

https://momonoki2017.blogspot.com/2018/03/pythonfft-4.html