2016.12.14:
代碼已知bug:
由于類中有全局變量,聲明多個對象時,全局變量指向會改變,造成結果錯誤并且有內存泄漏。因此該份代碼只能聲明一個Classifier對象。
新的代碼已經重新封裝,并且做成多標簽輸出,下載地址:http://download.csdn.net/detail/sinat_30071459/9715053
主要修改:http://blog.csdn.net/sinat_30071459/article/details/53611678
因為caffe-windows-master的代碼比較多,看著很亂,所以想到把它封裝成一個類來調用做圖像分類,這里以GPU版本為例,記錄一下自己封裝成DLL和如何使用封裝后的DLL的過程。
1.打開解決方案
首先,需要修改解決方案配置(默認是Release),我們新建一個叫ReleaseGPU,平臺修改為x64(因為用到的其他DLL是64位,配置成win32會出錯),如下:
這里我將caffelib的項目名改成了type_recognition_ver2_api_gpu,配置好ReleaseGPU后,右鍵項目type_recognition_ver2_api_gpu——>屬性,配置屬性頁:
(1)配置屬性——常規
(2)C/C++——常規——附加包含目錄:
../../3rdparty/include
../../src
../../include
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include
預處理器——預處理器定義添加:
TYPE_RECOGNITION_LINK_SHARED
TYPE_RECOGNITION_API_EXPORTS
(3)鏈接器——常規——附加庫目錄:
../../3rdparty/lib
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\lib\x64
(4)鏈接器——輸入——附加依賴項:
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib
cudart.lib
cublas.lib
curand.lib
libprotobuf.lib
hdf5_tools.lib
hdf5_hl_fortran.lib
hdf5_fortran.lib
hdf5_hl_f90cstub.lib
hdf5_f90cstub.lib
hdf5_cpp.lib
hdf5_hl_cpp.lib
hdf5_hl.lib
hdf5.lib
zlib.lib
szip.lib
opencv_world300.lib
shlwapi.lib
leveldb.lib
cublas_device.lib
cuda.lib
libglog.lib
lmdb.lib
cudnn.lib
libopenblas.dll.a
libgflags.lib
這樣就配置好了。
2.添加文件
(1)添加classfy.h和classify.cpp
[plain]?view plaincopy
//classify.h?? #pragma?once?? #include?<caffe/caffe.hpp>?? #include?<opencv2/core/core.hpp>?? #include?<opencv2/highgui/highgui.hpp>?? #include?<opencv2/imgproc/imgproc.hpp>?? #include?<iosfwd>?? #include?<memory>?? #include?<utility>?? #include?<vector>?? #include?<iostream>?? #include?<string>?? #include?<fstream>?? #include?<sstream>?? ?? using?namespace?caffe;??//?NOLINT(build/namespaces)?? //using?namespace?boost::property_tree;?? using?std::string;?? ?? /*?Pair?(number,?confidence)?representing?a?prediction.?*/?? typedef?std::pair<int,?float>?Prediction;?? ?? class?ClassifierImpl?{?? public:?? ????ClassifierImpl::ClassifierImpl(){};?? ????ClassifierImpl(const?string&?model_file,?? ????????const?string&?trained_file,?? ????????const?string&?mean_file,?? ????????const?string&?label_file);?? ?? ????std::vector<Prediction>?Classify(const?cv::Mat&?img,?int?N?=?2);?? ?? private:?? ????void?SetMean(const?string&?mean_file);?? ?? ????std::vector<float>?Predict(const?cv::Mat&?img);?? ?? ????void?WrapInputLayer(std::vector<cv::Mat>*?input_channels);?? ?? ????void?Preprocess(const?cv::Mat&?img,?? ????????std::vector<cv::Mat>*?input_channels);?? ?? private:?? ????shared_ptr<Net<float>?>?net_;?? ????cv::Size?input_geometry_;?? ????int?num_channels_;?? ????cv::Mat?mean_;?? };??
[plain]?view plaincopy
//classify.cpp?? #include?"classify.h"?? ClassifierImpl::ClassifierImpl(const?string&?model_file,?? ????const?string&?trained_file,?? ????const?string&?mean_file,?? ????const?string&?label_file)?? {?? #ifdef?CPU_ONLY?? ????Caffe::set_mode(Caffe::CPU);?? #else?? ????Caffe::set_mode(Caffe::GPU);?? #endif?? ?? ????/*?Load?the?network.?*/?? ????net_.reset(new?Net<float>(model_file,?TEST));?? ????net_->CopyTrainedLayersFrom(trained_file);?? ?? ????CHECK_EQ(net_->num_inputs(),?1)?<<?"Network?should?have?exactly?one?input.";?? ????CHECK_EQ(net_->num_outputs(),?1)?<<?"Network?should?have?exactly?one?output.";?? ?? ????Blob<float>*?input_layer?=?net_->input_blobs()[0];?? ????num_channels_?=?input_layer->channels();?? ????CHECK(num_channels_?==?3?||?num_channels_?==?1)?? ????????<<?"Input?layer?should?have?1?or?3?channels.";?? ????input_geometry_?=?cv::Size(input_layer->width(),?input_layer->height());?? ?? ????/*?Load?the?binaryproto?mean?file.?*/?? ????SetMean(mean_file);?? ?? ????Blob<float>*?output_layer?=?net_->output_blobs()[0];?? ?????? }?? ?? static?bool?PairCompare(const?std::pair<float,?int>&?lhs,?? ????const?std::pair<float,?int>&?rhs)?{?? ????return?lhs.first?>?rhs.first;?? }?? ?? /*?Return?the?indices?of?the?top?N?values?of?vector?v.?*/?? 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;?? }?? ?? /*?Return?the?top?N?predictions.?*/?? std::vector<Prediction>?ClassifierImpl::Classify(const?cv::Mat&?img,?int?N)?{?? ????std::vector<float>?output?=?Predict(img);?? ?? ????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(idx,?output[idx]));?? ????}?? ?? ????return?predictions;?? }?? ?? /*?Load?the?mean?file?in?binaryproto?format.?*/?? void?ClassifierImpl::SetMean(const?string&?mean_file)?{?? ????BlobProto?blob_proto;?? ????ReadProtoFromBinaryFileOrDie(mean_file.c_str(),?&blob_proto);?? ?? ????/*?Convert?from?BlobProto?to?Blob<float>?*/?? ????Blob<float>?mean_blob;?? ????mean_blob.FromProto(blob_proto);?? ????CHECK_EQ(mean_blob.channels(),?num_channels_)?? ????????<<?"Number?of?channels?of?mean?file?doesn't?match?input?layer.";?? ?? ????/*?The?format?of?the?mean?file?is?planar?32-bit?float?BGR?or?grayscale.?*/?? ????std::vector<cv::Mat>?channels;?? ????float*?data?=?mean_blob.mutable_cpu_data();?? ????for?(int?i?=?0;?i?<?num_channels_;?++i)?{?? ????????/*?Extract?an?individual?channel.?*/?? ????????cv::Mat?channel(mean_blob.height(),?mean_blob.width(),?CV_32FC1,?data);?? ????????channels.push_back(channel);?? ????????data?+=?mean_blob.height()?*?mean_blob.width();?? ????}?? ?? ????/*?Merge?the?separate?channels?into?a?single?image.?*/?? ????cv::Mat?mean;?? ????cv::merge(channels,?mean);?? ????/*?Compute?the?global?mean?pixel?value?and?create?a?mean?image?? ????*?filled?with?this?value.?*/?? ????cv::Scalar?channel_mean?=?cv::mean(mean);?? ????mean_?=?cv::Mat(input_geometry_,?mean.type(),?channel_mean);?? }?? std::vector<float>?ClassifierImpl::Predict(const?cv::Mat&?img)?{?? ????Blob<float>*?input_layer?=?net_->input_blobs()[0];?? ????input_layer->Reshape(1,?num_channels_,?? ????????input_geometry_.height,?input_geometry_.width);?? ????/*?Forward?dimension?change?to?all?layers.?*/?? ????net_->Reshape();?? ????std::vector<cv::Mat>?input_channels;?? ????WrapInputLayer(&input_channels);?? ?? ????Preprocess(img,?&input_channels);?? ?? ????net_->ForwardPrefilled();?? ?? ????/*?Copy?the?output?layer?to?a?std::vector?*/?? ????Blob<float>*?output_layer?=?net_->output_blobs()[0];?? ????const?float*?begin?=?output_layer->cpu_data();?? ????const?float*?end?=?begin?+?output_layer->channels();?? ????return?std::vector<float>(begin,?end);?? }?? ?? /*?Wrap?the?input?layer?of?the?network?in?separate?cv::Mat?objects?? *?(one?per?channel).?This?way?we?save?one?memcpy?operation?and?we?? *?don't?need?to?rely?on?cudaMemcpy2D.?The?last?preprocessing?? *?operation?will?write?the?separate?channels?directly?to?the?input?? *?layer.?*/?? void?ClassifierImpl::WrapInputLayer(std::vector<cv::Mat>*?input_channels)?{?? ????Blob<float>*?input_layer?=?net_->input_blobs()[0];?? ?? ????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)?{?? ????????cv::Mat?channel(height,?width,?CV_32FC1,?input_data);?? ????????input_channels->push_back(channel);?? ????????input_data?+=?width?*?height;?? ????}?? }?? ?? void?ClassifierImpl::Preprocess(const?cv::Mat&?img,?? ????std::vector<cv::Mat>*?input_channels)?{?? ????/*?Convert?the?input?image?to?the?input?image?format?of?the?network.?*/?? ????cv::Mat?sample;?? ????if?(img.channels()?==?3?&&?num_channels_?==?1)?? ????????cv::cvtColor(img,?sample,?CV_BGR2GRAY);?? ????else?if?(img.channels()?==?4?&&?num_channels_?==?1)?? ????????cv::cvtColor(img,?sample,?CV_BGRA2GRAY);?? ????else?if?(img.channels()?==?4?&&?num_channels_?==?3)?? ????????cv::cvtColor(img,?sample,?CV_BGRA2BGR);?? ????else?if?(img.channels()?==?1?&&?num_channels_?==?3)?? ????????cv::cvtColor(img,?sample,?CV_GRAY2BGR);?? ????else?? ????????sample?=?img;?? ?? ????cv::Mat?sample_resized;?? ????if?(sample.size()?!=?input_geometry_)?? ????????cv::resize(sample,?sample_resized,?input_geometry_);?? ????else?? ????????sample_resized?=?sample;?? ?? ????cv::Mat?sample_float;?? ????if?(num_channels_?==?3)?? ????????sample_resized.convertTo(sample_float,?CV_32FC3);?? ????else?? ????????sample_resized.convertTo(sample_float,?CV_32FC1);?? ?? ????cv::Mat?sample_normalized;?? ????cv::subtract(sample_float,?mean_,?sample_normalized);?? ?? ????/*?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);?? ?? ????CHECK(reinterpret_cast<float*>(input_channels->at(0).data)?? ????????==?net_->input_blobs()[0]->cpu_data())?? ????????<<?"Input?channels?are?not?wrapping?the?input?layer?of?the?network.";?? }??
然后,我們再寫一個導出類即可(如下(2))。
(2)添加type_recognition_ver2_api_gpu.h和type_recognition_ver2_api_gpu.cpp
[plain]?view plaincopy
//type_recognition_ver2_api_gpu.h?? #ifndef?TYPE_RECOGNITION_API_H_??//保證頭文件包含一次?? #define?TYPE_RECOGNITION_API_H_?? ?? #ifdef?TYPE_RECOGNITION_LINK_SHARED?? #if?defined(__GNUC__)?&&?__GNUC__?>=?4?? #define?TYPE_RECOGNITION_API?__attribute__?((visibility("default")))?? #elif?defined(__GNUC__)?? #define?TYPE_RECOGNITION_API?? #elif?defined(_MSC_VER)?? #if?defined?(TYPE_RECOGNITION_API_EXPORTS)?? #define?TYPE_RECOGNITION_API?__declspec(dllexport)?? #else?? #define?TYPE_RECOGNITION_API?__declspec(dllimport)?? #endif?? #else?? #define?TYPE_RECOGNITION_API?? #endif?? #else?? #define?TYPE_RECOGNITION_API?? #endif?? ?? #include?<opencv2/core/core.hpp>?? #include?<string>?? #include?<vector>?? /*?Pair?(label,?confidence)?representing?a?prediction.?*/?? typedef?std::pair<int,?float>?Prediction;?? class?TYPE_RECOGNITION_API?Classifier?//導出類?? {?? public:?? ????Classifier(){};?? ????~Classifier();?? ????Classifier(const?std::string&?model_file,?? ????????const?std::string&?trained_file,?? ????????const?std::string&?mean_file,?? ????????const?std::string&?label_file);?? ????std::vector<Prediction>?Classify(const?cv::Mat&?img,?int?N?=?2);?? };?? ?? #endif??
[plain]?view plaincopy
//type_recognition_ver2_api_gpu.cpp?? #include?"type_recognition_ver2_api_gpu.h"?? #include?"classify.h"?? ClassifierImpl?*impl?=?NULL;?? Classifier::Classifier(const?std::string&?model_file,?? ????const?std::string&?trained_file,?? ????const?std::string&?mean_file,?? ????const?std::string&?label_file)?? {?? #ifdef?_MSC_VER?? #pragma?comment(?linker,?"/subsystem:windows")?? #endif?? ????impl?=?new?ClassifierImpl(model_file,?trained_file,?mean_file,?label_file);?? }?? ?? Classifier::~Classifier()?? {?? ????//impl->~ClassifierImpl();?? ????if?(impl)?? ????????delete?impl;?? }?? std::vector<Prediction>?Classifier::Classify(const?cv::Mat&?img,?int?N)?? {?? ????return?impl->Classify(img,?N);?? }??
這時,右鍵type_recognition_ver2_api_gpu項目,生成即可,在ReleaseGPU文件夾內即可得到如下文件:
用到的文件是dll和lib文件,使用這些文件方法如下。
3.使用DLL
將type_recognition_ver2_api_gpu.dll復制到caffe-windows-master\3rdparty\bin;
將type_recognition_ver2_api_gpu.lib復制到caffe-windows-master\3rdparty\lib;
將type_recognition_ver2_api_gpu.h復制到caffe-windows-master\3rdparty\include;
然后,新建一個控制臺項目,配置成x64,
右鍵項目配置如下:
C/C++——常規——附加包含目錄:(這里路徑自己修改)
? ? ? ? ? ? ? ********\3rdparty\include
鏈接器——常規——附加庫目錄:
? ? ? ? ? ? ?********\3rdparty\lib
鏈接器——輸入——附加依賴項:
將type_recognition_ver2_api_gpu.lib和opencv_world300.lib加進去,
然后,為項目添加一個cpp文件:
[plain]?view plaincopy
#include?<iostream>?? #include?<string>?? #include?<opencv2/core/core.hpp>?? #include?<opencv2/highgui/highgui.hpp>?? #include?"type_recognition_ver2_api_gpu.h"?? int?main(int?argc,?char**?argv)?? {?? ????std::string?model_file("./model/deploy.prototxt");?? ????std::string?trained_file("./model/net.caffemodel");?? ????std::string?mean_file("./model/type_mean.binaryproto");?? ????std::string?label_file("./model/typelabels.txt");?? ?? ????Classifier?myclassifier(model_file,?trained_file,?mean_file,?label_file);?? ?? ?????? ????cv::Mat?img?=?cv::imread("../image/automobile/000001.jpg",?-1);?? ?? ????std::vector<Prediction>?result?=?myclassifier.Classify(img);?? ????Prediction?p?=?result[0];?? ????std::cout?<<"類別:"<<?p.first?<<?"確信度:"?<<?p.second?<<?"\n";?? ????getchar();?? ????return?0;?? }??
結果:
(下面鏈接的代碼有bug,聲明多個對象會出問題,新的代碼:http://blog.csdn.net/sinat_30071459/article/details/53735600)
封裝好的代碼加入了OpenCV顯示圖像,可通過鏈接下載:http://download.csdn.net/detail/sinat_30071459/9568131? 是一個txt文件,因為csdn上傳限制,上傳到了百度云,txt里面有百度云鏈接。
將Classification\CLassificationDLL\bin加入環境變量后即可。
效果如下:(把Freetype庫也加了進去,標簽可以顯示中文)
總結
以上是生活随笔為你收集整理的封装caffe-windows-master为动态链接库的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。