Windowsでffmpegを使ってみた話
はじめに
ffmpegを使ってみた話ですが、あくまでもC++から使ってみようっていう話。
ffmpegのバイナリを使うって話はググれば山ほど出てくるんだけど、Cのライブラリを使うって言う話はあまり出てこなかったので、備忘録を兼ねて。
そもそも、どうしてffmpegを使おうと思い至ったかと言いますと、OpenCVのVideoCaptureってコーデックが揃った外部のffmpegを指定してもフレームのシークや動画情報の取得が上手く行かない場合が多く、そもそもコーデックに対応してないなんて場合もあって微妙に使いづらいという問題があります。(汎用的な機構でいろんな動画フォーマットを開けるというだけで十分便利なのですがね)
それで、ffmpegを直接使えば多少の手間はかかれど、そういう問題はないだろうと思い、実際に試してみました。
準備
いつも通り、Windows環境での話しかしませんので、MacやLinuxの人はパッケージマネージャーで適当に準備してください。
それで、Windows環境の場合ですが一からffmpegをビルドするのは大量のコーデックをビルドする必要があって、かなり骨が折れるので下記からダウンロードしてきましょう。
32bit、64bit、好きな方を選んで、SharedとDevの両方のパッケージをダウンロードしましょう。なぜかはわかりませんが、Devを選択してダウンロードしてくるとリンクするシンボルが含まれたlibファイルはあるのにdllがないのでSharedパッケージに含まれるdllを併せて利用します。
テストコード
下記のサンプルコードを改変してC++で使えるようにしました。ただ、それだけだと面白みが無いので、フレームのシークをできるようにてみました。(正しく動いているとは言ってない)
ffmpeg tutorial
ポイントは、ffmpegのヘッダーを extern "C" で括ってやることぐらいですかね。
ただ、このコード。たまにシーク出来ないことがあるので、おそらくどこか間違ってるんでしょうね。おいおい修正します。
cygwinでFindOpenCVするとエラーになる
やんごとなき事情により、cygwinでcygwinでcmakeを使う必要がでてきたのだけれども、FindPackageでapt-cygでインストールしたOpenCVを探すとエラーが出た。
以下は実際のエラー出力
CMake Error at /lib/cmake/opencv/OpenCVModules.cmake:124 (message):
The imported target "opencv_core" references the file"//lib/libopencv_core.dll.a"
but this file does not exist. Possible reasons include:
* The file was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and contained
"/lib/cmake/opencv/OpenCVModules.cmake"
but not all the files it references.
Call Stack (most recent call first):
/lib/cmake/opencv/OpenCVConfig.cmake:71 (include)
util/CMakeLists.txt:5 (find_package)
それで、その原因となるコードを追っかけると原因はOpenCVModules-relwithdebinfo.cmakeにあった。
以下はその1行目から17行目を抜粋したコード
#---------------------------------------------------------------- # Generated CMake target import file for configuration "RelWithDebInfo". #---------------------------------------------------------------- # Commands may need to know the format version. set(CMAKE_IMPORT_FILE_VERSION 1) # Import target "opencv_core" for configuration "RelWithDebInfo" set_property(TARGET opencv_core APPEND PROPERTY IMPORTED_CONFIGURATIONS RELWITHDEBINFO) set_target_properties(opencv_core PROPERTIES IMPORTED_IMPLIB_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libopencv_core.dll.a" IMPORTED_LINK_INTERFACE_LIBRARIES_RELWITHDEBINFO "" IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/bin/cygopencv_core-2.4.dll" ) list(APPEND _IMPORT_CHECK_TARGETS opencv_core ) list(APPEND _IMPORT_CHECK_FILES_FOR_opencv_core "${_IMPORT_PREFIX}/lib/libopencv_core.dll.a" "${_IMPORT_PREFIX}/bin/cygopencv_core-2.4.dll" )
つまるところ/lib/opencv_core.dll.aに探したいライブラリがあるんだけど、_IMPORT_PREFIXが/の場合//lib/opencv_core.dll.aに成ってしまっているというのが問題です。本来Linuxでは/をいくつ重ねても許容されますが、cygwinでは//がWindows環境における\\と同一の意味として解釈されるため、libとういう名前のサーバー無いためエラーとなります。
というわけで、このコードの8行目に以下のコードを追加して末尾の/を削除するように変更。
string(REPLACE "/" ";" _IMPORT_PREFIX_TEMP ${_IMPORT_PREFIX}) list(LENGTH _IMPORT_PREFIX_TEMP _IMPORT_PREFIX_LENGTH) SET(_IMPORT_ITERATION_COUNT 2) while(${_IMPORT_ITERATION_COUNT} LESS ${_IMPORT_PREFIX_LENGTH}) math(EXPR _INSERT_POSITION "${_IMPORT_ITERATION_COUNT} - 2") list(INSERT ${_INSERT_POSITION} ${_IMPORT_ITERATION_COUNT} "/") math(EXPR _IMPORT_ITERATION_COUNT "${_IMPORT_ITERATION_COUNT}+1") endwhile() string(CONCAT _IMPORT_PREFIX ${_IMPORT_PREFIX_TEMP}) unset(_INSERT_POSITION) unset(_IMPORT_PREFIX_TEMP) unset(_IMPORT_PREFIX_LENGTH) unset(_IMPORT_ITERATION_COUNT)
結構アドホックな気がするけど、急ぎの案件だったのでとりあえず、これで解消しました。
同じ症状に陥って、他にこうしたらなんかCMake通ったよみたいな人いたら教えて欲しいです。
DLIB with CUDAでCNNを使ってみた in Windows
DLIBのコンパイル
DLIBのCUDAを有効にするためには、CUDA 8.0(RC版じゃないですよ)とcuDNN 5.0以上が必要です。また、Windowsも64bit環境でなければならないので注意してください。
まず、cuDNNは下記のURLからダウンロードしてください。バージョンは5.0でも5.1でもどっちでも構いません。
cuDNN
developer.nvidia.com
このcuDNNを適当な所に解凍し、さらに以下のCMakeを実行します。
cmake -DCOMPILER_CAN_DO_CPP_11=ON -DCMAKE_PREFIX_PATH=<cuDNNのルートディクレトリ> <dlibのルートディレクトリ>
ただし、このままだとコンフィギャレーションエラーが出るので、以下の様に"dlib/cmake_utils/test_for_cudnn/find_cudnn.txt"を以下のように書き換えます(githubのリポジトリにある最新版のdlibを使う場合、このバグは修正済みなので飛ばしてOK)。
message(STATUS "Looking for cuDNN install...") # Look for cudnn, we will look in the same place as other CUDA # libraries and also a few other places as well. find_path(cudnn_include cudnn.h HINTS ${CUDA_INCLUDE_DIRS} ENV CUDNN_INCLUDE_DIR ENV CUDNN_HOME PATHS /usr/local ENV CPATH PATH_SUFFIXES include ) get_filename_component(cudnn_hint_path "${CUDA_CUBLAS_LIBRARIES}" PATH) find_library(cudnn cudnn HINTS ${cudnn_hint_path} ENV CUDNN_LIBRARY_DIR ENV CUDNN_HOME PATHS /usr/local /usr/local/cuda ENV LD_LIBRARY_PATH PATH_SUFFIXES lib64 lib x64 ) mark_as_advanced(cudnn cudnn_include)
これでコンパイルすれば、無事CMakeも通ると思います。
結果
推定精度は前回と同じなんだけど、実行時間(学習時間)が爆速になりました。
以前は学習に2日程かかっていたものが20sで終わるようになりました(GTX 970で実行)
まあ、DLIBではそもそもCPUにおける並列化すらしていないようなので当たり前といえば当たり前ですが。
CUDA 8.0がいつの間にかリリースされててVisual Studio 2015 Update 3でも使えるようになっていた話
CUDA8.0 RCがリリースされたのが4月頃だったと思うが、それから半年。CUDA8.0がいつの間にかリリースされていた。
ダウンロードは下記から。
さて、このCUDA 8.0の何がスゴイかと言うとCuda8.0 RC では未対応だったVisual Studio 2015 Update 2以降に対応しているという点だ。
これで、DLIBをコンパイルできれば、Windows環境でGPGPUを使ったDNNがまともに使えるかもしれない。
GLEWをVisual Studio 2015とCMakeでビルドするときの注意点
GLEWをCMakeでVisual Studio 2015向けのビルドファイルを生成してDebugビルドするとリンクエラーがでる。
実際にリンクに失敗する原因となったシンボル名について検索するとGLEWのgithubのissueが見つかった。
cmake: Cannot build glew32d.dll with Visual Studio 2015 · Issue #99 · nigels-com/glew · GitHub
曰く、Visual Studio 2015以降では/RTC1オプションをオンにした状態だとリンクエラーが出るそうで、無効(Default)にすることを推奨しているそうです。
ちなみに/RTCオプションとはランタイムエラーチェックに関するオプションだそうで、変数のメモリ配置においてReleaseビルドを想定したDebugを行えるようにするオプションだそうです。
/RTC (ランタイム エラー チェック)
DLIBでCNNを使ってみた in Windows
はじめに
今回は前回コンパイルしなおしたDLIBを使ってWindows環境のC++(これ重要)でCNNを使おうぜって話。
ほぼサンプル通りだけど、コメントは日本語化しましたので解説していきます。
ソースコードは全て以下に全部上げてるんでよしなに。
READMEはいずれ更新します。
なお、私は趣味で機械学習に触れているので、間違えていることや厳密性を欠いていることがままあります。
もし、間違えているところがありましたら、コメント等いただけると幸いです。
What is CNN
CNNとは深層学習の一種でConvolutional Nueral Networkのこと。
中間層でフィルタ処理(畳み込み処理)をするからそう呼ばれているそうです。
(厳密には、フィルタ処理というわけではないそうですが、忘れたんで適当にググれば山ほど情報は出てきます)
今回取り扱うのはLeNetと呼ばれるもので全7層(入力層除く)から成るCNNとなっています。
解説
以下は TestDNN/TestDNN.cpp の中身となっています。
[1] 学習データの読み込み
今回使うデータはMNISTの手書き文字となっています。
データは以下からダウンロードした4つのファイルを"./Train"以下に保存してください。
MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges
[2] CNNの定義
これがおそらく一番の難所。
実際にCNNを定義するんだけれども如何せん読みづらい。
dlibにおいてDNNの構造を定義する時は最も内側にあるテンプレート引数が入力層で,loss_multiclass_logのすぐ内側にあるテンプレート引数が出力層にあたる。
ちなみに以下の場合、dlib::input
ここで、dlib::input<>が入力のためのインターフェースで、デフォルトでは dlib::array2d とdlib::matrix型に対応している。
// [2] CNN の定義 // CNN の定義 // A<B<C>> となっている場合 Cが入力で,Bが中間層,Aが出力層 // なので,出力はfcにするのがいいと思う. // fc<10, ...>:Fully connected layerでノード数は10 // relu:活性化関数の名前.詳しくはReLUで調べてください // con<16,5,5,1,1,SUBNET> で5✕5のフィルタサイズを1✕1のstrideで畳み込みするノードが16個ある // max_pool<2, 2, 2, 2, SUBNET> 2✕2のウインドウサイズで2✕2のstrideでプーリングを行う. // relu<fc<84, ...>> この場合活性化関数がReLUで84ノードからなる層を定義している. // max_pool<2,2,2,2,relu<con<16,5,5,1,1,SUBNET>>> これでconvolutionした結果をReLU関数で活性化してそれをMax poolingする // input<array2d<uchar>> cv_image<uchar>を入力に取る.現在cv::Matを入力に取れるように試行錯誤中 using net_type = dlib::loss_multiclass_log< dlib::fc<10, dlib::relu<dlib::fc<84, dlib::relu<dlib::fc<120, dlib::max_pool<2, 2, 2, 2, dlib::relu<dlib::con<16, 5, 5, 1, 1, dlib::max_pool<2, 2, 2, 2, dlib::relu<dlib::con<6, 5, 5, 1, 1, dlib::input<dlib::array2d<uchar>> >>>>>>>>>>>>; // 上のCNN場合 // -FC-> : Fully connectedな接続 // -> : 重みを共有した接続 // 入力画像->[6ノードの畳み込み層]->プーリング層->[16ノードの畳み込み層]->プーリング層-FC-> ... // [120ノードの普通のNN]-FC>[84ノードの普通のNN]-FC>出力(10次元ベクトルで各次元に各数字の確率が保存される)
ここで,サンプルに登場している各クラスについてはそれぞれ以下の通りになっている。
- loss_multiclass_log:おそらく損失関数。
- fc: Fully Connected layer のこと。つまり、接続する層同士で全てのノードが接続されている状態。
- relu:活性化関数の一種で、ReLU関数のこと。制御系だとランプ関数と呼ばれるもので、で表される。
- max_pool:Max pooling による重みのリサンプリング
- con:畳み込み層
- input:DNNの入力のTraits。dlibではデフォルトでarray2dとmatrix型につちえ実装がなされている。
上記の用語についてわからない用語があれば以下のQitaのエントリーが詳しいと思う。
[3] 学習器の設定
これはなんてことないですね。ただの学習器の設定です。
今回は学習に全ての画像を読み込んでいるので何も考えずにtrainメソッドを呼べばOKです.
[4] 学習結果の保存
DLIBのサンプル曰く、保存する前に一度clearメソッドを呼べとのことです。詳しい理由は書いていなかったのですが、これはポイントなので外さないようにとのことでした。
実行結果
以下が実行結果
かなり高い識別率がでていることがわかる。
正答数 | 誤答数 | 総数 | |
学習済みデータ | 59985 | 15 | 60000 |
非学習済みデータ | 9914 | 86 | 10000 |
おわりに
今回学習に2日近くかかっているので、やっぱりCUDAは欲しいなと思う。
ただ、現状Windows環境でCUDAを使うことが難しいのが本当に辛い。
C++でDNNが使えないなんてあるわけないよ in Windows
一ヶ月くらいクソ忙しかったせいで久々の更新。
書くネタは山ほどあるんだけど、またその内、連続で書きます。
はじめに
Windows環境のC++(これ重要)でDNN(Deep Nueral Network)を使おうぜって話。
DNNといえば最近流行りのものなんだけど、如何せんWindows環境。特にC++となるとグッと道が狭まる傾向にある。
正直嫌がらせでも受けてるんじゃないんですかねって言うくらいに。
実際、有名な所でGoogleのTensol Flowがあるけれども、あれもMacとLinuxの64bitしか対応していない。
それで色々とC++で使えるDNNののライブラリを調べてみたわけです。
個人的な要望として、GPGPUによる高速化が成されているものがベストかなと思っています。
Tensor Flow
Googleが出した商用フリーの機械学習ライブラリで、なんかすごいらしい。
曰く、状態遷移図で表現できるあらゆる問題を解けるとか凄い話を聞いたことがある。
私は現在Windows以外のまともな環境が無いので使ったことはないけれども、学習の経過をビジュアライズまで自動でやってくれて、その結果をブラウザで確認できるのがすごく魅力的。
ただし、前述の通りWindowsでは使えないのです。
OpenCV
おそらく最も有名無い画像処理/コンピュータビジョンのライブラリ。
なのだけれども、いくら探してもOpenCVだけでDNNを使うという話が出てこない。
より正確に言うとC++とOpenCVだけを使ってDNNを使うという話が出てこない。
実際に「OpenCV DeepNeuralNetwork」で検索しても上から10個が全てCaffeのモデルを読み込んでみた、という内容のものばかりだし、OpenCVのサンプル一覧を見てもそれらしいものが見あたらなかった。
DLIB
以前記事をかいたけれども、機械学習ライブラリでその中にDNNも含まれており、もちろんGPGPUを用いた高速化も可能。
なのだけれども、残念なことにWindows環境だとリンクエラーがでる。
FAQ曰く、Visual Studioの場合C++11に完全に対応しているバージョンがないので未対応とのこと。
さらに、こいつ。厄介なことに全てのパラメータがテンプレートで解決されるため、動的にパラメータを変えてどうのこうのっていうのはやり難い。
まあ、最近はAutoEncoderかなにかで事前学習して層数とかを決めるらしいからそれもあまり問題にならないのかもしれないけれども。
Tiny DNN(https://github.com/tiny-dnn/tiny-dnn)
非常にスマートなライブラリで、Windowsに対応している素敵ちゃん。
必要なライブラリとしてOpenCVを要求していることは入力インターフェースにOpenCVを使っていると思われる。
特徴としては、DLIBと違ってパラメータを動的に決定することが可能。
ただし、CPUでの学習しか対応していないので、その辺りはネックに成るかもしれない。
えっ、無くね?って話
Tiny DNNが一番良いのだけれども、GPGPUによる高速化がないと学習が終わる気しない。
Tiny DNNのトップページ曰く、
98.8% accuracy on MNIST in 13 minutes training (@Core i7-3520M)
とのことなので、6000サンプル10クラスよりも多いクラスに分けたり、サンプル数を増やしたりとしていると爆発的に計算時間が掛かりそう、というか掛かるのでやっぱりGPGPUが良い。
DLIBさんのDNNってなぜに動かないのん?
FAQに書かれている内容は以下の通りだが、どうにも納得できない。
The deep learning toolkit in dlib requires a C++11 compiler. Unfortunately, as of July 2016, no versions of Visual Studio fully support C++11, so not all the deep learning code will compile. However, all the other modules in dlib can be used in Visual Studio without any trouble.
確かに、Visual Studio 2015は結構C++11の中で対応していない機能がある。
ただ、それもUpdate 2でかなり解消されている。
それで、実際にソースコードを読み漁ってみてもどこにもそれらしいコードは見られなかった。
というわけで無理やりコンパイルしてしまえという結論に至りました。
DLIB with DNN in Windows
DLIBをDNNが使える状態でVisual Studioにてコンパイルするためにはcmakeをするときに以下のオプションを足せばOKです。
cmake -DCOMPILER_CAN_DO_CPP_11 <dlibのルートディレクトリ>
これで後は普通にコンパイルするだけです。
実際にライブラリをリンクしてコンパイルしても問題なく動作することを確認しています。
ただし、未だnvccでコンパイルが通るかどうか確認していません。
というのもCUDA8.0 RC ですらVisual Studio 2015 Update 1までしか対応していなくて、現在、私が持っている環境はUpdate2とUpdate3なので、両方共CUDAをONにしてdlibのコンパイルを出来ていません。
なので、上記の話は全てCPUで使う場合にのみ有効です。
CUDAについては、おいおい動作確認をしていきます。
それとサンプルコードも実はすでに書いたのですが、それはまた後日別記事であげますので悪しからず。
追記
今朝方、上記の方法にてDLIBをVisual Studio 2015 Update2でコンパイルしてみるとコンパイルに失敗したので、おそらくCUDAを使うことは難しそうです。
追記2
私がこの記事を上げてから、ほぼ同時か、翌日くらいにgithubにissueが上がっていたので追記。
どうやらdlib::repeatを使ってDNNのノードを定義すると、テンプレートの展開で無限ループにハマるらしくVS2015 Update3ではまだDNNを使えないようにしているらしい。
追記3
実際にDLIBを使ってDNNを使ってみたサンプル&解説記事を書きました。
elda27.hatenablog.com