Deep LearningのCVPR(Computer Vision and Pattern Recognition)領域ではビデオや画像を扱うことがほとんどです。その際に頻出するffmpegの使い方です。
ffmpeg(https://www.ffmpeg.org/)は特にUnix環境で動画や音声の処理を行う上で基本的な機能を網羅したアプリケーションで、Unix環境ではデファクト・スタンダードです。Pythonパッケージで動画を扱う「ffmpeg-python」や音声を扱う「pydub」などのパッケージライブラリが存在しますが、それらの多くは単なるffmpegのラッパーで、稼働にはffmpeg自体の存在が必須条件となっています。
1. 動画からのフレーム切り出しの例
通常はコマンドラインで使用しますが、Python使用者はJupyter Notebookからffmpegを使う必要に迫られます。その際は下記のように「subprocess」を使って子プロセスとしてコールして動作させます。
mp4ビデオからフレームを10フレームごとに切り出し、jpg画像として保存する場合は下記のように記述します。
import subprocess input_video_name = 'hogehoge.mp4' framestep = 10 output_image_name = 'fugafuga.jpg' command = 'ffmpeg -i '+input_video_name+' -q:v 8 -vf framestep='+str(framestep)+' -an -vsync 0 '+output_image_name subprocess.call(command, shell=True)
なお、Kaggle Competitionでこのように動画から画像を切り出す処理をする場合、保存された画像が大量になってHDDの使用限度を超えてしまう可能性があります。その場合は上記の例のように「-q:v」オプションを使って保存する画像の画質を下げて(1が最高で、上記では8に設定)、使用するHDD要領を下げることができます。このように保存した画像は往々にしてObject Recognition(物体認識)にかけることになるわけでしょうが、実際にOpenCVで両方の画質の画像にFacial Recognitionをかけたところ、認識精度は全く落ちませんでした。
2. 動画からの音声切り出しの例
同様に、ビデオから音声を切り出して音声ファイルとして保存する場合にも使えます。
import subprocess input_video_name = 'hogehoge.mp4' output_image_name = 'fugafuga'+'%06d.m4a' command = 'ffmpeg -i '+input_video+' -acodec copy -map 0:1 -vn '+output_audio subprocess.call(command, shell=True)
3. ffmpegじゃなければダメなのか?
私の見解では、ffmpegに代わる十分な機能を持った使い勝手の良いアプリケーションは他にないです。具体例を挙げると、上記で挙げた「動画の切り出し」は全く同じ機能をOpenCVで書くと下記になります。
import cv2 input_video_name = 'hogehoge.mp4' framestep = 10 output_image_name = 'fugafuga' count = 0 cap = cv2.VideoCapture(input_video_name) # 各フレームを切り出して保存 while True: ret, frame = cap.read() if ret == True: count += 1 if count % framestep == 0: cv2.imwrite(output_image_name+str("{0:06d}".format(count))+'.jpg', frame)
ffmpegに比べると処理速度が若干劣りますが、動画の切り出しはOpenCVでもwhileでループを回して簡単に書けるのです。が、音声の切り出しがOpenCVではできません。では代わりのPythonパッケージでできないかと考えると安直にはPydubあたりに行き着きますが、実はPydubはffmpegのラッパーでしかなく、ffmpegのインストールが必要です。それなら素直にffmpegを使うまででしょう。
4. Kaggleでffmpegを使いたい時は?
Kaggleの公開データセットとして登録してあるffmpeg static build on Kaggleを使います。Kaggleでkernelの編集をしている時、右上にKaggle環境内のデータセットを選択してインポートできるGUIがあります。それを使って編集中のkernelにffmpeg static buildをインポートします。
インポートが完了すると、「../input/ffmpeg-static-build/ffmpeg-git-amd64-static.tar.xz」にアーカイブされたffmpegが見えるようになるので、これをふつうに自分の作業ディレクトリ(/kaggle/working/)にアンアーカイブします。Jupyter Notebookで下記を実行します。
!tar xvf ../input/ffmpeg-static-build/ffmpeg-git-amd64-static.tar.xz作業ディレクトリに「ffmpeg-git-20191209-amd64-static」というディレクトリができ、その中にすでに実行形式のffmpegが入っています。
ここで、通常通り「ffmpeg」とコマンドをコールしても、作ったばかりの上記ディレクトリにはパスが通っていないので、下記をJupyter Notebookで実行してパスを追加しておきます。
# add path to ffmpeg above to call it just as 'ffmpeg' import os os.environ["PATH"] += os.pathsep + '/kaggle/working/ffmpeg-git-20191209-amd64-static/' os.environ['PATH']
これで「ffmpeg」とコールするだけでffmpegを使えるようになります。
Kaggleは初回はこのような仕組みを知る必要がありますね。私は非常に苦労して時間を浪費してしまいました。