ウィーナーフィルタ:最適線形フィルタの理論とPython実装

ウィーナーフィルタの導出(MSE最小化・ウィーナー・ホップ方程式)から周波数領域での実装、画像・信号のノイズ除去までを解説します。

はじめに

ウィーナーフィルタは、平均二乗誤差(MSE)を最小化する最適線形フィルタです。ノイズを含む観測信号から元の信号を推定する問題において、定常確率過程の枠組みで理論的に最適な解を与えます。

問題設定

ノイズを含む観測信号:

\[y(t) = s(t) + n(t) \tag{1}\]

ここで \(s(t)\) は所望の信号、\(n(t)\) はノイズです。フィルタ \(h(t)\) を通した出力 \(\hat{s}(t)\) で \(s(t)\) を推定します。

\[\hat{s}(t) = \int_{-\infty}^{\infty} h(\tau) y(t - \tau) d\tau \tag{2}\]

MSE 最小化

推定誤差の二乗平均を最小化します。

\[\min_h E\left[|s(t) - \hat{s}(t)|^2\right] \tag{3}\]

ウィーナー・ホップ方程式

最適条件(直交性原理)から、以下の方程式が導かれます。

\[R_{yy}(\tau) * h(\tau) = R_{sy}(\tau) \tag{4}\]

ここで \(R_{yy}\) は観測信号の自己相関、\(R_{sy}\) は所望信号と観測信号の相互相関です。

周波数領域での解

周波数領域では畳み込みが乗算になるため、ウィーナーフィルタは簡潔に表現できます。

\[H(f) = \frac{S_{sy}(f)}{S_{yy}(f)} \tag{5}\]

信号とノイズが無相関の場合:

\[H(f) = \frac{S_{ss}(f)}{S_{ss}(f) + S_{nn}(f)} = \frac{\text{SNR}(f)}{\text{SNR}(f) + 1} \tag{6}\]

ここで \(S_{ss}(f)\) は信号のパワースペクトル、\(S_{nn}(f)\) はノイズのパワースペクトル、\(\text{SNR}(f) = S_{ss}(f) / S_{nn}(f)\) は周波数ごとの信号対雑音比です。

直感的な理解: SNRが高い周波数帯域はそのまま通し、SNRが低い帯域は減衰させます。

Python実装

1次元信号のノイズ除去

import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, ifft, fftfreq

np.random.seed(42)

# --- 信号生成 ---
fs = 1000  # サンプリング周波数
t = np.arange(0, 1, 1/fs)
signal = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 20 * t)
noise = np.random.normal(0, 0.8, len(t))
observed = signal + noise

# --- ウィーナーフィルタ(周波数領域) ---
N = len(observed)
Y = fft(observed)
freqs = fftfreq(N, 1/fs)

# パワースペクトル推定
S_yy = np.abs(Y)**2 / N
noise_power = np.var(noise) * N  # ノイズのパワー(一様)
S_nn = noise_power / N * np.ones(N)
S_ss = np.maximum(S_yy - S_nn, 0)  # 信号パワーの推定

# ウィーナーフィルタ
H = S_ss / (S_ss + S_nn + 1e-10)
S_filtered = ifft(Y * H).real

# --- 可視化 ---
fig, axes = plt.subplots(3, 1, figsize=(12, 8))

axes[0].plot(t, signal, 'b-', alpha=0.7, label='Original')
axes[0].set_title('Original Signal')
axes[0].set_ylabel('Amplitude')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

axes[1].plot(t, observed, 'gray', alpha=0.5, label='Noisy')
axes[1].set_title('Observed (Signal + Noise)')
axes[1].set_ylabel('Amplitude')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

axes[2].plot(t, signal, 'b-', alpha=0.3, label='Original')
axes[2].plot(t, S_filtered, 'r-', alpha=0.8, label='Wiener filtered')
axes[2].set_title('Wiener Filter Output')
axes[2].set_xlabel('Time [s]')
axes[2].set_ylabel('Amplitude')
axes[2].legend()
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

mse_noisy = np.mean((signal - observed)**2)
mse_filtered = np.mean((signal - S_filtered)**2)
print(f"MSE (noisy):    {mse_noisy:.4f}")
print(f"MSE (filtered): {mse_filtered:.4f}")
print(f"Improvement:    {(1 - mse_filtered/mse_noisy)*100:.1f}%")

フィルタの周波数応答

plt.figure(figsize=(10, 4))
positive_freqs = freqs[:N//2]
plt.plot(positive_freqs, H[:N//2], 'g-', linewidth=2)
plt.xlabel('Frequency [Hz]')
plt.ylabel('|H(f)|')
plt.title('Wiener Filter Frequency Response')
plt.grid(True, alpha=0.3)
plt.xlim(0, 100)
plt.tight_layout()
plt.show()

カルマンフィルタとの関係

特徴ウィーナーフィルタカルマンフィルタ
前提定常過程非定常でも可
処理方式バッチ(周波数領域)逐次(時間領域)
最適性MSE最小(定常)MSE最小(線形ガウス)
適用範囲信号処理状態推定・制御

ウィーナーフィルタは定常過程における最適フィルタであり、カルマンフィルタはその非定常・逐次処理への拡張と見なせます。

関連記事

参考文献

  • Haykin, S. (2014). Adaptive Filter Theory (5th ed.). Pearson. Chapters 2-3.
  • Wiener, N. (1949). Extrapolation, Interpolation, and Smoothing of Stationary Time Series. MIT Press.
  • Vaseghi, S. V. (2008). Advanced Digital Signal Processing and Noise Reduction (4th ed.). Wiley.