【Caffe】C++でCaffeをAPI的に使う
前回の記事でWindows環境でCaffeを利用できるようにした。これを別プロジェクトからAPI的に利用したかったので実施した。実力不足で不恰好になっているため、改善方法は継続して調査。作成したプログラムは少し重いがGitHubで公開中なので、本ページの最後から辿ってもらいたい。
0.方針
本来ならlibcaffeプロジェクトで作成するlibファイルを別プロジェクトから参照できるようにすべきだが、どうしてもVisualStudioでの設定が出来なかった。そのため前回の記事でコンパイルしたCaffeの中にあるcaffeプロジェクトをDLL作成用に変更し、libcaffeのラッパーを作成する。ちなみに、解決できていないエラーは以下のようなもの。レイヤーの読み込みがうまくいっていないらしい。
c++ - Caffe layer creation failure - Stack Overflow
Google グループ
1.caffeプロジェクトをDLL出力に変更
下記サイトを参考にcaffeプロジェクトの出力をDLLに変更。ただし、今回のプリプロセッサはCAFFE_WRAPとした。
Visual C++でのDLLの一般的な作成方法(暗黙的リンク) - Gobble up pudding
2.caffeプロジェクトにcaffe_wrapper.cpp/caffe_wrapper.hを作成
DLLのソースを記述。外部から呼べる関数run_test()はMNISTの数字識別を行う関数を利用させていただいている。この関数内ではlibcaffeの関数を自由に使うことが出来るので、CaffeAPIを使ったプログラミングはこの中で行うことになる。もともとあったcaffe.cppは使わないので削除。
※このlibcaffeを直接利用するcaffeプロジェクトと同じものを自作できれば問題ないのだがどうしても設定が違っているようでうまくいかなかった。
caffe_wrapper.cpp
#include "caffe_wrapper.h" #include <stdexcept> namespace CaffeUtil { caffe_wrapper::caffe_wrapper(){ } void caffe_wrapper::run_test(string img_path, string model_path, string def_path){ // get a testing image and display Mat img = imread(img_path); cvtColor(img, img, CV_BGR2GRAY); imshow("img", img); waitKey(1); // Set up Caffe Caffe::set_mode(Caffe::GPU); int device_id = 0; Caffe::SetDevice(device_id); LOG(INFO) << "Using GPU"; // Load net Net<float> net(def_path, TEST); string model_file = model_path; net.CopyTrainedLayersFrom(model_file); // set the patch for testing vector<Mat> patches; patches.push_back(img); // push vector<Mat> to data layer float loss = 0.0; boost::shared_ptr<MemoryDataLayer<float> > memory_data_layer; memory_data_layer = boost::static_pointer_cast<MemoryDataLayer<float>>(net.layer_by_name("data")); vector<int> labels(patches.size()); memory_data_layer->AddMatVector(patches, labels); // Net forward const vector<Blob<float>*> & results = net.ForwardPrefilled(&loss); float *output = results[1]->mutable_cpu_data(); // Display the output for (int i = 0; i < 10; i++) { printf("Probability to be Number %d is %.3f\n", i, output[i]); } waitKey(0); } }
caffe_wrapper.h
#pragma once #ifdef CAFFE_WRAP #define CWDLL_API __declspec(dllexport) #else #define CWDLL_API __declspec(dllimport) #endif #include <string> #include "caffe/caffe.hpp" #include "caffe/util/io.hpp" #include "caffe/blob.hpp" #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "boost/smart_ptr/shared_ptr.hpp" #include "caffe/layers/memory_data_layer.hpp" // Caffe's required library #pragma comment(lib, "libcaffe.lib") // enable namespace using namespace std; using namespace cv; using namespace boost; using namespace caffe; namespace CaffeUtil { class CWDLL_API caffe_wrapper { public: // コンストラクタ&デストラクタ caffe_wrapper(); ~caffe_wrapper() {}; // テスト処理実行関数 void run_test(string img_path, string model_path, string def_path); }; }
3.DLL利用側プロジェクト作成
新規プロジェクトcaffe_userを作成する。通常のコンソールプロジェクトでOK。以下に示す内容でDLLを利用するソース(main.cpp)を記述。
追加のインクルードディレクトリ、追加のライブラリディレクトリにCUDA、CuDNN、OpenCV、Boostなど3rdParty製ライブラリのパスを通す。これらはlibcaffeをビルドする際にNuGetで取得しNugetPackagesのディレクトリに保存されているはずである。同じものである必要があるため注意深くパスを通す。また、追加の依存ファイルにcaffe.libを追加。
main.cpp
#include "../caffe/caffe_wrapper.h" #include <iostream> #include <iomanip> #include <vector> using namespace CaffeUtil; int main() { caffe_wrapper *cw = new caffe_wrapper(); string input = "..\\..\\..\\data\\images\\mnist_0.png"; string model = "..\\..\\..\\data\\mnist\\lenet_iter_10000.caffemodel"; string def = "..\\..\\..\\data\\mnist\\lenet_test-memory-1.prototxt"; cw->run_test(input, model, def); return 0; }
4.ビルド&実行
ビルドはlibcaffe→caffe→caffe_userの順で実行する。最終的にcaffe_user.exeが出来るのでそれを実行すると文字認識のプログラムが走る。
★注意★実行時には各種dllが必要なため、dllと同じフォルダにcaffe_user.exeを配置して実行すること。
参考までに私が作成したプロジェクトを以下のサイトにアップロードする。細かなインクルード・リンクまわりやプロジェクトの設定で説明し切れていないものがあると思うので参照していただきたい。
GitHub - whg-res/caffe-from_c: a suggestion to use caffe in windows from C
※参考
http://fa11enprince.hatenablog.com/entry/2014/06/20/015808
https://initialneil.wordpress.com/2015/07/15/caffe-vs2013-opencv-in-windows-tutorial-i/
https://github.com/whg-res/caffe-from_c/