由于我的目标就是用Caffe来做图像的分类,而且是要用C++来做图像的分类。那么,目前最重要的事就是要验证一下能不能用Caffe做这件事,以及Caffe是怎么做这件事的。看到官网上提供了一个例子:Classifying ImageNet: using the C++ API。那我们就从这里开始吧。](http://caffe.berkeleyvision.org/gathered/examples/cpp_classification.html)。那我们就从这里开始吧。)
Caffe, at its core, is written in C++. It is possible to use the C++ API of Caffe to implement an image classification application similar to the Python code presented in one of the Notebook examples. To look at a more general-purpose example of the Caffe C++ API, you should study the source code of the command line tool caffe in tools/caffe.cpp.
A simple C++ code is proposed in examples/cpp_classification/classification.cpp.
也就是,官网说Caffe是完全可以用C++接口进行调用的。要想知道怎么调用,可以研究一下tools/caffe.cpp和examples/cpp_classification/classification.cpp这两个文件。按我目前的理解,tools/caffe.cpp是用来训练的,而classification.cpp则是教我们如何用已经训练好的模型进行分类。其实,对于caffe可以训练这件事是毋庸置疑的,只不过还没有深入了解。但现在最重要的,是要看看当模型训练完成之后,如何调用C++ API函数对图像进行识别。所以,本文章仅对examples/cpp_classification/classification.cpp这个文件感兴趣。网上已经有一个大神写了一篇关于这个议题的文章了,因此,我就只是在Windows系统上来重新实验一下吧。
目标
在Windows 7上实验对小猫图片(examples\images\cat.jpg
)进行分类。
准备
我们需要准备三个文件:
- caffemodel文件:可以直接在此地址下载,下载后放到models\bvlc_reference_caffenet文件夹下。
- 均值文件: notepad++打开data/ilsvc12/get_ilsvrc_aux.sh,“编辑”->“文档格式转换”->“转换为UNIX格式”。在caffe目录下执行
data/ilsvc12/get_ilsvrc_aux.sh
执行并下载后,均值文件放在 data/ilsvrc12/ 文件夹里。
- synset_words.txt文件
在调用脚本文件下载均值的时候,这个文件也一并下载好了。里面放的是1000个类的名称。
C++实现
用Visual Studio打开Caffe.sln。生成examples\classification。如果生成的是Release版本,则生成的exe程序会在caffe\build\examples\cpp_classification\Release中。在控制台运行classification.exe,出现了Usage解释:
Usage: classification.exe deploy.prototxt network.caffemodel mean.binaryproto la
bels.txt img.jpg
也就是说一共需要五个参数:
- deploy.prototxt
- network.caffemodel
- mean.binaryproto
- labels.txt
- img.jpg
> cd caffe
> build\examples\cpp_classification\Release\classification.exe ^
models\bvlc_reference_caffenet\deploy.prototxt ^
models\bvlc_reference_caffenet\bvlc_reference_caffenet.caffemodel ^
data\ilsvrc12\imagenet_mean.binaryproto ^
data\ilsvrc12\synset_words.txt ^
examples\images\cat.jpg
输出结果
---------- Prediction for examples\images\cat.jpg ----------
0.3134 - "n02123045 tabby, tabby cat"
0.2380 - "n02123159 tiger cat"
0.1235 - "n02124075 Egyptian cat"
0.1003 - "n02119022 red fox, Vulpes vulpes"
0.0715 - "n02127052 lynx, catamount"
即有0.3134的概率为tabby cat, 有0.2380的概率为tiger cat ……
OK,到此为止,说明用C++写程序来预测某一张图像属于哪个类是可用的。但如果真的自己要写的话,还是得仔细看一下classification.cpp
。它一共有265行,我们就先来看一下它的main函数吧。
int main(int argc, char** argv) {
if (argc != 6) {
std::cerr << "Usage: " << argv[0]
<< " deploy.prototxt network.caffemodel"
<< " mean.binaryproto labels.txt img.jpg" << std::endl;
return 1;
}
::google::InitGoogleLogging(argv[0]);
string model_file = argv[1];
string trained_file = argv[2];
string mean_file = argv[3];
string label_file = argv[4];
Classifier classifier(model_file, trained_file, mean_file, label_file);
string file = argv[5];
std::cout << "---------- Prediction for "
<< file << " ----------" << std::endl;
cv::Mat img = cv::imread(file, -1);
CHECK(!img.empty()) << "Unable to decode image " << file;
std::vector<Prediction> predictions = classifier.Classify(img);
/* Print the top N predictions. */
for (size_t i = 0; i < predictions.size(); ++i) {
Prediction p = predictions[i];
std::cout << std::fixed << std::setprecision(4) << p.second << " - \""
<< p.first << "\"" << std::endl;
}
}
line# 2-7: 显示用法信息
line# 9: 初始化glog……很惭愧,还没有使用过glog
line# 15: 初始化classifier对象。Classifier类是关键,也是在这个文件中定义的。
line# 22: 用OpenCV库读取图像
line# 24: 调用classifier的方法Classify,对刚刚读取的图像进行识别,并将结果存储在Prediction数组中,其中,Prediction的定义为
typedef std::pair<string, float> Prediction;
是<label,score>键值对。
line# 27-31: 输出结果。
cpp除了main以外的其它地方就干了一件事:定义Classifier。等到真正要用的时候,我们可以把Classifier类直接拿来用。因此,暂时就不展开分析了。
总结
如果我们有了caffemodel文件,均值文件和synset_words.txt文件,则借助** Classifier类**,就可以实现简单地对OpenCV Mat图像预测,给出前n个预测结果。