【試行錯誤】「〇〇で歌ってみた」動画の自動生成 その３：動画生成に必要な替え歌情報の整理

# 概要
前回までで、MusicXMLから歌詞とタイムスタンプを抽出し、元歌詞と対応付けることができました。今回はこの情報に基づいて動画生成に必要な替え歌情報を整理し、最終的に以下のようなファイルを得ることを目指します。


```
  parodytext  start  duration     originaltext           picture_path
0        マルタ    2.4       1.2  春が来た　春が来た　どこに来た      Flag_of_Malta.png
1      ニカラグア    3.6       2.4  春が来た　春が来た　どこに来た  Flag_of_Nicaragua.png
2        イラン    6.0       1.2  春が来た　春が来た　どこに来た       Flag_of_Iran.png
3      コロンビア    7.2       4.2  春が来た　春が来た　どこに来た   Flag_of_Colombia.png
4      ハンガリー   12.0       1.2  山に来た　里に来た　野にも来た    Flag_of_Hungary.png
```

これまでのシリーズ一覧は以下です
[【試行錯誤】「〇〇で歌ってみた」動画の自動生成 リンクまとめ](https://qiita.com/shimajiroxyz/items/5df23a0ca81a3a2d9568)

# 背景
次回以降で静止画、動画を効率よく作るため、必要な情報を整理します。
具体的には
* データ化する情報の決定
* 情報のデータフレーム化

を行います。

# データ化する情報の決定

今回は以下のような静止画のつなぎ合わせで動画を作ることにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259353/5bd58bbe-c23f-1203-9196-d2faadd2dbd2.png)

静止画では上から順に画像、替え歌歌詞、元歌詞を並べています。また動画を作る際にはその静止画をどのタイミングから何秒間表示するかも決定する必要があります。
したがって１つの画像に対して

* 替え歌歌詞
* 元歌詞
* 画像（のパス）
* 開始時刻
* 持続時間

が求められればよいです。

# 事前準備
## 替え歌単語に関連する画像
動画自動生成にあたっては画像とそのファイル名は事前に得られているものとします。
具体的には画像ファイルをダウンロードしておいたうえで、以下のような単語とファイル名の対応表があればよいです。

```:countryname_to_filename.csv
アイスランド,Flag_of_Iceland.png
アイルランド,Flag_of_Ireland.png
アゼルバイジャン,Flag_of_Azerbaijan.png
ターリバーン,Flag_of_the_Taliban.png
...
```

練習として、今回は国名の替え歌を作る想定でやってみます。
以下などを参考に国旗の画像をスクレイピング等で入手し、国名とファイル名の対応表を作っておきます。

参考：[Wikipediaの国旗画像を一括ダウンロードする【Python】【BeautifulSoup】](https://qiita.com/shimajiroxyz/items/bebcdb06b47c6b399063)
の情報が必要です。

## 替え歌と元歌詞の対応
替え歌も今回は別途作成しておく前提とします。
以下のように、替え歌の各単語と、単語に対応する元歌詞のモウラが必要です。

```Python
# 替え歌歌詞
original_lyric = [ ("は","る","が"), ("き","た", "は", "る", "が"), ("き","た"), ("ど","こ","に","き","た")
  , ("や","ま","に"), ("き","た","さ","と","に"), ("き","た", "の"), ("に","も"), ("き","た")
  , ("は","な","が"),("さ","く","は","な","が"),("さ","く"),("ど","こ","に","さ","く")
  , ("や","ま","に","さ"),("く","さ"),("と","に","さ","く"),("の","に", "も","さ","く")]
parody_lyric = ['マルタ', 'ニカラグア', 'イラン', 'コロンビア',
 'ハンガリー', 'シンガポール', 'フィンランド', '日本', 'キューバ',
 'カナダ', 'アルバニア', 'タイ', 'ホンジュラス',
 'ジャマイカ', 'ブータン', 'モーリシャス', 'ポルトガル']
```

替え歌は[Soramimic](https://soramimic.com/)などを活用すると作れます。
上記のような対応が分かる形での変数を毎回手打ちするのは大変なため、もう少し人間が書きやすい記法から上記を自動生成したいですが、今は先に進みたいので、今後の課題とします。

## 元歌詞の歌唱タイミング
MusicXMLから元歌詞の歌唱タイミング（１モウラ単位の歌詞とその開始時間、持続時間）を取得しておきます。これは「その２」でやりました。

以下の情報をデータフレームに読み込んでおきます。

```:output/sample1_timestamp.csv
mora,mora_id,word_id,word_surface,phrase_id,mora_hiragana,start,duration
ハ,0,0,春,0,は,2.4,0.6
ル,1,0,春,0,る,3.0,0.3
ガ,2,1,が,0,が,3.3,0.3
キ,3,2,来,0,き,3.5999999999999996,0.6
タ,4,3,た　,0,た,4.199999999999999,0.6
...
```

```Python
timestamp_df = pd.read_csv("./output/sample1_timestamp.csv")
```

# 替え歌情報のデータフレーム化
## 元歌詞フレーズの取得
データフレーム化にあたって、各替え歌単語と一緒に表示する元歌詞を決定する必要があります。
冒頭で図示したように今回は元歌詞はフレーズ単位で静止画に表示したいので、タイムスタンプ情報からフレーズ単位の元歌詞を復元します。

```Python
# phrase文字列を復元
phrases = []
last_word_id, last_phrase_id = -1, -1
for index, row in timestamp_df.iterrows():
  word, word_id, phrase_id = row["word_surface"], row["word_id"], row["phrase_id"]
  if phrase_id != last_phrase_id:
    phrases.append(word)
  elif phrase_id == last_phrase_id and word_id != last_word_id:
    phrases[-1] += word
  last_phrase_id = phrase_id
  last_word_id = word_id
print(phrases)
```

```
['春が来た\u3000春が来た\u3000どこに来た', '山に来た\u3000里に来た\u3000野にも来た', '花がさく\u3000花がさく\u3000どこにさく', '山にさく\u3000里にさく\u3000野にもさく']
```

## 替え歌歌詞（単語）の開始時間、持続時間の取得
替え歌歌詞と元歌詞のmora_idの対応に基づいて、替え歌歌詞の表示開始時刻、表示持続時間を計算します。

```Python
mora_id = 0
rows = []
for parodyword_id, (originalword, parodyword) in enumerate(zip(original_lyric, parody_lyric)):
  
  parodyword_start_sec = timestamp_df["start"][mora_id]
  parodyword_end_sec = timestamp_df["start"][mora_id+len(originalword)-1] + timestamp_df["duration"][mora_id+len(originalword)-1]
  parodyword_duration_sec = parodyword_end_sec - parodyword_start_sec

  phrase_id = timestamp_df["phrase_id"][mora_id]
  
  row = {
    "parodytext": parodyword
    , "start": parodyword_start_sec
    , "duration": parodyword_duration_sec
    , "originaltext": phrases[phrase_id]    
  }
  rows.append(row)

  # mora_idを更新
  mora_id += len(originalword)

parodyinfo_df = pd.DataFrame(rows)
print(parodyinfo_df.head())
```

```
  parodytext  start  duration     originaltext
0        マルタ    2.4       1.2  春が来た　春が来た　どこに来た
1      ニカラグア    3.6       2.4  春が来た　春が来た　どこに来た
2        イラン    6.0       1.2  春が来た　春が来た　どこに来た
3      コロンビア    7.2       4.2  春が来た　春が来た　どこに来た
4      ハンガリー   12.0       1.2  山に来た　里に来た　野にも来た
```

## 替え歌歌詞と画像ファイルの対応付け
先に作成した替え歌情報（`parodyinfo_df`）にファイル名を足します。

```Python
# 画像パスを追加
figinfo_df = pd.read_csv("countryname_to_filename.csv",names=["name", "file"])
# 名前とファイル名の対応が取りやすいように辞書形式にする
figinfo_dict = {k:v for k,v in zip(figinfo_df["name"], figinfo_df["file"])}

parodyinfo_df["picture_path"] = parodyinfo_df["parodytext"].map(figinfo_dict)
parodyinfo_df.to_csv("output/parodyinfo.csv",index=False)
print(parodyinfo_df.head())
```

```
  parodytext  start  duration     originaltext           picture_path
0        マルタ    2.4       1.2  春が来た　春が来た　どこに来た      Flag_of_Malta.png
1      ニカラグア    3.6       2.4  春が来た　春が来た　どこに来た  Flag_of_Nicaragua.png
2        イラン    6.0       1.2  春が来た　春が来た　どこに来た       Flag_of_Iran.png
3      コロンビア    7.2       4.2  春が来た　春が来た　どこに来た   Flag_of_Colombia.png
4      ハンガリー   12.0       1.2  山に来た　里に来た　野にも来た    Flag_of_Hungary.png
```

これで必要な情報が整理できました。
最後に出力した`parodyinfo.csv`を目検でみて、誤りがないかを軽くチェックします。
対応表を参考記事に沿って作った場合、「タイ」のファイル名がうまく取得できていないかもしれません。対応表では「タイ」の国名が「タイ王国」と記述されているためです。このような表記ゆれによる紐付け漏れを防ぐには単語をidで管理すればよいのですが、今後の課題とします。
今回は手動で修正することにします。

# おわりに
以上で、静止画、動画を作るための替え歌情報を整理し、データフレーム化しておくことができました。
今回は替え歌単語と元歌詞フレーズを対応付けましたが、作成する動画の種類によっては替え歌単語もフレーズの長さで区切りたいなどバリエーションがあると思います。そのときは今回のコードを少し変えれば実装できるかと思います。

少し物足りないですが、次（静止画の作成）もそれなりにボリュームがあるので、ここで区切りとします。