caffe添加一个新层

来源:互联网 发布:长沙学历网络教育报名 编辑:程序博客网 时间:2024/05/17 03:47

转载来自:http://blog.csdn.net/xizero00/article/details/52529341

首先我们设计我们层所拥有的参数
out_height,即输出的图像的高度
out_width,即输出图像的宽度
visualize,是否需要将图像显示出来


那么可以在src/caffe/proto/caffe.proto文件中加入如下代码:

[cpp] view plain copy
  1. message ImageScaleParameter {  
  2.   // Specify the output height and width  
  3.   optional uint32 out_height = 1;  
  4.   optional uint32 out_width = 2;  
  5.    
  6.   // for debug you can see the source images and scaled images  
  7.   optional bool visualize = 3 [default = false];  
  8. }  



这里就指定了参数的名称以及参数的类型,optional说明该参数是可选的可以出现也可以不出现,此外[default=false]表明该参数的默认值是false
每个参数都指定一个数字表明参数的标识。

接着,我们可以将我们设计好的参数放入LayerParameter里头:
[cpp] view plain copy
  1. optional HingeLossParameter hinge_loss_param = 114;  
  2. optional ImageDataParameter image_data_param = 115;  
  3. optional ImageScaleParameter image_scale_param = 147;  
  4. optional InfogainLossParameter infogain_loss_param = 116;  
  5. optional InnerProductParameter inner_product_param = 117;  



注意加入的时候看一看LayerParameter的注释,当你修改完毕了也要注意加入这样提示,这样方便后人更加方便地添加自定义层
// LayerParameter next available layer-specific ID: 148 (last added: image_scale_param)

接下来我们实现我们自己的层的头文件:

(1)实现的首先需要设置不允许头文件重复加入的宏定义:

[cpp] view plain copy
  1. #ifndef CAFFE_IMAGE_SCALE_LAYER_HPP_  
  2. #define CAFFE_IMAGE_SCALE_LAYER_HPP_  



(2)加入必要的头文件
[cpp] view plain copy
  1. #include "caffe/blob.hpp"  
  2. #include "caffe/layer.hpp"  
  3. #include "caffe/proto/caffe.pb.h"  
  4. #include "caffe/layer.hpp"  



(3)加入返回的层的类型字符串
[cpp] view plain copy
  1. virtual inline const char* type() const { return "ImageScale"; }  



(4)告诉caffe本层的输入有几个,输出有几个
[cpp] view plain copy
  1. virtual inline int ExactNumBottomBlobs() const { return 1; }  
  2. virtual inline int ExactNumTopBlobs() const { return 1; }  



(5)由于本层实现是图像的缩放,所以不需要反传,因此直接写一个空的虚函数的实现
[cpp] view plain copy
  1. virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,  
  2.   const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {};  



(6)定义在使用过程中所使用的类中的成员变量,注意类的成员变量的命名最后是以下划线结束,这样能够保持与caffe的代码一致性
[cpp] view plain copy
  1. int out_height_;  
  2. int out_width_;  
  3. int height_;  
  4. int width_;  
  5. bool visualize_;  
  6. int num_images_;  
  7. int num_channels_;  



(7)最后别忘记加入endif这个宏,此外注意加入必要的注释,以表明这个endif所对应的开头是什么
[cpp] view plain copy
  1. #endif  // CAFFE_IMAGE_SCALE_LAYER_HPP_  




下面给出详细的头文件代码:
[cpp] view plain copy
  1. #ifndef CAFFE_IMAGE_SCALE_LAYER_HPP_  
  2. #define CAFFE_IMAGE_SCALE_LAYER_HPP_  
  3. #include "caffe/blob.hpp"  
  4. #include "caffe/layer.hpp"  
  5. #include "caffe/proto/caffe.pb.h"  
  6. #include "caffe/layer.hpp"  
  7. namespace caffe {  
  8. // written by xizero00 2016/9/13  
  9. template <typename Dtype>  
  10. class ImageScaleLayer : public Layer<Dtype> {  
  11.  public:  
  12.   explicit ImageScaleLayer(const LayerParameter& param)  
  13.       : Layer<Dtype>(param) {}  
  14.   virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  15.       const vector<Blob<Dtype>*>& top);  
  16.   virtual void Reshape(const vector<Blob<Dtype>*>& bottom,  
  17.       const vector<Blob<Dtype>*>& top);  
  18.   virtual inline const char* type() const { return "ImageScale"; }  
  19.   virtual inline int ExactNumBottomBlobs() const { return 1; }  
  20.   virtual inline int ExactNumTopBlobs() const { return 1; }  
  21.  protected:  
  22.   /// @copydoc ImageScaleLayer  
  23.   virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
  24.       const vector<Blob<Dtype>*>& top);  
  25.   virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,  
  26.       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {};  
  27.   int out_height_;  
  28.   int out_width_;  
  29.   int height_;  
  30.   int width_;  
  31.   bool visualize_;  
  32.   int num_images_;  
  33.   int num_channels_;  
  34. };  
  35. }  // namespace caffe  
  36. #endif  // CAFFE_IMAGE_SCALE_LAYER_HPP_  



接下来写具体的层的设置以及层的前传的实现:
(8)加入必要的头文件
[cpp] view plain copy
  1. #include "caffe/layers/image_scale_layer.hpp"  
  2. #include "caffe/util/math_functions.hpp"  
  3. #include <opencv2/opencv.hpp>  





(9)实现层的设置函数LayerSetUp,在该函数中将网络的配置参数读取到类中的成员变量中,便于前传的时候以及对层进行设置的时候使用,并且检查参数的合法性
[cpp] view plain copy
  1. template <typename Dtype>  
  2. void ImageScaleLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  3.       const vector<Blob<Dtype>*>& top) {  
  4.   // get parameters  
  5.   const ImageScaleParameter& param = this->layer_param_.image_scale_param();  
  6.   // get the output size  
  7.   out_height_ = param.out_height();  
  8.   out_width_ = param.out_width();  
  9.   visualize_ = param.visualize();  
  10.    
  11.   // get the input size  
  12.   num_images_ = bottom[0]->num();  
  13.   height_ = bottom[0]->height();  
  14.   width_ = bottom[0]->width();  
  15.   num_channels_ = bottom[0]->channels();  
  16.   // check the channels must be images  
  17.   // channel must be 1 or 3, gray image or color image  
  18.   CHECK_EQ( (num_channels_==3) || (num_channels_ == 1), true);  
  19.   // check the output size  
  20.   CHECK_GT(out_height_, 0);  
  21.   CHECK_GT(out_height_, 0);  
  22.    
  23. }  



(10)实现层的Reshape函数,来设定该层的输出的大小,我们使用从网络配置文件中的参数类设置输出的大小
[cpp] view plain copy
  1. template <typename Dtype>  
  2. void ImageScaleLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,  
  3.       const vector<Blob<Dtype>*>& top) {  
  4.   // reshape the outputs  
  5.   top[0]->Reshape(num_images_, num_channels_, out_height_, out_width_);  
  6. }  



(11)实现前向传播函数Forward_cpu,我实现的就是将图像一幅一幅地进行缩放到配置文件中所给的大小。
[cpp] view plain copy
  1. template <typename Dtype>  
  2. void ImageScaleLayer<Dtype>::Forward_cpu(  
  3.     const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {  
  4.   const Dtype* bottom_data = bottom[0]->cpu_data();  
  5.   Dtype * top_data = top[0]->mutable_cpu_data();  
  6.   cv::Mat srcimage, dstimage;  
  7.    
  8.   // precompurte the index  
  9.   const int srcimagesize = width_ * height_;  
  10.   const int dstimagesize = out_width_ *  out_height_;  
  11.   const int srcchimagesize = srcimagesize * num_channels_;  
  12.   const int dstchimagesize = dstimagesize * num_channels_;  
  13.   for  ( int idx_img = 0; idx_img < num_images_; idx_img++ )  
  14.   {  
  15.         // zeros source images and scaled images  
  16.         srcimage = cv::Mat::zeros(height_, width_, CV_32FC1);  
  17.         dstimage = cv::Mat::zeros(out_height_, out_width_, CV_32FC1);  
  18.         // read from bottom[0]  
  19.         for  ( int idx_ch = 0; idx_ch < num_channels_; idx_ch++ )  
  20.         {  
  21.                 for  (int i = 0; i < height_; i++)  
  22.                 {  
  23.                         for ( int j=0; j < width_; j++ )  
  24.                         {  
  25.                                 int image_idx = idx_img * srcchimagesize + srcimagesize * idx_ch + height_ *i + j;  
  26.                                 srcimage.at<float>(i,j) = (float)bottom_data[image_idx];  
  27.                         }  
  28.                 }  
  29.         }  
  30.         // resize to specified size  
  31.         // here we use linear interpolation  
  32.         cv::resize(srcimage, dstimage, dstimage.size());  
  33.         // store the resized image to top[0]  
  34.         for (int idx_ch = 0; idx_ch < num_channels_; idx_ch++)  
  35.         {  
  36.                 for (int i = 0; i < out_height_; i++)  
  37.                 {  
  38.                         for (int j = 0; j < out_width_; j++)  
  39.                         {  
  40.                                 int image_idx = idx_img * dstchimagesize + dstimagesize * idx_ch + out_height_*i + j;  
  41.                                 top_data[image_idx] = dstimage.at<float>(i,j);  
  42.                         }  
  43.                 }  
  44.         }  
  45.         if (visualize_)  
  46.         {  
  47.                 cv::namedWindow("src image", CV_WINDOW_AUTOSIZE);  
  48.                 cv::namedWindow("dst image", CV_WINDOW_AUTOSIZE);  
  49.                 cv::imshow("src image", srcimage);  
  50.                 cv::imshow("dst image", dstimage);  
  51.                 cv::waitKey(0);  
  52.         }  
  53.   }  
  54. }  




最后给出完整的实现:

[cpp] view plain copy
  1. #include "caffe/layers/image_scale_layer.hpp"  
  2. #include "caffe/util/math_functions.hpp"  
  3. #include <opencv2/opencv.hpp>  
  4. namespace caffe {  
  5. template <typename Dtype>  
  6. void ImageScaleLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  7.       const vector<Blob<Dtype>*>& top) {  
  8.   // get parameters  
  9.   const ImageScaleParameter& param = this->layer_param_.image_scale_param();  
  10.   // get the output size  
  11.   out_height_ = param.out_height();  
  12.   out_width_ = param.out_width();  
  13.   visualize_ = param.visualize();  
  14.    
  15.   // get the input size  
  16.   num_images_ = bottom[0]->num();  
  17.   height_ = bottom[0]->height();  
  18.   width_ = bottom[0]->width();  
  19.   num_channels_ = bottom[0]->channels();  
  20.   // check the channels must be images  
  21.   // channel must be 1 or 3, gray image or color image  
  22.   CHECK_EQ( (num_channels_==3) || (num_channels_ == 1), true);  
  23.   // check the output size  
  24.   CHECK_GT(out_height_, 0);  
  25.   CHECK_GT(out_height_, 0);  
  26.    
  27. }  
  28. template <typename Dtype>  
  29. void ImageScaleLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,  
  30.       const vector<Blob<Dtype>*>& top) {  
  31.   // reshape the outputs  
  32.   top[0]->Reshape(num_images_, num_channels_, out_height_, out_width_);  
  33. }  
  34. template <typename Dtype>  
  35. void ImageScaleLayer<Dtype>::Forward_cpu(  
  36.     const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {  
  37.   const Dtype* bottom_data = bottom[0]->cpu_data();  
  38.   Dtype * top_data = top[0]->mutable_cpu_data();  
  39.   cv::Mat srcimage, dstimage;  
  40.    
  41.   // precompurte the index  
  42.   const int srcimagesize = width_ * height_;  
  43.   const int dstimagesize = out_width_ *  out_height_;  
  44.   const int srcchimagesize = srcimagesize * num_channels_;  
  45.   const int dstchimagesize = dstimagesize * num_channels_;  
  46.   for  ( int idx_img = 0; idx_img < num_images_; idx_img++ )  
  47.   {  
  48.         // zeros source images and scaled images  
  49.         srcimage = cv::Mat::zeros(height_, width_, CV_32FC1);  
  50.         dstimage = cv::Mat::zeros(out_height_, out_width_, CV_32FC1);  
  51.         // read from bottom[0]  
  52.         for  ( int idx_ch = 0; idx_ch < num_channels_; idx_ch++ )  
  53.         {  
  54.                 for  (int i = 0; i < height_; i++)  
  55.                 {  
  56.                         for ( int j=0; j < width_; j++ )  
  57.                         {  
  58.                                 int image_idx = idx_img * srcchimagesize + srcimagesize * idx_ch + height_ *i + j;  
  59.                                 srcimage.at<float>(i,j) = (float)bottom_data[image_idx];  
  60.                         }  
  61.                 }  
  62.         }  
  63.         // resize to specified size  
  64.         // here we use linear interpolation  
  65.         cv::resize(srcimage, dstimage, dstimage.size());  
  66.         // store the resized image to top[0]  
  67.         for (int idx_ch = 0; idx_ch < num_channels_; idx_ch++)  
  68.         {  
  69.                 for (int i = 0; i < out_height_; i++)  
  70.                 {  
  71.                         for (int j = 0; j < out_width_; j++)  
  72.                         {  
  73.                                 int image_idx = idx_img * dstchimagesize + dstimagesize * idx_ch + out_height_*i + j;  
  74.                                 top_data[image_idx] = dstimage.at<float>(i,j);  
  75.                         }  
  76.                 }  
  77.         }  
  78.         if (visualize_)  
  79.         {  
  80.                 cv::namedWindow("src image", CV_WINDOW_AUTOSIZE);  
  81.                 cv::namedWindow("dst image", CV_WINDOW_AUTOSIZE);  
  82.                 cv::imshow("src image", srcimage);  
  83.                 cv::imshow("dst image", dstimage);  
  84.                 cv::waitKey(0);  
  85.         }  
  86.   }  
  87. }  
  88. #ifdef CPU_ONLY  
  89. STUB_GPU(ImageScaleLayer);  
  90. #endif  
  91. INSTANTIATE_CLASS(ImageScaleLayer);  
  92. REGISTER_LAYER_CLASS(ImageScale);  
  93. }  // namespace caffe  




请把上述代码,保存为image_scale_layer.hpp和cpp。然后放入到对应的include和src/caffe/layers文件夹中。

那么在使用的时候可以进行如下配置
[cpp] view plain copy
  1. layer {  
  2.   name: "imagescaled"  
  3.   type: "ImageScale"  
  4.   bottom: "data"  
  5.   top: "imagescaled"  
  6.   image_scale_param {  
  7.     out_height: 128  
  8.     out_width: 128  
  9.     visualize: true  
  10.   }  
  11. }  



上述配置中out_height和out_width就是经过缩放之后的图片的大小,而visualize表明是否显示的意思。
至此,我们就完成了一个很简单的caffe自定义层的实现,怎么样,很简单吧?
我测试的模型(我想你肯定知道怎么用caffe所听的工具将mnist数据集转换为lmdb吧)是:
[cpp] view plain copy
  1. # Simple single-layer network to showcase editing model parameters.  
  2. name: "sample"  
  3. layer {  
  4.   name: "data"  
  5.   type: "Data"  
  6.   top: "data"  
  7.   include {  
  8.     phase: TRAIN  
  9.   }  
  10.   transform_param {  
  11.     scale: 0.0039215684  
  12.   }  
  13.   data_param {  
  14.     source: "examples/mnist/mnist_train_lmdb"  
  15.     batch_size: 10  
  16.     backend: LMDB  
  17.   }  
  18. }  
  19.   
  20. layer {  
  21.   name: "imagescaled"  
  22.   type: "ImageScale"  
  23.   bottom: "data"  
  24.   top: "imagescaled"  
  25.   image_scale_param {  
  26.     out_height: 128  
  27.     out_width: 128  
  28.     visualize: true  
  29.   }  
  30. }  

测试所用的solver.prototxt
[cpp] view plain copy
  1. net: "examples/imagescale/sample.prototxt"  
  2.   
  3.   
  4. base_lr: 0.01  
  5. lr_policy: "step"  
  6. gamma: 0.1  
  7. stepsize: 10000  
  8. display: 1  
  9. max_iter: 1  
  10. weight_decay: 0.0005  
  11. snapshot: 1  
  12. snapshot_prefix: "examples/imagescale/sample"  
  13. momentum: 0.9  
  14. # solver mode: CPU or GPU  
  15. solver_mode: GPU  




然后运行的时候仅仅需要写个bash文件到caffe的目录:
[plain] view plain copy
  1. #!/usr/bin/env sh  
  2. set -e  
  3.   
  4. snap_dir="examples/imagescale/snapshots"  
  5.   
  6. mkdir -p $snap_dir  
  7.   
  8. TOOLS=./build/tools  
  9.   
  10.   
  11. $TOOLS/caffe train \  
  12. --solver=examples/imagescale/solver.prototxt 2>&1 | tee -a $snap_dir/train.log  


下面给出我的结果:


小的是输入的原始图像,大的是经过缩放之后的图像。