連続信号をデジタル化するとき、サンプリング周波数が不十分だとエイリアシング(aliasing)が発生します。高い周波数の信号が低い周波数として「化ける」この現象は、音声処理、画像処理、通信工学などあらゆるデジタル信号処理で注意が必要です。
エイリアシングは単なる「サンプリング不足」ではなく、周波数スペクトルの折り返し(フォールディング)という明確な数学的メカニズムに基づいています。本記事では、この現象を数式で厳密に理解し、対策を考えます。
本記事の内容
- エイリアシングの直感的な理解
- サンプリングの数学的表現と折り返しの導出
- ナイキスト-シャノンのサンプリング定理との関係
- アンチエイリアシングフィルターの設計
- 画像処理におけるエイリアシング
- Pythonでの可視化と検証
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
エイリアシングの直感的理解
エイリアシングの本質は、「離散サンプル点だけ見ると、異なる周波数の信号が同じに見える」ことです。
例えば、サンプリング周波数 $f_s = 10\,\text{Hz}$ で信号を取得する場合を考えます。周波数 $3\,\text{Hz}$ の正弦波と $13\,\text{Hz}$ の正弦波は、サンプリング点ではまったく同じ値を取ります。これは:
$$ \sin(2\pi \cdot 3 \cdot n/10) = \sin(2\pi \cdot 3n/10) $$
$$ \sin(2\pi \cdot 13 \cdot n/10) = \sin(2\pi \cdot 13n/10) = \sin(2\pi n + 2\pi \cdot 3n/10) = \sin(2\pi \cdot 3n/10) $$
となるからです。$13\,\text{Hz}$ の信号は $3\,\text{Hz}$ の「エイリアス」(偽名)として現れます。
サンプリングの数学的表現
理想サンプリング
連続信号 $x(t)$ をサンプリング周期 $T_s = 1/f_s$ で理想サンプリングすると、サンプリングされた信号 $x_s(t)$ は:
$$ x_s(t) = x(t) \cdot \sum_{n=-\infty}^{\infty} \delta(t – nT_s) $$
ここで $\delta(t)$ はディラックのデルタ関数です。この信号のフーリエ変換を求めます。
サンプリングされた信号のスペクトル
くし型関数(ディラックコム)のフーリエ変換は:
$$ \sum_{n=-\infty}^{\infty} \delta(t – nT_s) \xrightarrow{\mathcal{F}} \frac{1}{T_s}\sum_{k=-\infty}^{\infty} \delta\left(f – \frac{k}{T_s}\right) = f_s \sum_{k=-\infty}^{\infty} \delta(f – kf_s) $$
時間領域での乗算は周波数領域での畳み込みに対応するので:
$$ X_s(f) = X(f) * f_s \sum_{k=-\infty}^{\infty} \delta(f – kf_s) $$
畳み込みを実行すると:
$$ \boxed{X_s(f) = f_s \sum_{k=-\infty}^{\infty} X(f – kf_s)} $$
この式が示すのは、サンプリングされた信号のスペクトルは、元のスペクトル $X(f)$ が $f_s$ の間隔で無限に繰り返されるということです。
折り返し(エイリアシング)の発生条件
元の信号の最大周波数を $f_{\max}$ とします。スペクトルの繰り返しコピーが重ならない条件は:
$$ f_{\max} < f_s - f_{\max} $$
$$ \boxed{f_s > 2f_{\max}} $$
これがナイキスト-シャノンのサンプリング定理です。$f_N = f_s/2$ をナイキスト周波数と呼びます。
$f_s \leq 2f_{\max}$ の場合、スペクトルのコピーが重なり合い(折り返し)、元のスペクトルとエイリアス成分を分離できなくなります。この重なりがエイリアシングです。
エイリアシングの周波数関係
周波数 $f_0$ の信号がサンプリング周波数 $f_s$ でサンプリングされたとき、エイリアスとして現れる周波数 $f_a$ は:
$$ f_a = |f_0 – k \cdot f_s| \quad (k \text{ は } f_a \text{ が } [0, f_s/2] \text{ に入る整数}) $$
より具体的に、$f_0 > f_s/2$ の場合のエイリアス周波数は:
$$ \boxed{f_a = \left| f_0 \bmod f_s – f_s \cdot \text{round}\left(\frac{f_0 \bmod f_s}{f_s}\right) \right|} $$
実用上は、$f_0$ を $f_s$ で割った余りが $f_s/2$ を超えるかどうかで場合分けします:
$$ f_a = \begin{cases} f_0 \bmod f_s & \text{if } (f_0 \bmod f_s) \leq f_s/2 \\ f_s – (f_0 \bmod f_s) & \text{if } (f_0 \bmod f_s) > f_s/2 \end{cases} $$
アンチエイリアシングフィルター
原理
エイリアシングを防ぐ最も基本的な方法は、サンプリング前にナイキスト周波数以上の成分を除去することです。これがアンチエイリアシング(AA)フィルターです。
理想的なAAフィルターは、ナイキスト周波数 $f_N = f_s/2$ を遮断周波数とする理想ローパスフィルターです:
$$ H_{\text{ideal}}(f) = \begin{cases} 1 & |f| \leq f_N \\ 0 & |f| > f_N \end{cases} $$
しかし、理想ローパスフィルターは因果的でない(未来の入力が必要)ため、実現不可能です。実際にはバターワースフィルターやチェビシェフフィルターなどの近似フィルターを使います。
フィルター次数とロールオフ
$n$ 次バターワースフィルターの振幅特性は:
$$ |H(f)| = \frac{1}{\sqrt{1 + (f/f_c)^{2n}}} $$
次数 $n$ が大きいほど急峻なロールオフが得られ、通過帯域と阻止帯域の遷移が狭くなります。ただし次数が高いと位相歪みが大きくなります。
オーバーサンプリング
もう一つの対策はオーバーサンプリングです。必要な帯域に対してサンプリング周波数を十分高く取ることで、エイリアシングの影響を軽減します。デジタルオーディオでは、$f_s = 44.1\,\text{kHz}$(CD)ではなく $f_s = 192\,\text{kHz}$(ハイレゾ)でサンプリングし、その後デジタルフィルターで帯域制限する手法が使われます。
画像処理におけるエイリアシング
空間エイリアシング
画像はピクセルで離散化された2次元信号です。空間周波数がナイキスト空間周波数を超えると、モアレパターンやジャギー(階段状のエッジ)として現れます。
2次元のサンプリング定理は、ピクセルピッチ $\Delta x$ に対して:
$$ f_{x,\max} < \frac{1}{2\Delta x} $$
画像のダウンサンプリングとAAフィルター
画像を縮小(ダウンサンプリング)する際には、縮小前にAAフィルター(ガウシアンフィルターなど)を適用する必要があります。フィルターなしで縮小すると、高周波成分がエイリアシングとして現れます。
ガウシアンフィルターのカーネル:
$$ G(x, y) = \frac{1}{2\pi\sigma^2}\exp\left(-\frac{x^2+y^2}{2\sigma^2}\right) $$
ダウンサンプリング比 $r$ に対して、$\sigma \approx 0.5/r$ 程度のガウシアンを事前に適用すると、エイリアシングを効果的に抑制できます。
Pythonでの可視化
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# === (1) 時間領域でのエイリアシング ===
ax = axes[0, 0]
fs = 10.0 # サンプリング周波数 [Hz]
t_cont = np.linspace(0, 1, 10000)
t_sample = np.arange(0, 1, 1/fs)
f1, f2 = 3.0, 13.0 # 3Hzと13Hz(= 3 + 10)
sig1_cont = np.sin(2*np.pi*f1*t_cont)
sig2_cont = np.sin(2*np.pi*f2*t_cont)
sig1_sample = np.sin(2*np.pi*f1*t_sample)
sig2_sample = np.sin(2*np.pi*f2*t_sample)
ax.plot(t_cont, sig1_cont, 'b-', lw=1.5, alpha=0.7, label=f'{f1:.0f} Hz (original)')
ax.plot(t_cont, sig2_cont, 'r-', lw=1.5, alpha=0.7, label=f'{f2:.0f} Hz (aliased)')
ax.stem(t_sample, sig1_sample, linefmt='k-', markerfmt='ko', basefmt='k-',
label=f'Samples ($f_s$={fs:.0f} Hz)')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Amplitude')
ax.set_title('Aliasing in time domain', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
# === (2) 周波数領域でのスペクトルの折り返し ===
ax = axes[0, 1]
fs = 100.0
f_sig = 30.0 # 信号周波数
f_axis = np.linspace(-200, 200, 2000)
# 元のスペクトル(帯域制限信号を模擬)
def bandlimited_spectrum(f, f0, bw=10):
return np.exp(-0.5*((f-f0)/bw)**2) + np.exp(-0.5*((f+f0)/bw)**2)
X_orig = bandlimited_spectrum(f_axis, f_sig, bw=8)
ax.fill_between(f_axis, X_orig, alpha=0.3, color='blue', label='Original $X(f)$')
# サンプリング後のスペクトル(繰り返し)
X_sampled = np.zeros_like(f_axis)
for k in range(-3, 4):
X_sampled += bandlimited_spectrum(f_axis - k*fs, f_sig, bw=8)
ax.plot(f_axis, X_sampled, 'r-', lw=1.5, alpha=0.8, label=f'Sampled ($f_s$={fs:.0f} Hz)')
ax.axvline(fs/2, color='green', ls='--', lw=1.5, label=f'Nyquist freq = {fs/2:.0f} Hz')
ax.axvline(-fs/2, color='green', ls='--', lw=1.5)
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('|X(f)|')
ax.set_title('Spectrum folding (aliasing in frequency domain)', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_xlim(-180, 180)
# === (3) エイリアス周波数のマッピング ===
ax = axes[1, 0]
fs = 1000.0
f_input = np.linspace(0, 3*fs, 1000)
def alias_freq(f0, fs):
"""エイリアス周波数を計算"""
f_mod = np.mod(f0, fs)
f_alias = np.where(f_mod <= fs/2, f_mod, fs - f_mod)
return f_alias
f_alias = alias_freq(f_input, fs)
ax.plot(f_input/fs, f_alias/(fs/2), 'b-', lw=2)
ax.set_xlabel('$f_0 / f_s$')
ax.set_ylabel('$f_{alias} / f_N$')
ax.set_title(f'Alias frequency mapping ($f_s$={fs:.0f} Hz)', fontsize=13)
ax.axvline(0.5, color='r', ls='--', lw=1, alpha=0.7, label='$f_N = f_s/2$')
ax.axvline(1.0, color='gray', ls=':', lw=1, alpha=0.7)
ax.axvline(1.5, color='r', ls='--', lw=1, alpha=0.7)
ax.axvline(2.0, color='gray', ls=':', lw=1, alpha=0.7)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
# === (4) アンチエイリアシングフィルターの効果 ===
ax = axes[1, 1]
# 帯域外成分を含む信号
fs_low = 50.0
t_cont2 = np.linspace(0, 0.2, 10000)
t_sample2 = np.arange(0, 0.2, 1/fs_low)
# 信号: 5Hz + 40Hz(ナイキスト周波数25Hzを超える)
signal = np.sin(2*np.pi*5*t_cont2) + 0.5*np.sin(2*np.pi*40*t_cont2)
signal_sample_noAA = np.sin(2*np.pi*5*t_sample2) + 0.5*np.sin(2*np.pi*40*t_sample2)
# AAフィルター適用後(40Hzを除去 = 5Hzのみ残す)
signal_filtered = np.sin(2*np.pi*5*t_cont2)
signal_sample_AA = np.sin(2*np.pi*5*t_sample2)
ax.plot(t_cont2, signal, 'gray', lw=1, alpha=0.5, label='Original (5Hz + 40Hz)')
ax.plot(t_cont2, signal_filtered, 'b-', lw=1.5, alpha=0.7, label='After AA filter (5Hz only)')
ax.stem(t_sample2, signal_sample_noAA, linefmt='r-', markerfmt='ro', basefmt='r-',
label='Sampled w/o AA')
ax.stem(t_sample2, signal_sample_AA, linefmt='g-', markerfmt='g^', basefmt='g-',
label='Sampled w/ AA')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Amplitude')
ax.set_title(f'Anti-aliasing filter effect ($f_s$={fs_low:.0f} Hz, $f_N$={fs_low/2:.0f} Hz)', fontsize=12)
ax.legend(fontsize=8, loc='upper right')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
左上のグラフは、$3\,\text{Hz}$ と $13\,\text{Hz}$ の正弦波がサンプリング周波数 $10\,\text{Hz}$ では同じサンプル値を持つことを示しています。$13 = 3 + 10$ の関係から、$13\,\text{Hz}$ の信号は $3\,\text{Hz}$ のエイリアスとして現れます。
右上は周波数領域の可視化です。サンプリングにより元のスペクトルが $f_s$ 間隔で繰り返され、ナイキスト周波数を超える成分が折り返されて重なる様子が確認できます。
左下はエイリアス周波数のマッピングです。入力周波数がナイキスト周波数を超えると折り返され、$f_s$ の整数倍で周期的にマッピングされることが分かります。
右下はアンチエイリアシングフィルターの効果です。$40\,\text{Hz}$ 成分(ナイキスト周波数 $25\,\text{Hz}$ を超える)をフィルターで除去してからサンプリングすることで、エイリアシングを防止できることが確認できます。
まとめ
本記事では、エイリアシングの数学的メカニズムと対策について解説しました。
- サンプリングされた信号のスペクトル: $X_s(f) = f_s \sum_{k=-\infty}^{\infty} X(f – kf_s)$(元のスペクトルの繰り返し)
- エイリアシングの発生条件: $f_s \leq 2f_{\max}$ のとき、スペクトルの折り返しが発生
- サンプリング定理: $f_s > 2f_{\max}$ で完全復元可能
- アンチエイリアシングフィルター: サンプリング前にナイキスト周波数以上を除去
- 画像処理: ダウンサンプリング前にガウシアンフィルター等の適用が必要
次のステップとして、以下の記事も参考にしてください。