dcase_utilとsed_evalを使って音響イベント分析をする〜dcase_util編〜

## 内容
音響イベント分析;音響イベント検出(SED; Sound Event Detection)やを行う際にdcase_utilを使って特徴抽出や結果の可視化をしたり，sed_evalを使って評価指標を算出する方法を紹介する．

## 知らない人向け：音響イベント分析とは
自動車の音，人の話し声，鳥の鳴き声など周囲のあらゆる環境音に対し，何の環境音が鳴っているか（識別），またいつ鳴っているか（区間検出）等を機械学習モデルに予測させるようなタスクを音響イベント分析という．
自動運転や見守りシステム，ライフログの自動生成などの要素技術として研究が進められている．

これを見ればだいたいわかると思う．↓
https://www.jstage.jst.go.jp/article/jasj/74/4/74_198/_pdf
井本桂右. "音響イベントと音響シーンの分析." 日本音響学会誌 74.4 (2018): 198-207.

問題設定として，音響シーン識別(Audio Scene Classification), 音響タグ付け(Audio Tagging), 音響イベント検出(Sound Event Detection)の主に3種類がある．

**1. 音響シーン識別**
    1つの分析対象の音声（クリップという単位で語られることが多い）に対し，それがどのような状況か（街中，公園といった場所や料理中，人の会話といったシチュエーション）を識別するタスク．1クリップにつき1ラベル

**2. 音響タグ付け**
    1クリップにどのような音が含まれているかをタグとして付与するタスク．1クリップに複数ラベルつくことも．

**3. 音響イベント検出**
    1クリップにどのような音が**いつ**鳴っているかの区間検出をするタスク．1クリップに複数，時刻つきラベルがつく

イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493056/44a7b4cb-f935-2887-c6ae-ecc1a657ab30.png)
参照：[The Machine Learning Approach for Analysis of Sound Scenes and Events](https://www.semanticscholar.org/paper/The-Machine-Learning-Approach-for-Analysis-of-Sound-Heittola-%C3%87akir/707393141e893a17421358eea739d623de2a0c45)より

## dcase_util

[dcase_util](https://dcase-repo.github.io/dcase_util/)

dcase_utilは音響イベント検出をする際の特徴抽出，イベントの可視化などを楽にやるためのライブラリ．

pipでインストールできる．
```pip install dcase_util``` だけでインストールはおk．

- 特徴抽出
音響イベント分析でよく入力となっている対数メルスペクトログラム（log-mel band power）をお手軽に算出することができる．

```python:extract_raw_audio.py

# 音声ファイルを読み込み, ステレオからモノラルにミックスダウンする
audio_container = dcase_util.containers.AudioContainer().load(
  filename=dcase_util.utils.Example.audio_filename()
).mixdown()

# extractor(特徴抽出器)をインスタンス化
mel_extractor = dcase_util.features.MelExtractor()

# 特徴抽出を行う
mels = mel_extractor.extract(audio_container)
```

librosaなどで音声を読み込んでnumpy.arrayにした場合など，音声波形がnumpy.arrayの場合でもok．

```python:extract_numpy_audio.py

# 例としてnumpy.arrayのsin波を作成
t = numpy.linspace(0, 2, 2 * 44100, endpoint=False)
x1 = numpy.sin(220 * 2 * numpy.pi * t)

# extractor
mel_extractor = dcase_util.features.MelExtractor()

# 特徴抽出
mels = mel_extractor.extract(x1)

# プロット
dcase_util.containers.DataMatrix2DContainer(
    data=mels,
    time_resolution=mel_extractor.hop_length_seconds
).plot()
```

プロットするとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493056/625689ec-4d5a-6e7c-e8b1-3ca027445744.png)


- アノテーションの可視化

MetaDataContainerにイベントに関するアノテーションを辞書型のリストとして渡すと可視化してくれる．

```python:sound_event_visualization.py

''' 
MetaDataContainerには
filename(ファイル名・クリップ名),
event_label(イベントのラベル),
onset（開始時間）,
offset(終了時間)
をキーにもつ辞書型オブジェクトのリストを入れる
'''
event_list = [
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 1.0,
            'offset': 2.0,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 2.05,
            'offset': 2.5,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 5.1,
            'offset': 5.15,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'footsteps',
            'onset': 3.1,
            'offset': 4.15,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'dog',
            'onset': 2.6,
            'offset': 3.6,
        },
    ]

# アノテーションなどのメタデータはMetaDataContainerに渡す
meta_container_events = dcase_util.containers.MetaDataContainer(event_list)

# 可視化のためのイベントロールを作成
event_roll = meta_container_events.to_event_roll()

# プロットする
event_roll.plot()

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493056/c8fdb399-55d6-24f9-251e-4b46abb681f9.png)
こんな感じに可視化してくれる．

- イベントロールとスペクトログラムを並べて表示

両方を並べて表示させたい場合は，メルスペクトログラムをFeatureContainerに格納し，
イベントロールのプロット時にそれをパラメータとして与えるとできる．

```python:plot_spec_and_event.py

# アノテーション
event_list = [
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 1.0,
            'offset': 2.0,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 2.05,
            'offset': 2.5,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'speech',
            'onset': 5.1,
            'offset': 5.15,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'footsteps',
            'onset': 3.1,
            'offset': 4.15,
        },
        {
            'filename': 'file1.wav',
            'event_label': 'dog',
            'onset': 2.6,
            'offset': 3.6,
        },
    ]


# 音声
audio = dcase_util.containers.AudioContainer().load(
  filename=dcase_util.utils.Example.audio_filename()
).mixdown()

# メルスペクトログラム
mel_extractor = dcase_util.features.MelExtractor(
    n_mels=128, 
    fs=audio.fs
)

mel_data = mel_extractor.extract(y=audio)

# 時間解像度はメルスペクトログラムのhop_lengthに
feature_container = dcase_util.containers.FeatureContainer(
    data=mel_data, 
    time_resolution=mel_extractor.hop_length_seconds
)
meta_container_events = dcase_util.containers.MetaDataContainer(event_list)
event_roll = meta_container_events.to_event_roll()

# プロットする
event_roll.plot(
    plot=False,
    panel_title='sample',
    data_container=feature_container,
    figsize=(40, 10),
    data_panel_title='Features (log mel energies)', 
    binary_panel_title='Event Roll',
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493056/a4b98e9d-ea53-c089-1124-4955ef5fc5d6.png)

簡単に結果を可視化できる．

とりあえずここまで．次回はsed_evalを紹介する．