機械学習で用いる顔画像データセットの一作り方（２：動画をフレーム解析して候補画像取得）

[前回](http://qiita.com/ligerbolt/items/aeaa88980030fba940b6)はBing Image Search APIを用いた候補画像の収集方法を紹介しましたが、
今回は動画をフレーム解析して収集する方法を紹介します。

#今回用いた開発環境
+ MacOS X El Capitan 10.11.4 
+ Python 3.5
+ OpenCV 3.1

#動画から候補画像を抽出するメリット
動画は基本的にパラパラ漫画と同じ仕組みで、静止画像が短時間内に切り替わることで動きを表現しています。
この時動画を構成する静止画像をフレーム画像、動画を構成している単位時間あたりのフレーム画像数を
フレームレートと言い、fps（1秒間にどれだけのフレーム画像数が使われているか）という単位で表現しています。
※一般的に国内のテレビ放送（信号規格NTSC） は、１秒間に約３０フレーム画像（29.97fps）で構成されています。

つまり動画から顔が映ったシーンのフレーム画像を抽出すれば、そのシーンが短い動画からでもある程度の候補画像が確保できます。
またDVD等の動画は被写体の顔がカメラ目線で撮影したものが多く、また光量等の補正も適切なものが多いため、
動画から抽出された候補画像からは学習に適した顔画像を抽出できる可能性が高いのもメリットの一つです。

今回は前回も利用したOpenCVを用いて、動画から候補画像となるフレーム画像を抽出したいと思います。


##QuickTime Playerでキャプチャ動画取得
今回は無料ツールのQuickTime Playerを用いて動画をキャプチャすることにします。

わざわざ動画をキャプチャすることに不便を感じるかもしれませんが、QuickTime Playerでキャプチャ出力した.mov形式の動画はOpenCVでファイル読み込みがスムーズに出来る利点があります。
（もしOpenCVで読み込み可能な動画が既にある場合には、この手順は飛ばしてください。）

またDVD等長時間の動画から全フレーム画像を抽出する場合、その数は膨大になる上に全てのフレーム画像に顔が適切に映っているとは限りません。そこで動画から顔が映っている映像シーンを部分的にキャプチャすることで、効率良く顔の映ったフレーム画像を抽出する狙いもあります。
更にこの方法を用いれば、web上で公開されている動画像からも候補画像を抽出することができます。

**※※※　注意　※※※**
**今回キャプチャする動画はあくまで機械学習での用途を前提にしております。**
**キャプチャした動画を再配布したりすることは絶対お控えください。**
**またweb上に掲載されてている動画等をキャプチャする際には、**
**動画を掲載している各動画サイトごとの規約などに触れない範囲で行ってください！**


キャプチャ方法に関しては[公式サイト](https://support.apple.com/ja-jp/HT201066)の「画面を録画する」に詳細な説明が掲載されているので、
ここでは説明を割愛します。
QuickTime Playerをダウンロード・インストールの必要が有る際には、[こちら](https://support.apple.com/ja_JP/downloads/quicktime)からどうぞ。
なおキャプチャした動画に音声は一切入りませんが、今回は音声は必要ないので問題ありません。
　
##OpenCVでキャプチャ動画から候補画像となるフレーム画像取得
キャプチャ動画を生成したら、それより候補画像となるフレーム画像を抽出します。
フレーム画像を抽出し、候補画像として保存するコード例は以下のとおりです。

```python
# -*- coding: utf-8 -*-

import cv2


def movie_to_image(num_cut):

    video_path = '/hoge/hoge.mov'   # キャプチャ動画のパス（ファイル名含む）
    output_path = '/hoge/save/'  # 出力するフォルダパス
    
    # キャプチャ動画読み込み（キャプチャ構造体生成）
    capture = cv2.VideoCapture(video_path)

    img_count = 0  # 保存した候補画像数
    frame_count = 0  # 読み込んだフレーム画像数

	# フレーム画像がある限りループ
    while(capture.isOpened()):
    	 # フレーム画像一枚取得
        ret, frame = capture.read()
        if ret == False:
            break

        # 指定した数だけフレーム画像を間引いて保存
        if frame_count % num_cut == 0:
            img_file_name = output_path + str(img_count) + ".jpg"
            cv2.imwrite(img_file_name, frame)
            img_count += 1
            
        frame_count += 1

    # キャプチャ構造体開放
    capture.release()


if __name__ == '__main__':    
	
	# 間引き数を10にしてフレーム画像抽出
	movie_to_image(int(10))
			
```

`movie_to_image`メソッドに引数として与えているのは、フレーム画像を間引く数です。
上記でも説明しましたが長時間の動画の場合、全てのフレーム画像を候補画像として保存すると膨大な枚数になってしまいます。
また動画中のモーションが極めて遅いと、ほぼ同じ構成の顔フレーム画像を何枚も生成することになるために
場合によってはそれを嫌う状況もあるかと思います。
そこで保存するフレーム画像をある程度間引くことで、それらの問題を解決します。

よってフレームレートが29.97fpsの動画の場合、
間引き数を`30`に設定すれば動画内における一秒間隔で約一枚のフレーム画像が取得可能となります。
　
##webカメラを用いたリアルタイムでの候補画像取得
上記コードではキャプチャした動画ファイルより候補画像を生成しましたが、このコードを少し変えるだけで
webカメラ等で動画を撮影しながらリアルタイムで候補画像を生成することも出来ます。

例えばMacBook Airに標準搭載されているインカメラを用いるには、
上記コードを以下のように変更するだけで可能となります。

```
	#capture = cv2.VideoCapture(video_path)
	
	# カメラデバイス番号を与える
	capture = cv2.VideoCapture(0)
```
　
他にもUSB接続のwebカメラ等も使えますが、カメラによってはデバイス認識されず使えないケースも出てきます。
※ 筆者のMBA環境では内蔵インカメには`0`、USB接続カメラ（バッファロー製）では`1`以降の
  カメラデバイス番号が振り当てられるのを確認しました。
  
またマシンスペックがある程度ないと撮影しながらのリアルタイム処理が追いつかない場合もあり、
特に全フレーム数を保存する場合にはよりその問題が顕著になる傾向にあります。
その場合には間引き数を調整することで解決してください。
　
　
以上簡単ではありますが、動画のフレーム解析による候補画像収集例の説明でした。