アカデミック

【超初心者向け】シンプルなPeak PickingをPythonで実装してみる。

pythonでpeak pickingをしてみたい!

少しややこしそうなアルゴリズムだけど大丈夫かな?

今回は,Onset Detectionのために用いられるpeak pickingをpythonで実装する方法をお伝えしていこうと思います。また,本記事はpython実践講座シリーズの内容になります。その他の記事は,こちらの「Python入門講座/実践講座まとめ」をご覧ください。

【超初心者向け】python入門講座/実践講座まとめ目次入門講座 1.実行環境 2.文字の出力 3.データ型 4.変数 5.更新と変換 6.比較演算子 7.論理演算子 8.条件分岐 9.リスト...

コーディングに関して未熟な部分がたくさんあると思いますので,もし何かお気づきの方は教えていただけると幸いです。また,誤りについてもご指摘していただけると非常に助かります。

お題

pythonでpeak pickingを実装してみよう。

流れ

Peak Pickingは主にOnset Detectionというタスクに用いられます。Onset Detectionはある音響信号から所望の信号が鳴っている時刻を検出するタスクです。一般に,アクティベーションと呼ばれる確率値(所望の信号が鳴っている確率)を求めてから,様々な手法で量子化されることによってOnset時刻は求められます。

Peak Pickingは,アクティベーションを量子化するための手法の1つです。最も単純なpeak Pickingの手法は「閾値処理」です。例えば,アクティベーションの値が0.5を上回っていれば「鳴っている」と判断するという方法です。

しかし,このような閾値に基づく手法には,ある欠点があります。それは,Onsetが連続して現れてしまうという点です。例えば,以下のようなアクティベーションを考えてみましょう。

テスト用のアクティベーション

このようなアクティベーションのオンセット時刻を求めるとします。単純に「0.5を上回っている」という条件でOnsetを検出してしまうと,下のようにオンセットが連続して現れてしまいます。

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

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です