pythonでpeak pickingをしてみたい!
少しややこしそうなアルゴリズムだけど大丈夫かな?
今回は,Onset Detectionのために用いられるpeak pickingをpythonで実装する方法をお伝えしていこうと思います。また,本記事はpython実践講座シリーズの内容になります。その他の記事は,こちらの「Python入門講座/実践講座まとめ」をご覧ください。
お題
pythonでpeak pickingを実装してみよう。
流れ
Peak Pickingは主にOnset Detectionというタスクに用いられます。Onset Detectionはある音響信号から所望の信号が鳴っている時刻を検出するタスクです。一般に,アクティベーションと呼ばれる確率値(所望の信号が鳴っている確率)を求めてから,様々な手法で量子化されることによってOnset時刻は求められます。
Peak Pickingは,アクティベーションを量子化するための手法の1つです。最も単純なpeak Pickingの手法は「閾値処理」です。例えば,アクティベーションの値が0.5を上回っていれば「鳴っている」と判断するという方法です。
しかし,このような閾値に基づく手法には,ある欠点があります。それは,Onsetが連続して現れてしまうという点です。例えば,以下のようなアクティベーションを考えてみましょう。
このようなアクティベーションのオンセット時刻を求めるとします。単純に「0.5を上回っている」という条件でOnsetを検出してしまうと,下のようにオンセットが連続して現れてしまいます。
本来であれば,Onset時刻は1フレーム分に相当します。というのも,Onsetと対応する概念として「Offset」があるからです。Onsetは音の鳴り始めを表しており,Offsetは音の鳴り終わりを表しています。ですので,Onset1フレーム分だけ立つように推定するべきなのです。
以上のことを考慮すれば,閾値処理に加えて「前後数フレームを見て現在のアクティベーションが最大であればPeakとして採用する」というアルゴリズムを採用すれば良いことが分かります。それでは,早速実装に入っていきましょう。このコードを走らせれば,以下のようにピークを検出できると思います。window幅は2に設定しました。
実装
def peakpicking(activation, window, thres):
'''
activation: ndarray
window: torelance frame size
thres: minimum point of the peaks
'''
# peakはこいつに格納していきます。
picked_activation = []
for i in range(activation.shape[0]):
# 端っこは例外処理
if i < window:
if (activation[i] == np.max(activation[0:i+window+1])) and (activation[i] > thres):
picked_activation.append(1.0)
else:
picked_activation.append(0.0)
# 端っこは例外処理
elif i > activation.shape[0] - 1 - window:
if (activation[i] == np.max(activation[i-window:])) and (activation[i] > thres):
picked_activation.append(1.0)
else:
picked_activation.append(0.0)
# 端っこ以外の処理
else:
if (activation[i] == np.max(activation[i-window:i+window+1])) and (activation[i] > thres):
picked_activation.append(1.0)
else:
picked_activation.append(0.0)
return picked_activation