1、前言
主要参考两篇博客以及很多论坛解决细节问题:
http://www.cnblogs.com/trantor/p/4570097.html
https://initialneil.wordpress.com/2015/01/11/build-caffe-in-windows-with-visual-studio-2013-cuda-6-5-opencv-2-4-9/
移植环境:Windows7 64位+原版caffe+opencv3.0。
本文主要在于移植原版caffe,而依赖库采用的是比较稳定的,而非最新版,比如未使用opencv3.1。如果想自己编译新的依赖库,可以私聊博主或者下方留言,我会根据情况看是否有必要写一个编译caffe依赖库的博客。表示依赖库用CMake编译还是蛮多问题的,折腾了一周
【PS】读者一定要注意路径问题,因此本文教程也尽量采用图片说明,且路径都标示出来了。
2、依赖库
2.1 安装boost
原始下载地址:http://sourceforge.net/projects/boost/files/boost-binaries/1.56.0/boost_1_56_0-msvc-12.0-64.exe/download
百度云盘地址:链接:http://pan.baidu.com/s/1pK7PHcn 密码:eu3v
我安装时候是默认一直下一步的,安装路径也是默认的:C:\local\boost_1_56_0
无需修改环境变量
【PS】坑人的数字卫士,建议关掉它,刚准备安装就给我把安装包删掉了。
2.2 安装opencv
原始下载地址:https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.0.0/opencv-3.0.0.exe/download
百度云盘地址:链接:http://pan.baidu.com/s/1sltOJm9 密码:19u0
参考安装文档:http://jingyan.baidu.com/article/64d05a0245aa70de55f73b12.html
第一步:提取文件
第二步:配置环境变量
添加到path中的环境变量:C:\local\opencv\build\x64\vc12\bin;C:\local\opencv\build\x86\vc12\bin
2.3 安装cuda
参考我前面安装微软版本caffe的GPU配置:http://blog.csdn.net/zb1165048017/article/details/51549105
2.4 下载其它依赖库
可以自己参考fengbingchun的博客自行配置(可能会有部分问题),也可以下载NZ的博客提供的三方包,本文采用后者
原始下载地址:https://drive.google.com/file/d/0B_G5BUend20PRnFhMUlMelFEZW8/view
云盘下载地址:链接:http://pan.baidu.com/s/1bpJtOpp 密码:dmsr
解压以后会有两个文件夹:
将第一个3rdparty拖入你想要编译caffe的目录,我这里新建了一个目录称为caffe-original
至此所有的依赖库已准备好。
3、VS配置caffe工程
3.1 下载官方caffe
官方下载地址:https://github.com/BVLC/caffe
云盘下载地址:链接:http://pan.baidu.com/s/1pLnho0N 密码:e580
直接将caffe-master内部所有文件拖入3rdparty所在文件夹中
然后在E:\caffe-original\caffe下新建一个文件夹bin
3.2 加入工程
3.2.1 新建空项目
然后修改配置管理器->活动解决方案平台
3.2.2 更改环境变量
先把属性管理器调出来:视图->其它窗口->属性管理器
然后右侧会有如下窗口
分别对debug和release添加引用文件和库目录:
① Debug下右键属性
通用属性->常规->输出目录,选择3.1中新建的bin文件夹
通用属性->c/c++->常规->附加包含目录,添加相应的包含(include)文件
通用属性->链接器->常规->附加库目录,添加相应的静态库(lib)所在文件夹
通用属性->链接器->输入->附加依赖项,添加相应的静态库(lib)文件
内容:
opencv_ts300d.lib;opencv_world300d.lib;gflagsd.lib;libglog.lib;libopenblas.dll.a;libprotobufd.lib;
libprotoc.lib;leveldbd.lib;lmdbd.lib;libhdf5_D.lib;libhdf5_hl_D.lib;Shlwapi.lib;cudart.lib;cuda.lib;
nppi.lib;cufft.lib;cublas.lib;curand.lib
② Release下右键属性
通用属性->常规->输出目录,选择3.1中新建的bin文件夹
通用属性->c/c++->常规->附加包含目录,添加相应的包含(include)文件
通用属性->链接器->常规->附加库目录,添加相应的静态库(lib)所在文件夹
上面这三个过程,直接把Debug下对应处文字拷贝过来放进去。
通用属性->链接器->输入->附加依赖项,添加相应的静态库(lib)文件
内容:
opencv_ts300.lib;opencv_world300.lib;gflags.lib;libglog.lib;libopenblas.dll.a;
libprotobuf.lib;libprotoc.lib;leveldb.lib;lmdb.lib;libhdf5.lib;
libhdf5_hl.lib;Shlwapi.lib;cudart.lib;cuda.lib;nppi.lib;cufft.lib;cublas.lib;curand.lib
3.2.3 将caffe相关文件加入工程
解决方案资源管理器->源文件->现有项中选择 E:\caffe-original\src\caffe中的所有文件
4、逐步编译三个cpp
4.1 common.cpp
解决fopen_s不安全问题:
解决方案->caffe右键属性->配置属性->c/c++->预处理器->预处理器定义添加
_CRT_SECURE_NO_WARNINGS
修复getpid()问题,打开common.cpp,
添加头文件
#include<process.h>
注释第36行,修改内容如下:
//pid = getpid();
#ifndef _MSC_VER pid = getpid();
#else pid = _getpid();
#endif
注释第55行,不然最后总体编译会有问题
//::google::InstallFailureSignalHandler();
检查是否修改完毕:common.cpp右键->编译,观察是否生成成功
4.2 blob.cpp(重点)
在E:\caffe-original\scripts目录下新建GeneratePB.bat,内容如下:
if exist "../src/caffe/proto/caffe.pb.h" (echo caffe.pb.h remains the same as before
) else (echo caffe.pb.h is being generated"../3rdparty/bin/protoc" -I="../src/caffe/proto" --cpp_out="../src/caffe/proto" "../src/caffe/proto/caffe.proto"
)
也可以下载:
原始下载地址:https://drive.google.com/file/d/0B_G5BUend20PRDc3bXI0YkRLaUU/view
云盘下载地址:链接:http://pan.baidu.com/s/1ge3wsMJ 密码:ikd8
运行bat,会发现在E:\caffe-original\src\caffe\proto有三个文件了,caffe.pb.cc、caffe.pb.h、caffe.proto
尝试blob.cpp右键->编译,是否能通过。
一般来说Release模式不会出问题,但是Debug模式会出现:
错误 35 error C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility 2132 1 caffe
困扰了很久很久,解决方案:
在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include路径中的xutility(94kb左右的那个),右键属性,取消其只读属性,在第14行加入:
#pragma warning(disable:4996)
再编译一遍,果断成功。
4.3 solver.cpp
第11行也就是刚include完毕所有头文件的地方,添加
//port fir win32
#ifdef _MSC_VER
#define snprintf sprintf_s
#endif
尝试编译一下
5、源文件编译——layer文件夹
①解决方案资源管理器->caffe->右键源文件->添加->新建筛选器->重命名为layers
②从E:\caffe-original\src\caffe\layers随便拖一个cu文件到源文件的layers文件中,此处拖入的是absval_layer.cu
③解决方案资源管理器->caffe右键->生成依赖项->生成自定义->选择第一项cuda7.5
④对着②中拖入的absval_layer.cu右键属性->常规->项类型-CUDA C/C++(此处有几个长得很像,一定要看清,CUDA C/C++是在最后一个,否则选错了以后再编译caffe时会出现obj连接问题)
⑤源文件->layers右键->添加->现有项->添加E:\caffe-original\src\caffe\layers文件夹所有文件;
然后检查一下是不是所有的cu文件都是项类型为CUDA/C++(一般来说操作顺序对了,这一步可以忽视)
⑥打开bnll_layer.cu,注释第8行,并添加:
//const float kBNLL_THRESHOLD = 50.;
#define kBNLL_THRESHOLD 50.0
6、源文件编译——util文件夹
①解决方案资源管理器->caffe->右键源文件->添加->新建筛选器->重命名为util
②源文件->util右键->添加->现有项->添加E:\caffe-original\src\caffe\util文件夹所有文件;
③修改io.cpp的相关部分
第35行和53行中的O_RDONLY修改为O_RDONLY | O_BINARY
第1行后面添加
#if defined(_MSC_VER)
#include<io.h>
#define open _open
#endif
也就是说#include <google/protobuf/io/coded_stream.h>编程第6行了。
第44、53、67行的close(fd)全部改为_close(fd)
编译一下io.cpp看看成功了没有。
④修改math_functions.cpp内容
在第9行添加
#define __builtin_popcount __popcnt
#define __builtin_popcountl __popcnt
编译试试成功了没有。
7、源文件编译——proto文件夹
①解决方案资源管理器->caffe->右键源文件->添加->新建筛选器->重命名为proto
②源文件->util右键->添加->现有项->添加E:\caffe-original\src\caffe\proto文件夹所有文件;
8、编译caffe
①解决方案资源管理器->caffe->右键源文件->添加->现有项->选择E:\caffe-original\tools下的caffe.cpp
②解决方案资源管理器->caffe->右键生成,会出现:
>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppBuild.targets(364,5): warning MSB8004: Output Directory does not end with a trailing slash. This build instance will add the slash as it is required to allow proper evaluation of the Output Directory.
③上方工具栏->生成->取消
④解决方案资源管理器->caffe->属性->配置属性->常规->输出目录改为..\bin\
⑤同样Release也需要改为..\bin\
⑥解决方案资源管理器->caffe->右键重新生成
9、余下问题解决
可见编译貌似没这么成功,在源文件->util->signal_handler.cpp和hdf5.cpp出现了问题
9.1 解决signal_handler.cpp问题
直接用我们前面微软版本的替换就行,代码如下:
#include <boost/bind.hpp>
#include <glog/logging.h>#include <signal.h>
#include <csignal>#include "caffe/util/signal_handler.h"namespace {static volatile sig_atomic_t got_sigint = false;static volatile sig_atomic_t got_sighup = false;static bool already_hooked_up = false;void handle_signal(int signal) {switch (signal) {
#ifdef _MSC_VERcase SIGBREAK: // there is no SIGHUP in windows, take SIGBREAK instead.got_sighup = true;break;
#elsecase SIGHUP:got_sighup = true;break;
#endifcase SIGINT:got_sigint = true;break;}}void HookupHandler() {if (already_hooked_up) {LOG(FATAL) << "Tried to hookup signal handlers more than once.";}already_hooked_up = true;
#ifdef _MSC_VERif (signal(SIGBREAK, handle_signal) == SIG_ERR) {LOG(FATAL) << "Cannot install SIGBREAK handler.";}if (signal(SIGINT, handle_signal) == SIG_ERR) {LOG(FATAL) << "Cannot install SIGINT handler.";}
#elsestruct sigaction sa;// Setup the handlersa.sa_handler = &handle_signal;// Restart the system call, if at all possiblesa.sa_flags = SA_RESTART;// Block every signal during the handlersigfillset(&sa.sa_mask);// Intercept SIGHUP and SIGINTif (sigaction(SIGHUP, &sa, NULL) == -1) {LOG(FATAL) << "Cannot install SIGHUP handler.";}if (sigaction(SIGINT, &sa, NULL) == -1) {LOG(FATAL) << "Cannot install SIGINT handler.";}
#endif}// Set the signal handlers to the default.void UnhookHandler() {if (already_hooked_up) {
#ifdef _MSC_VERif (signal(SIGBREAK, SIG_DFL) == SIG_ERR) {LOG(FATAL) << "Cannot uninstall SIGBREAK handler.";}if (signal(SIGINT, SIG_DFL) == SIG_ERR) {LOG(FATAL) << "Cannot uninstall SIGINT handler.";}
#elsestruct sigaction sa;// Setup the sighub handlersa.sa_handler = SIG_DFL;// Restart the system call, if at all possiblesa.sa_flags = SA_RESTART;// Block every signal during the handlersigfillset(&sa.sa_mask);// Intercept SIGHUP and SIGINTif (sigaction(SIGHUP, &sa, NULL) == -1) {LOG(FATAL) << "Cannot uninstall SIGHUP handler.";}if (sigaction(SIGINT, &sa, NULL) == -1) {LOG(FATAL) << "Cannot uninstall SIGINT handler.";}
#endifalready_hooked_up = false;}}// Return true iff a SIGINT has been received since the last time this// function was called.bool GotSIGINT() {bool result = got_sigint;got_sigint = false;return result;}// Return true iff a SIGHUP has been received since the last time this// function was called.bool GotSIGHUP() {bool result = got_sighup;got_sighup = false;return result;}
} // namespacenamespace caffe {SignalHandler::SignalHandler(SolverAction::Enum SIGINT_action,SolverAction::Enum SIGHUP_action):SIGINT_action_(SIGINT_action),SIGHUP_action_(SIGHUP_action) {HookupHandler();
}SignalHandler::~SignalHandler() {UnhookHandler();
}SolverAction::Enum SignalHandler::CheckForSignals() const {if (GotSIGHUP()) {return SIGHUP_action_;}if (GotSIGINT()) {return SIGINT_action_;}return SolverAction::NONE;
}// Return the function that the solver can use to find out if a snapshot or
// early exit is being requested.
ActionCallback SignalHandler::GetActionFunction() {return boost::bind(&SignalHandler::CheckForSignals, this);
}} // namespace caffe
9.2 解决hdf5.cpp问题
直接用我们前面微软版本的替换就行,代码如下:
#include "caffe/util/hdf5.hpp"#include <string>
#include <vector>namespace caffe {// Verifies format of data stored in HDF5 file and reshapes blob accordingly.
template <typename Dtype>
void hdf5_load_nd_dataset_helper(hid_t file_id, const char* dataset_name_, int min_dim, int max_dim,Blob<Dtype>* blob) {// Verify that the dataset exists.CHECK(H5LTfind_dataset(file_id, dataset_name_))<< "Failed to find HDF5 dataset " << dataset_name_;// Verify that the number of dimensions is in the accepted range.herr_t status;int ndims;status = H5LTget_dataset_ndims(file_id, dataset_name_, &ndims);CHECK_GE(status, 0) << "Failed to get dataset ndims for " << dataset_name_;CHECK_GE(ndims, min_dim);CHECK_LE(ndims, max_dim);// Verify that the data format is what we expect: float or double.std::vector<hsize_t> dims(ndims);H5T_class_t class_;status = H5LTget_dataset_info(file_id, dataset_name_, dims.data(), &class_, NULL);CHECK_GE(status, 0) << "Failed to get dataset info for " << dataset_name_;switch (class_) {case H5T_FLOAT:// In VC++ declaring and initializing variables in case statement without// curly braces (new scope), cause compiler error C2360// https://msdn.microsoft.com/en-us/library/61af7cx3.aspx{LOG_FIRST_N(INFO, 1) << "Datatype class: H5T_FLOAT";break;}case H5T_INTEGER:{LOG_FIRST_N(INFO, 1) << "Datatype class: H5T_INTEGER";break;}case H5T_TIME:{LOG(FATAL) << "Unsupported datatype class: H5T_TIME";}case H5T_STRING:{LOG(FATAL) << "Unsupported datatype class: H5T_STRING";}case H5T_BITFIELD:{LOG(FATAL) << "Unsupported datatype class: H5T_BITFIELD";}case H5T_OPAQUE:{LOG(FATAL) << "Unsupported datatype class: H5T_OPAQUE";}case H5T_COMPOUND:{LOG(FATAL) << "Unsupported datatype class: H5T_COMPOUND";}case H5T_REFERENCE:{LOG(FATAL) << "Unsupported datatype class: H5T_REFERENCE";}case H5T_ENUM:{LOG(FATAL) << "Unsupported datatype class: H5T_ENUM";}case H5T_VLEN:{LOG(FATAL) << "Unsupported datatype class: H5T_VLEN";}case H5T_ARRAY:{LOG(FATAL) << "Unsupported datatype class: H5T_ARRAY";}default:{LOG(FATAL) << "Datatype class unknown";}}vector<int> blob_dims(dims.size());for (int i = 0; i < dims.size(); ++i) {blob_dims[i] = dims[i];}blob->Reshape(blob_dims);
}template <>
void hdf5_load_nd_dataset<float>(hid_t file_id, const char* dataset_name_,int min_dim, int max_dim, Blob<float>* blob) {hdf5_load_nd_dataset_helper(file_id, dataset_name_, min_dim, max_dim, blob);herr_t status = H5LTread_dataset_float(file_id, dataset_name_, blob->mutable_cpu_data());CHECK_GE(status, 0) << "Failed to read float dataset " << dataset_name_;
}template <>
void hdf5_load_nd_dataset<double>(hid_t file_id, const char* dataset_name_,int min_dim, int max_dim, Blob<double>* blob) {hdf5_load_nd_dataset_helper(file_id, dataset_name_, min_dim, max_dim, blob);herr_t status = H5LTread_dataset_double(file_id, dataset_name_, blob->mutable_cpu_data());CHECK_GE(status, 0) << "Failed to read double dataset " << dataset_name_;
}template <>
void hdf5_save_nd_dataset<float>(const hid_t file_id, const string& dataset_name, const Blob<float>& blob,bool write_diff) {int num_axes = blob.num_axes();hsize_t *dims = new hsize_t[num_axes];for (int i = 0; i < num_axes; ++i) {dims[i] = blob.shape(i);}const float* data;if (write_diff) {data = blob.cpu_diff();} else {data = blob.cpu_data();}herr_t status = H5LTmake_dataset_float(file_id, dataset_name.c_str(), num_axes, dims, data);CHECK_GE(status, 0) << "Failed to make float dataset " << dataset_name;delete[] dims;
}template <>
void hdf5_save_nd_dataset<double>(hid_t file_id, const string& dataset_name, const Blob<double>& blob,bool write_diff) {int num_axes = blob.num_axes();hsize_t *dims = new hsize_t[num_axes];for (int i = 0; i < num_axes; ++i) {dims[i] = blob.shape(i);}const double* data;if (write_diff) {data = blob.cpu_diff();} else {data = blob.cpu_data();}herr_t status = H5LTmake_dataset_double(file_id, dataset_name.c_str(), num_axes, dims, data);CHECK_GE(status, 0) << "Failed to make double dataset " << dataset_name;delete[] dims;
}string hdf5_load_string(hid_t loc_id, const string& dataset_name) {// Get size of datasetsize_t size;H5T_class_t class_;herr_t status = \H5LTget_dataset_info(loc_id, dataset_name.c_str(), NULL, &class_, &size);CHECK_GE(status, 0) << "Failed to get dataset info for " << dataset_name;char *buf = new char[size];status = H5LTread_dataset_string(loc_id, dataset_name.c_str(), buf);CHECK_GE(status, 0)<< "Failed to load int dataset with name " << dataset_name;string val(buf);delete[] buf;return val;
}void hdf5_save_string(hid_t loc_id, const string& dataset_name,const string& s) {herr_t status = \H5LTmake_dataset_string(loc_id, dataset_name.c_str(), s.c_str());CHECK_GE(status, 0)<< "Failed to save string dataset with name " << dataset_name;
}int hdf5_load_int(hid_t loc_id, const string& dataset_name) {int val;herr_t status = H5LTread_dataset_int(loc_id, dataset_name.c_str(), &val);CHECK_GE(status, 0)<< "Failed to load int dataset with name " << dataset_name;return val;
}void hdf5_save_int(hid_t loc_id, const string& dataset_name, int i) {hsize_t one = 1;herr_t status = \H5LTmake_dataset_int(loc_id, dataset_name.c_str(), 1, &one, &i);CHECK_GE(status, 0)<< "Failed to save int dataset with name " << dataset_name;
}int hdf5_get_num_links(hid_t loc_id) {H5G_info_t info;herr_t status = H5Gget_info(loc_id, &info);CHECK_GE(status, 0) << "Error while counting HDF5 links.";return info.nlinks;
}string hdf5_get_name_by_idx(hid_t loc_id, int idx) {ssize_t str_size = H5Lget_name_by_idx(loc_id, ".", H5_INDEX_NAME, H5_ITER_NATIVE, idx, NULL, 0, H5P_DEFAULT);CHECK_GE(str_size, 0) << "Error retrieving HDF5 dataset at index " << idx;char *c_str = new char[str_size+1];ssize_t status = H5Lget_name_by_idx(loc_id, ".", H5_INDEX_NAME, H5_ITER_NATIVE, idx, c_str, str_size+1,H5P_DEFAULT);CHECK_GE(status, 0) << "Error retrieving HDF5 dataset at index " << idx;string result(c_str);delete[] c_str;return result;
}} // namespace caffe
编译这两个cpp试试,直接对着cpp右键编译。
9.3 解决steam问题
出现了问题
错误 20 error C4703: 使用了可能未初始化的本地指针变量“stream” e:\caffe-original\src\caffe\layers\base_data_layer.cpp 101 1 caffe
解决方法
解决方案资源管理器->caffe->右键属性->配置属性->C/C++->SDL检查,选择“否”
9.4 重新编译
解决方案资源管理器->caffe->右键生成
9.5 最终问题
可以发现生成成功了,但是ctrl+F5却发现缺少dll
好吧,dll一般来说可以在E:\caffe-original\3rdparty\bin下找到,但是可能不全,这里我提供一下下载地址
链接:http://pan.baidu.com/s/1jI7AoRk 密码:9teo
宿舍笔记本出现了libgfortran-3.dll丢失问题,读者电脑如果无此问题可以无需下载:
链接:http://pan.baidu.com/s/1bpr92hh 密码:de8q
解压以后得到的dll全部丢到C:\Windows\System32里面就行了,好像丢C:\Windows\SysWOW64这里面没用貌似
然后ctrl+F5,看到了心满意足的答案。。。。