【caffe-windows】Linux至Windows平台的caffe移植

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

【注】以下全是在Debug模式下操作

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_VER
    case SIGBREAK:  // there is no SIGHUP in windows, take SIGBREAK instead.
      got_sighup = true;
      break;
#else
    case SIGHUP:
      got_sighup = true;
      break;
#endif
    case 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_VER
    if (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.";
    }
#else
    struct sigaction sa;
    // Setup the handler
    sa.sa_handler = &handle_signal;
    // Restart the system call, if at all possible
    sa.sa_flags = SA_RESTART;
    // Block every signal during the handler
    sigfillset(&sa.sa_mask);
    // Intercept SIGHUP and SIGINT
    if (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_VER
      if (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.";
      }
#else
      struct sigaction sa;
      // Setup the sighub handler
      sa.sa_handler = SIG_DFL;
      // Restart the system call, if at all possible
      sa.sa_flags = SA_RESTART;
      // Block every signal during the handler
      sigfillset(&sa.sa_mask);
      // Intercept SIGHUP and SIGINT
      if (sigaction(SIGHUP, &sa, NULL) == -1) {
        LOG(FATAL) << "Cannot uninstall SIGHUP handler.";
      }
      if (sigaction(SIGINT, &sa, NULL) == -1) {
        LOG(FATAL) << "Cannot uninstall SIGINT handler.";
      }
#endif
      already_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;
  }
}  // namespace

namespace 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 dataset
  size_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,看到了心满意足的答案。。。。


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风翼冰舟

额~~~CSDN还能打赏了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值