適応線スペクトル強調器(ALE)によるノイズ除去

適応線スペクトル強調器(Adaptive Line Enhancer, ALE)によるノイズ除去を試してみました。

久しぶりにノイズ除去を試してみたく、参考文献[1] を読んだら、ALEが紹介されていましたので、Python で実装してみた次第です。

ALEの原理

ALEのブロック図を以下に示します。

図:ALEのブロック図
図:ALEのブロック図

ALEでは図のように D サンプル遅延させた \(\ x[n-D]\ \)から適応フィルタを使用して予測信号\(\ y[n]\ \)を出力します。

適応フィルタの係数 \(\ h_{m}[n]\ \) の更新のやり方は何でも良いそうですが、NLMSアルゴリズムを使用する場合は、以下の式で更新します。

$$
h_{m}[n+1] = h_{m}[n] + \mu \frac{x[n-D-m]}{\sum_{l=0}^{M-1}x^{2}[n-D-l]} e[n] $$

これでノイズ除去できる理由としては、白色雑音は過去の信号と相関がないため、予測信号のなかに含まれませんが、正弦波などの周期的な信号は過去の信号の線形結合で予測ができるため、ノイズ除去した信号を抽出できるというわけです。

プログラム

ALEのプログラムを Python で実装しました。ソースコード ale.py は以下となります。

import soundfile as sf
import numpy as np

# パラメータ
M  = 500      # フィルタのタップ数
mu = 0.001    # ステップサイズ
D  = 10       # 遅延量
wav_in_name  = "input.wav"  # 入力WAVファイル名
wav_out_name = "output.wav" # 出力WAVファイル名

# WAVファイルを読み込む
x, fs = sf.read( wav_in_name )

# 配列用意
x_buf = np.zeros(M, dtype=np.float64)
h_buf = np.zeros(M, dtype=np.float64)
y     = np.zeros(len(x), dtype=np.float64)

# eps 用意
fi  = np.finfo(np.float64)
eps = fi.eps 

for n in range( len(x) ):

    # バッファに詰める
    x_buf[1:M] = x_buf[0:M-1]
    if n-D < 0:
        x_buf[0] = 0.0
    else:
        x_buf[0] = x[n-D]

    # 予測信号を求める
    y[n] = np.sum( x_buf * h_buf )

    # 誤差信号を求める
    error = x[n] - y[n] 

    # 適応フィルタ更新
    power = np.sum( np.square(x_buf) ) + eps
    h_buf = h_buf + (mu * error * x_buf)/power

# WAVファイルを書き込む
sf.write( wav_out_name, y, fs, subtype='PCM_16' )

19~21行目:適応フィルタ更新におけるゼロ除算を防ぐためにあらかじめマシンイプシロンを用意しておきます。

25~30行目:x[n-D]をバッファに詰めています。\(\ n-D < 0\ \) のときは0を詰めるようにしています。

実行方法

(1) プログラムを実行するディレクトリにソースコード(ale.py)とノイズ除去したいWAVファイルを格納する。

(2) ソースコード4~9行目のパラメータを修正する。

# パラメータ
M  = 500      # フィルタのタップ数
mu = 0.001    # ステップサイズ
D  = 10       # 遅延量
wav_in_name  = "input.wav"  # 入力WAVファイル名
wav_out_name = "output.wav" # 出力WAVファイル名

(3) 以下のコマンドで python を実行すると、ノイズ除去信号(output.wav)が出力される。

$ python ale.py

実行結果

ALEを使用してノイズ除去できるか確認してみました。設定したパラメータの値は以下です。

表:設定したパラメータ値
引数 パラメータ名 記号
1 フィルタのタップ数 \(M\) 500
2 ステップサイズ \(\mu\) 0.001
3 遅延量 \(D\) 10

とりあえず、参考文献[1]で使用していたパラメータ値を使用しています。

文献[1]によると、フィルタのタップ数が小さいとフィルタの特性が緩やかになり、白色雑音も含まれやすくなるため、タップ数は数10~数100くらいが良いとのことです。

遅延量 D については1以上に設定すればよいそうですが、一応やや大きめに設定しているとのことです。

正弦波 + ノイズ

まず、1kHz の正弦波に白色雑音を付加して、ノイズ除去できるか確認しました。入出力の音ファイルは以下となります。

入力(正弦波+ノイズ)

出力(ノイズ除去後)

波形とスペクトログラムについては以下のようになっています。

図:入出力の波形とスペクトログラム
図:入出力の波形とスペクトログラム(正弦波)
せとち
おおー、めっちゃきれいな音になってる!!

音声 + ノイズ

つぎに音声に白色雑音を付加して、ノイズ除去できるか確認しました。入出力の音ファイルは以下となります。

入力(音声+ノイズ)

出力(ノイズ除去後)

波形とスペクトログラムについては以下です。

図:入出力の波形とスペクトログラム(音声)
図:入出力の波形とスペクトログラム(音声)
せとち
ノイズはなくなっていますが、子音が聞こえづらくなってますね(ちなみに音の大きさは揃えています)

おわりに

本記事では、適応線スペクトル強調器を実装して、ノイズ付加した正弦波と音声からノイズ除去してみました。

思った以上にノイズ除去されていて驚きました。ただ、音声の子音とかは周期的な信号ではないため、ノイズと同じように抑制されているなと感じました。そのため、音声に適用するのは少し難しそうな印象です。

■参考文献
[1] 川村新、”音声音響信号処理の基礎と実践”、コロナ社、2021.