caffe学习:通过研读classification.cpp了解如何使用caffe模型
在學(xué)習(xí)caffe的過程中,安裝caffe訓(xùn)練和測試手寫字體識別例子成功時以為caffe不過如此;再用編譯好的classification.bin分類小貓的圖片時,發(fā)現(xiàn)自己能做的又非常的少。每次分類我都要提供這些文件?我能隨便給一張圖片嗎?我能更加隨心所欲的用這個模型嗎?模型的輸出應(yīng)該怎么看?這些問題都可以通過研讀classification.cpp得到一定的啟發(fā)。所以我決定參照classification.cpp改一個自己寫的程序。
一、工程建立
如我的一篇博客Caffe + ROS + OpenCV + Qt creator所說,我利用Cmake管理工程,利用qt creator編寫程序。目錄結(jié)構(gòu)如下:
- classify文件夾?
- src文件夾?
- classify.cpp
- CmakeLists.txt
- src文件夾?
CmakeLists.txt內(nèi)容如下:
cmake_minimum_required(VERSION 2.8.3) project(classify)#設(shè)置caffe的根目錄,需改成你的路徑 set(Caffe_DIR /home/gph/Desktop/caffe_cmake/caffe-master) #設(shè)置OpenCV的根目錄,需改成你的路徑 set(OpenCV_DIR /home/gph/opencv/opencv-2.4.11/build)find_package(Caffe) include_directories(${Caffe_INCLUDE_DIRS}) find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS})include_directories(include ${OpenCV_INCLUDE_DIRS}${Caffe_INCLUDE_DIRS} )set(CPP_SOURCES src/classify.cpp)add_executable(classify ${CPP_SOURCES}) target_link_libraries(classify ${OpenCV_LIBS}${Caffe_LIBRARIES} )- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
打開qt creator,在Welcome頁點(diǎn)擊Open Project打開相應(yīng)的CmakeList.txt,再點(diǎn)擊Run cmake即可。
二、改寫程序
1.原程序解析
在classification.cpp中包含一個Classifier類,該類中包含:
Classifier函數(shù):根據(jù)模型的配置文件.prototxt,訓(xùn)練好的模型文件.caffemodel,建立模型,得到net_;處理均值文件,得到mean_;讀入labels文件,得到labels_。
classify函數(shù):調(diào)用Predict函數(shù)對圖像img進(jìn)行分類,返回std::pair< std::string, float >形式的預(yù)測結(jié)果。
私有函數(shù):僅供classifier函數(shù)和classify函數(shù)使用,包括
- setmean函數(shù):將均值文件讀入,轉(zhuǎn)化為一張均值圖像mean_。
- Predict函數(shù):調(diào)用Process函數(shù)將圖像輸入到網(wǎng)絡(luò)中,使用net_->Forward()函數(shù)進(jìn)行預(yù)測;將輸出層的輸出保存到vector容器中返回。
- Process函數(shù):這里寫代碼片對圖像的通道數(shù)、大小、數(shù)據(jù)形式進(jìn)行改變,減去均值mean_,再寫入到net_的輸入層中。
私有變量:
- net_:模型變量;
- input_geometry_:輸入層的圖像的大小;
- num_channels_:輸入層的通道數(shù);
- mean_:均值文件處理得到的均值圖像;
- labels_:標(biāo)簽文件,輸出的結(jié)果表示的含義;
2.改寫程序
改寫后的程序如下:
#include "caffe/caffe.hpp" #include <string> #include <opencv2/opencv.hpp>using namespace std; using namespace cv; using namespace caffe;//用于表存輸出結(jié)果的,string保存的預(yù)測結(jié)果對應(yīng)的字符,如cat;float表示概率 typedef pair<string, float> Prediction;// 函數(shù)Argmax()需要用到的子函數(shù) static bool PairCompare(const std::pair<float, int>& lhs,const std::pair<float, int>& rhs) {return lhs.first > rhs.first; }// 返回預(yù)測結(jié)果中概率從大到小的前N個預(yù)測結(jié)果的索引 static std::vector<int> Argmax(const std::vector<float>& v, int N) {std::vector<std::pair<float, int> > pairs;for (size_t i = 0; i < v.size(); ++i)pairs.push_back(std::make_pair(v[i], i));std::partial_sort(pairs.begin(), pairs.begin() + N, pairs.end(), PairCompare);std::vector<int> result;for (int i = 0; i < N; ++i)result.push_back(pairs[i].second);return result; }int main(int argc, char** argv) {// 定義模型配置文件,模型文件,均值文件,標(biāo)簽文件以及帶分類的圖像string model_file = "/home/gph/Desktop/caffe_cmake/caffe-master/models/bvlc_reference_caffenet/deploy.prototxt";string trained_file = "/home/gph/Desktop/caffe_cmake/caffe-master/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel";string label_file = "/home/gph/Desktop/caffe_cmake/caffe-master/data/ilsvrc12/synset_words.txt";string img_file = "/home/gph/Desktop/caffe_cmake/caffe-master/examples/images/cat.jpg";string mean_file = "/home/gph/Desktop/caffe_cmake/caffe-master/data/ilsvrc12/imagenet_mean.binaryproto";Mat img = imread(img_file);// 定義變量shared_ptr<Net<float> > net_;// 保存模型Size input_geometry_; // 模型輸入圖像的尺寸int num_channels_; // 圖像的通道數(shù)Mat mean_; // 根據(jù)均值文件計(jì)算得到的均值圖像vector<string> labels_; // 標(biāo)簽向量Caffe::set_mode(Caffe::GPU); // 是否使用GPUnet_.reset(new Net<float>(model_file, TEST)); // 加載配置文件,設(shè)定模式為分類net_->CopyTrainedLayersFrom(trained_file); // 根據(jù)訓(xùn)練好的模型修改模型參數(shù)Blob<float>* input_layer = net_->input_blobs()[0]; // 定義輸入層變量num_channels_ = input_layer->channels(); // 得到輸入層的通道數(shù)LOG(INFO) << "num_channels_:" << num_channels_; // 輸出通道數(shù)input_geometry_ = Size(input_layer->width(), input_layer->height()); // 得到輸入層的圖像大小// 處理均值文件,得到均值圖像BlobProto blob_proto;ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto); // mean_file.c_str()將string類型轉(zhuǎn)化為字符型Blob<float> mean_blob;mean_blob.FromProto(blob_proto);vector<Mat> channels;float* data = mean_blob.mutable_cpu_data();// data指針for (int i = 0; i < num_channels_; i++){Mat channel(mean_blob.height(), mean_blob.width(), CV_32FC1, data);//將一副單通道圖像的數(shù)據(jù)記錄再channel中channels.push_back(channel);data += mean_blob.height() * mean_blob.width();// data指向下一個通道的開始}Mat mean;merge(channels, mean); //分離的通道融合,查看cv::merge的作用Scalar channel_mean = cv::mean(mean);mean_ = Mat(input_geometry_, mean.type(), channel_mean);//得到均值圖像// 得到標(biāo)簽ifstream labels(label_file.c_str());string line;while (getline(labels, line))labels_.push_back(string(line));//判斷標(biāo)簽的類數(shù)和模型輸出的類數(shù)是否相同Blob<float>* output_layer = net_->output_blobs()[0];LOG(INFO) << "output_layer dimension: " << output_layer->channels()<< "; labels number: " << labels_.size();// 預(yù)測圖像input_layer->Reshape(1, num_channels_, input_geometry_.height, input_geometry_.width);net_->Reshape(); 調(diào)整模型//將input_channels指向模型的輸入層相關(guān)位置(大概是這樣的)vector<Mat> input_channels;int width = input_layer->width();int height = input_layer->height();float* input_data = input_layer->mutable_cpu_data();for (int i = 0; i < input_layer->channels(); i++){Mat channel(height, width, CV_32FC1, input_data);input_channels.push_back(channel);input_data += width * height;}//改變圖像的大小等Mat sample;if (img.channels() == 3 && num_channels_ == 1)cv::cvtColor(img, sample, cv::COLOR_BGR2GRAY);else if (img.channels() == 4 && num_channels_ == 1)cv::cvtColor(img, sample, cv::COLOR_BGRA2GRAY);else if (img.channels() == 4 && num_channels_ == 3)cv::cvtColor(img, sample, cv::COLOR_BGRA2BGR);else if (img.channels() == 1 && num_channels_ == 3)cv::cvtColor(img, sample, cv::COLOR_GRAY2BGR);elsesample = img;// change img sizecv::Mat sample_resized;if (sample.size() != input_geometry_)cv::resize(sample, sample_resized, input_geometry_);elsesample_resized = sample;// change img to floatcv::Mat sample_float;if (num_channels_ == 3)sample_resized.convertTo(sample_float, CV_32FC3);elsesample_resized.convertTo(sample_float, CV_32FC1);// img normalizecv::Mat sample_normalized;cv::subtract(sample_float, mean_, sample_normalized);//將圖像通過input_channels變量傳遞給模型/* This operation will write the separate BGR planes directly to the* input layer of the network because it is wrapped by the cv::Mat* objects in input_channels. */cv::split(sample_normalized, input_channels);// 調(diào)用模型進(jìn)行預(yù)測net_->Forward();// 得到輸出const float* begin = output_layer->cpu_data();const float* end = begin + output_layer->channels();//將輸出給vector容器vector<float> output = vector<float>(begin, end);//顯示概率前N大的結(jié)果int N = 10;N = std::min<int>(labels_.size(), N);std::vector<int> maxN = Argmax(output, N);std::vector<Prediction> predictions;for (int i = 0; i < N; ++i) {int idx = maxN[i];predictions.push_back(std::make_pair(labels_[idx], output[idx]));}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;}return 0; }// end for main- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
3.編譯并運(yùn)行
在classify文件夾路徑下輸入以下命令:
mkdir build cd build cmake .. make- 1
- 2
- 3
- 4
編譯成功后運(yùn)行程序:
./classify- 1
結(jié)果:
I1028 21:36:29.941694 13761 classify.cpp:53] num_channels_:3 I1028 21:36:29.944052 13761 classify.cpp:80] output_layer dimension: 1000; labels number: 1000 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.0539 - "n02119789 kit fox, Vulpes macrotis" 0.0144 - "n02123394 Persian cat" 0.0113 - "n04493381 tub, vat" 0.0063 - "n02120505 grey fox, gray fox, Urocyon cinereoargenteus" 0.0062 - "n02112018 Pomeranian"總結(jié)
以上是生活随笔為你收集整理的caffe学习:通过研读classification.cpp了解如何使用caffe模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Opencv判断是否加载图片的两种方法
- 下一篇: Caffe + ROS + OpenCV