ぴょこりんブログ

裏垢です。

へちょ顔に基づくのぞみ先生の発話部分切り出し。

この記事は、ぴょこりんクラスタ:Re Advent Calendar 2016のために書きました。

概要

前回の続き。 へちょ顔があるフレームに対応する部分のwavデータを切り出しただけ。大したことはしてない。

やったこと

前回の記事にはそんなコードありませんが、何気に保存しておいた前回のデータやらモデルやらを読み出します。

import pickle

faces=pickle.load(open('faces_ep.pkl','rb')) # 動画の必要な部分だけくり抜いたやつ
model=pickle.load(open('nmf.pkl','rb')) # 次元削減に使ったNMFのモデル
kmeans=pickle.load(open('hecho_model.pkl','rb')) # クラスタリングに使ったk-meansの(ry

前回は10フレーム毎にサンプリングしてましたが、今回はちゃんと全部見ます。 その代わりモデル等はめんどくさいので前回サンプリングして学習したものをそのまま使うことにしました。 Normalizeの結果が若干変わってしまう気がするけどまぁ大して変わらないだろうと。

とりあえず各フレームがへちょ顔のクラスタに分類されるか否かを眺めてみる。

dataset=[i.reshape(30*55*3) for i in faces]
from sklearn.preprocessing import normalize
X=normalize(model.transform(dataset))
key=[f(i) for i in kmeans.predict(X)]
are=[i[0] for i in enumerate(zip(key,key[1:])) if i[1][0]!=i[1][1]]

%matplotlib inline # jupyter上でやるときのおまじない。
f = lambda x: 1 if i==2 else 0
plt.figure(figsize=(12,3))
plt.plot(key)
plt.ylim(0,1.5)

f:id:cappsLk:20161212063906p:plain

y軸方向の値が1になってるのがへちょ顔発生フレーム、x軸はフレームのインデックス。発話中へちょ顔は出続けるので、y=1の状態が続いて矩形ができてる区間が音声を発してるところだと解釈できます。Chapter.1の動画のプログレスバーと比較するとあーそんな感じかーってなるかもです。 変数名が本当に雑で最悪ですがこのまま行きます。

そういえば前回の記事でのぞみ先生が跳ねることがわかったので、その対策が必要でした。大雑把に10フレーム未満のへちょ顔が発生しない区間はその両サイドのへちょ顔発生区間とマージするという雑な処理をかけます。

voiceframes=[are[i:i+2] for i in range(0,len(are),2)]
voiceframe2=[]
flag=False
for i in range(len(voiceframes)-1):
    if flag:
        flag=False
        continue
    if voiceframes[i+1][0]-voiceframes[i][1]<10:
        voiceframe2.append([voiceframes[i][0], voiceframes[i+1][1]])
    else:
        voiceframe2.append(voiceframes[i])
        flag==True

該当区間のwav書き出し、予めmp4から抽出したwavデータを用意しておきます。

n=len(faces)

for i,j in enumerate(voiceframe2):
    test=wave.open('./sanarara/Chap01.wav','r')
    m=test.getnframes()
    params=test.getparams()
    data = test.readframes(m)
    are=len(data)
    write = wave.Wave_write("./"+str(i)+".wav")
    p = list(params)
    p[3]=(int(are*(float(j[1])/n))-int(are*(float(j[0])/n)))/4
    p=tuple(p)
    write.setparams(p)
    write.writeframes(data[int(are*(float(j[0])/(2*n)))*2:int(are*(float(j[1])/n))])
    write.close()

areとかいう変数再利用していて最悪ですね(今気づいた)。

ちなみにpythonのwavモジュール、普通にストリング型で16進数っぽい何かを吐いて来るみたいな低レイヤっぽい仕様になっていて、区切りどころを間違えると普通に最高なのぞみ先生の声がひどいノイズと化します。なんとなく偶数フレームで切ったら一応ちゃんと聴けるものが保存出来たのでそういう感じでやったけれど、それが正しい運用とも到底思えないので、めんどくさがり屋さんは適切なラッパー探してくるのが良いと思いました。僕も次回からそうします*1

ちなみに今回の方法は、へちょ顔でのぞみ先生の発言を検知しているので、冒頭の暗いシーンや、のぞみ先生登場直前のへちょ顔が現れない発言は検知することが出来ません。Chapter.1からは20つのセリフが切り出せました。挙動不審なのぞみ先生もかわいい。

まとめ

のぞみ先生はかわいい。

*1:次回?