1. 程式人生 > >caffe 實戰系列:如何寫自己的資料層(以Deep Spatial Net為例)

caffe 實戰系列:如何寫自己的資料層(以Deep Spatial Net為例)

一、前言

想寫自己的層,首先必須得在caffe.proto中定義自己層的引數,以便於在proto配置檔案中對引數進行配置啦什麼的,其次你還要在caffe.proto宣告你的層的引數是可選的,然後你得在caffe的include目錄下新增你自己層的hpp標頭檔案,以及在caffe的src下的layer目錄下新增你自己的cpp實現檔案。

二、具體做法

(1)首先需要在caffe.proto中宣告自己所寫的層使用引數是可選的:

比如,首先在下面紅色的位置加入HeatmapDataParameter
  1. // Layer type-specific parameters.
  2.   //
  3.   // Note: certain layers may have more than one computational engine
  4.   // for their implementation. These layers include an Engine type and
  5.   // engine parameter for selecting the implementation.
  6.   // The default for the engine is set by the ENGINE switch at compile-time.
  7.   optional AccuracyParameter accuracy_param = 102;  
  8.   optional ArgMaxParameter argmax_param = 103;  
  9.   optional ConcatParameter concat_param = 104;  
  10.   optional ContrastiveLossParameter contrastive_loss_param = 105;  
  11.   optional ConvolutionParameter convolution_param = 106;  
  12.   optional DataParameter data_param = 107;  
  13.   optional DropoutParameter dropout_param = 108;  
  14.   optional DummyDataParameter dummy_data_param = 109;  
  15.   optional EltwiseParameter eltwise_param = 110;  
  16.   optional EmbedParameter embed_param = 137;  
  17.   optional ExpParameter exp_param = 111;  
  18.   optional FlattenParameter flatten_param = 135;  
  19.   optional HeatmapDataParameter heatmap_data_param = 140;// 加入自己層的引數
  20.   optional HDF5DataParameter hdf5_data_param = 112;  
  21.   optional HDF5OutputParameter hdf5_output_param = 113;  
  22.   optional HingeLossParameter hinge_loss_param = 114;  
  23.   optional ImageDataParameter image_data_param = 115;  
  24.   optional InfogainLossParameter infogain_loss_param = 116;  
  25.   optional InnerProductParameter inner_product_param = 117;  
  26.   optional LogParameter log_param = 134;  
  27.   optional LRNParameter lrn_param = 118;  
  28.   optional MemoryDataParameter memory_data_param = 119;  
  29.   optional MVNParameter mvn_param = 120;  
  30.   optional PoolingParameter pooling_param = 121;  
  31.   optional PowerParameter power_param = 122;  
  32.   optional PReLUParameter prelu_param = 131;  
  33.   optional PythonParameter python_param = 130;  
  34.   optional ReductionParameter reduction_param = 136;  
  35.   optional ReLUParameter relu_param = 123;  
  36.   optional ReshapeParameter reshape_param = 133;  
  37.   optional SigmoidParameter sigmoid_param = 124;  
  38.   optional SoftmaxParameter softmax_param = 125;  
  39.   optional SPPParameter spp_param = 132;  
  40.   optional SliceParameter slice_param = 126;  
  41.   optional TanHParameter tanh_param = 127;  
  42.   optional ThresholdParameter threshold_param = 128;  
  43.   optional TileParameter tile_param = 138;  
  44.   optional WindowDataParameter window_data_param = 129;  
  45. }  

因為我們是將引數定義在了V1LayerParameter層下面的,需要在\src\caffe\util下的upgrade_proto.cpp中加入如下幾行程式碼,方便已經訓練好的模型進行轉換。
  1. constchar* UpgradeV1LayerType(const V1LayerParameter_LayerType type) {  
  2.   switch (type) {  
  3.   case V1LayerParameter_LayerType_NONE:  
  4.     return"";  
  5.   case V1LayerParameter_LayerType_ABSVAL:  
  6.     return"AbsVal";  
  7.   case V1LayerParameter_LayerType_ACCURACY:  
  8.     return"Accuracy";  
  9.   case V1LayerParameter_LayerType_ARGMAX:  
  10.     return"ArgMax";  
  11.   case V1LayerParameter_LayerType_BNLL:  
  12.     return"BNLL";  
  13.   case V1LayerParameter_LayerType_CONCAT:  
  14.     return"Concat";  
  15.   case V1LayerParameter_LayerType_CONTRASTIVE_LOSS:  
  16.     return"ContrastiveLoss";  
  17.   case V1LayerParameter_LayerType_CONVOLUTION:  
  18.     return"Convolution";  
  19.   case V1LayerParameter_LayerType_DECONVOLUTION:  
  20.     return"Deconvolution";  
  21.   case V1LayerParameter_LayerType_DATA:  
  22.     return"Data";  
  23.   case V1LayerParameter_LayerType_DATA_HEATMAP:// 這是我們自己新增的輸入資料的層
  24.     return"DataHeatmap";      
  25.   case V1LayerParameter_LayerType_DROPOUT:  
  26.     return"Dropout";  
  27.   case V1LayerParameter_LayerType_DUMMY_DATA:  
  28.     return"DummyData";  
  29.   case V1LayerParameter_LayerType_EUCLIDEAN_LOSS:  
  30.     return"EuclideanLoss";  
  31.   case V1LayerParameter_LayerType_EUCLIDEAN_LOSS_HEATMAP:// 這是我們自己新增的計算損失函式的層
  32.     return"EuclideanLossHeatmap";      
  33.   case V1LayerParameter_LayerType_ELTWISE:  
  34.     return"Eltwise";  
  35.   case V1LayerParameter_LayerType_EXP:  
  36.     return"Exp";  
  37.   case V1LayerParameter_LayerType_FLATTEN:  
  38.     return"Flatten";  
  39.   case V1LayerParameter_LayerType_HDF5_DATA:  
  40.     return"HDF5Data";  
  41.   case V1LayerParameter_LayerType_HDF5_OUTPUT:  
  42.     return"HDF5Output";  
  43.   case V1LayerParameter_LayerType_HINGE_LOSS:  
  44.     return"HingeLoss";  
  45.   case V1LayerParameter_LayerType_IM2COL:  
  46.     return"Im2col";  
  47.   case V1LayerParameter_LayerType_IMAGE_DATA:  
  48.     return"ImageData";  
  49.   case V1LayerParameter_LayerType_INFOGAIN_LOSS:  
  50.     return"InfogainLoss";  
  51.   case V1LayerParameter_LayerType_INNER_PRODUCT:  
  52.     return"InnerProduct";  
  53.   case V1LayerParameter_LayerType_LRN:  
  54.     return"LRN";  
  55.   case V1LayerParameter_LayerType_MEMORY_DATA:  
  56.     return"MemoryData";  
  57.   case V1LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS:  
  58.     return"MultinomialLogisticLoss";  
  59.   case V1LayerParameter_LayerType_MVN:  
  60.     return"MVN";  
  61.   case V1LayerParameter_LayerType_POOLING:  
  62.     return"Pooling";  
  63.   case V1LayerParameter_LayerType_POWER:  
  64.     return"Power";  
  65.   case V1LayerParameter_LayerType_RELU:  
  66.     return"ReLU";  
  67.   case V1LayerParameter_LayerType_SIGMOID:  
  68.     return"Sigmoid";  
  69.   case V1LayerParameter_LayerType_SIGMOID_CROSS_ENTROPY_LOSS:  
  70.     return"SigmoidCrossEntropyLoss";  
  71.   case V1LayerParameter_LayerType_SILENCE:  
  72.     return"Silence";  
  73.   case V1LayerParameter_LayerType_SOFTMAX:  
  74.     return"Softmax";  
  75.   case V1LayerParameter_LayerType_SOFTMAX_LOSS:  
  76.     return"SoftmaxWithLoss";  
  77.   case V1LayerParameter_LayerType_SPLIT:  
  78.     return"Split";  
  79.   case V1LayerParameter_LayerType_SLICE:  
  80.     return"Slice";  
  81.   case V1LayerParameter_LayerType_TANH:  
  82.     return"TanH";  
  83.   case V1LayerParameter_LayerType_WINDOW_DATA:  
  84.     return"WindowData";  
  85.   case V1LayerParameter_LayerType_THRESHOLD:  
  86.     return"Threshold";  
  87.   default:  
  88.     LOG(FATAL) << "Unknown V1LayerParameter layer type: " << type;  
  89.     return"";  
  90.   }  
  91. }  


(2)然後在caffe.proto中下面的位置加入你自己的層的引數:

  1. // VGG heatmap params 自己層的引數
  2. message HeatmapDataParameter {  
  3.   optional bool segmentation = 1000 [default = false];   
  4.   optional uint32 multfact = 1001 [default = 1];  
  5.   optional uint32 num_channels = 1002 [default = 3];  
  6.   optional uint32 batchsize = 1003;  
  7.   optional string root_img_dir = 1004;  
  8.   optional bool random_crop = 1005;   // image augmentation type
  9.   optional bool sample_per_cluster = 1006;   // image sampling type
  10.   optional string labelinds = 1007 [default = ''];   // if specified, only use these regression variables
  11.   optional string source = 1008;  
  12.   optional string meanfile = 1009;  
  13.   optional string crop_meanfile = 1010;  
  14.   optional uint32 cropsize = 1011 [default = 0];  
  15.   optional uint32 outsize = 1012 [default = 0];  
  16.   optional float scale = 1013 [ default = 1 ];  
  17.   optional uint32 label_width = 1014 [ default = 1 ];  
  18.   optional uint32 label_height = 1015 [ default = 1 ];  
  19.   optional bool dont_flip_first = 1016 [ default = true ];  
  20.   optional float angle_max = 1017 [ default = 0 ];   
  21.   optional bool flip_joint_labels = 1018 [ default = true ];  
  22. }  
還有視覺化的測試引數
  1. / NOTE  
  2. // Update the next available ID when you add a new LayerParameter field.  
  3. //  
  4. // LayerParameter next available layer-specific ID: 139 (last added: tile_param)  
  5. message LayerParameter {  
  6.   optional string name = 1; // the layer name  
  7.   optional string type = 2; // the layer type  
  8.   repeated string bottom = 3; // the name of each bottom blob  
  9.   repeated string top = 4; // the name of each top blob  
  10.   // The train / test phase for computation.  
  11.   optional Phase phase = 10;  
  12.   // The amount of weight to assign each top blob in the objective.  
  13.   // Each layer assigns a default value, usually of either 0 or 1,  
  14.   // to each top blob.  
  15.   repeated float loss_weight = 5;  
  16.   // Specifies training parameters (multipliers on global learning constants,  
  17.   // and the name and other settings used for weight sharing).  
  18.   repeated ParamSpec param = 6;  
  19.   // The blobs containing the numeric parameters of the layer.  
  20.   repeated BlobProto blobs = 7;  
  21.   // Specifies on which bottoms the backpropagation should be skipped.  
  22.   // The size must be either 0 or equal to the number of bottoms.  
  23.   repeated bool propagate_down = 11;  
  24.   // Rules controlling whether and when a layer is included in the network,  
  25.   // based on the current NetState.  You may specify a non-zero number of rules  
  26.   // to include OR exclude, but not both.  If no include or exclude rules are  
  27.   // specified, the layer is always included.  If the current NetState meets  
  28.   // ANY (i.e., one or more) of the specified rules, the layer is  
  29.   // included/excluded.  
  30.   repeated NetStateRule include = 8;  
  31.   repeated NetStateRule exclude = 9;  
  32.   // Parameters for data pre-processing.  
  33.   optional TransformationParameter transform_param = 100;  
  34.   // Parameters shared by loss layers.  
  35.   optional LossParameter loss_param = 101;  
  36.   // Options to allow visualisation視覺化層的引數,就這兩貨哈  
  37.   optional bool visualise = 200 [ default = false ];  
  38.   optional uint32 visualise_channel = 201 [ default = 0 ];  



下面對各個引數進行解釋: segmentation            是否分割,預設是否, 假設影象的分割模板在segs/目錄 multfact                    將ground truth中的關節乘以這個multfact,就是影象中的位置,影象中的位置除以這個就是關節的位置,預設是1,也就是說關節的座標與影象的座標是一致大小的 num_channels           影象的channel數預設是3 batchsize                    batch大小 root_img_dir               存放影象檔案的根目錄 random_crop              是否需要隨機crop影象(如果true則做隨機crop,否則做中心crop) sample_per_cluster     影象取樣的型別(是否均勻地在clusters上取樣) labelinds                     類標索引(只使用迴歸變數才設定這個) source                        存放打亂檔案順序之後的檔案路徑的txt檔案 meanfile                    平均值檔案路徑 crop_meanfile           crop之後的平均值檔案路徑 cropsize                    crop的大小 outsize                      預設是0(就是crop出來之後的影象會縮放的因子,0表示不縮放) scale                         預設是1,實際上就是一系列預處理(去均值、crop、縮放之後的畫素值乘以該scale得到最終的影象的) label_width               heatmap的寬 label_height               heatmap的高 dont_flip_first              不要對調第一個關節的位置,預設是true angle_max              對影象進行旋轉的最大角度,用於增強資料的,預設是0度 flip_joint_labels          預設是true(即水平翻轉,將左右的關節對調) 為了保證完整性,把英文解釋全部: - visualise: show visualisations for crops, rotations etc (recommended for testing) - source: label file - root_img_dir: directory with images (recommend you store images on ramdisk) - meanfile: proto file containing the mean image(s) to be subtracted (optional) - cropsize: size of random crop (randomly cropped from the original image) - outsize: size that crops are resized to - multfact: label coordinates in the ground truth text file are multiplied by this (default 1) - sample_per_cluster: sample evenly across clusters - random_crop: do random crop (if false, do center crop) - label_height/width: width of regressed heatmap (must match net config) - segmentation: segment images on the fly (assumes images are in a segs/ directory) - angle_max: max rotation angle for training augmentation - flip_joint_labels: when horizontally flipping images for augmentation, if this is set to true the code also swaps left<->right labels (this is important e.g. for observer-centric pose estimation). This assumes that the left,right joint labelsare listed consecutively (e.g. wrist_left,wrist_right,elbow_left,elbow_right) - dont_flip_first: This option allows you to turn off label mirroring for the first label. E.g. for labels head,wrist_right,wrist_left,elbow_right,elbow_left,shoulder_right,shoulder_left, the first joint is head and should not be swapped with wrist_right.

(3)這樣,你就可以在proto中配置你自己層的引數了

下面給出一個配置heatmapdata層的例項:
  1. layer {  
  2.   name: "data"
  3.   type: "DataHeatmap"// 層的型別是DataHeatmap
  4.   top: "data"
  5.   top: "label"
  6.   visualise: false// 是否視覺化
  7.   include: { phase: TRAIN }     
  8.   heatmap_data_param {  
  9.     source: "/data/tp/flic/train_shuffle.txt"
  10.     root_img_dir: "/mnt/ramdisk/tp/flic/"
  11.     batchsize: 14  
  12.     cropsize: 248  
  13.     outsize: 256  
  14.     sample_per_cluster: false
  15.     random_crop: true
  16.     label_width: 64  
  17.     label_height: 64  
  18.     segmentation: false
  19.     flip_joint_labels: true
  20.     dont_flip_first: true
  21.     angle_max: 40     
  22.     multfact: 1  # set to 282 ifusing preprocessed data from website  
  23.   }  
  24. }  


(4)heatmapdata層的實現

1)在介紹實現之前需要給出我們的訓練資料的樣子 看完引數,我們看一下訓練的資料的格式感性理解一下: 下面給出一個樣例: train/FILE.jpg 123,144,165,123,66,22 372.296,720,1,480,0.53333 0 下面對樣例做出解釋 引數之間是以空格分隔 第一個引數是影象的路徑:train/FILE.jpg 第二個引數是關節座標:123,144,165,123,66,22 第三個引數是crop和scale的引數,分別為x_left,x_right,y_left,y_right,scaling_fact:372.296,720,1,480,0.53333 注意:第三個引數的crop的座標其實上針對的是mean影象的,在mean影象中進行crop,然後放大到與原始影象一樣大小,然後原始影象減去經過crop且放大之後的mean影象。這樣在對原始影象進行crop的時候就不用擔心了 第四個引數是是否cluster,是否均勻地在訓練中取樣影象: 0 This is a space-delimited file where the first arg is the path to your image the second arg is a comma-delimited list of (x,y) coordinates you wish to regress (the coordinates in the train/FILE.jpg image space) the third arg is a comma-delimited list of crops & scaling factors of the input image (in order x_left,x_right,y_left,y_right,scaling_fact). Note: These crop & scaling factors are only used to crop the mean image. You can set these to 0 if you aren't using a mean image (for mean subtraction). the fourth arg is a coordinate 'cluster' (from which you have the option to evenly sample images in training). You can set this to 0. 2)在講解該層如何實現之前首先介紹點預備知識: ①首先給出在opencv中如何crop一幅影象
  1. // You mention that you start with a CVMat* imagesource
  2. CVMat * imagesource;  
  3. // Transform it into the C++ cv::Mat format
  4. cv::Mat image(imagesource);  
  5. // Setup a rectangle to define your region of interest
  6. cv::Rect myROI(10, 10, 100, 100);  
  7. // Crop the full image to that image contained by the rectangle myROI
  8. // Note that this doesn't copy the data
  9. cv::Mat croppedImage = image(myROI);  


②如何進行隨機crop以及中心crop
上圖中的黃色邊框表示影象 藍色邊框表示x_border = x-cropsize以及y_border=y-cropsize大小的crop區域 如果隨機crop則表示從[0,x_border-1]以及[0,y_border-1]大小的區域(也就是圖中的藍色矩形框內)隨機採集一個點座標crop的左上角的點,然後以cropsize為邊長取一個正方型。 如果是中心crop則取圖中兩個虛線的交點,即藍色矩形的中心座標crop的左上角的點,然後以cropsize為邊長取一個正方形。 3)我們所寫的層應該繼承那個基類 我們所寫的HeatmapData層是繼承自BasePrefetchingDataLayer的(在檔案data_layers.hpp中),下面給出其定義
  1. template <typename Dtype>  
  2. class BasePrefetchingDataLayer :  
  3.     public BaseDataLayer<Dtype>, public InternalThread {  
  4.  public:  
  5.   explicit BasePrefetchingDataLayer(const LayerParameter& param);  
  6.   // LayerSetUp: implements common data layer setup functionality, and calls
  7.   // DataLayerSetUp to do special data layer setup for individual layer types.
  8.   // This method may not be overridden.
  9.   void LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  10.       const vector<Blob<Dtype>*>& top);  
  11.   virtualvoid Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
  12.       const vector<Blob<Dtype>*>& top);  
  13.   virtualvoid Forward_gpu(const vector<Blob<Dtype>*>& bottom,  
  14.       const vector<Blob<Dtype>*>& top);  
  15.   // Prefetches batches (asynchronously if to GPU memory)
  16.   staticconstint PREFETCH_COUNT = 3  
  17.  protected:  
  18.   virtualvoid InternalThreadEntry();  
  19.   virtualvoid load_batch(Batch<Dtype>* batch) = 0;  
  20.   Batch<Dtype> prefetch_[PREFETCH_COUNT];  
  21.   BlockingQueue<Batch<Dtype>*> prefetch_free_;  
  22.   BlockingQueue<Batch<Dtype>*> prefetch_full_;  
  23.   Blob<Dtype> transformed_data_;  
  24. };  


4)實現自己的層 首先定義層的標頭檔案
  1. // Copyright 2014 Tomas Pfister
  2. #ifndef CAFFE_HEATMAP_HPP_
  3. #define CAFFE_HEATMAP_HPP_
  4. #include "caffe/layer.hpp"
  5. #include <vector>
  6. #include <boost/timer/timer.hpp>
  7. #include <opencv2/core/core.hpp>
  8. #include "caffe/common.hpp"
  9. #include "caffe/data_transformer.hpp"
  10. #include "caffe/filler.hpp"
  11. #include "caffe/internal_thread.hpp"
  12. #include "caffe/proto/caffe.pb.h"
  13. namespace caffe  
  14. {  
  15. // 繼承自PrefetchingDataLayer
  16. template<typename Dtype>  
  17. class DataHeatmapLayer: public BasePrefetchingDataLayer<Dtype>  
  18. {  
  19. public:  
  20.     explicit DataHeatmapLayer(const LayerParameter& param)  
  21.         : BasePrefetchingDataLayer<Dtype>(param) {}  
  22.     virtual ~DataHeatmapLayer();  
  23.     virtualvoid DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  24.                                 const vector<Blob<Dtype>*>& top);  
  25.     virtualinlineconstchar* type() const { return"DataHeatmap"; }  
  26.     virtualinlineint ExactNumBottomBlobs() const { return 0; }  
  27.     virtualinlineint ExactNumTopBlobs() const { return 2; }  
  28. protected:  
  29.     // 虛擬函式,就是實際讀取一批資料到Batch中
  30.     virtualvoid load_batch(Batch<Dtype>* batch);  
  31.     // 以下都是自己定義的要使用的函式,都在load_batch中被呼叫了
  32.     // Filename of current image
  33.     inlinevoid GetCurImg(string& img_name, std::vector<float>& img_class, std::vector<float>& crop_info, int& cur_class);  
  34.     inlinevoid AdvanceCurImg();  
  35.     // Visualise point annotations
  36.     inlinevoid VisualiseAnnotations(cv::Mat img_annotation_vis, int numChannels, std::vector<float>& cur_label, int width);  
  37.     // Random number generator
  38.     inlinefloat Uniform(constfloat min, constfloat max);  
  39.     // Rotate image for augmentation
  40.     inline cv::Mat RotateImage(cv::Mat src, float rotation_angle);  
  41.     // Global vars
  42.     shared_ptr<Caffe::RNG> rng_data_;  
  43.     shared_ptr<Caffe::RNG> prefetch_rng_;  
  44.     vector<std::pair<std::string, int> > lines_;  
  45.     int lines_id_;     
  46.     int datum_channels_;  
  47.     int datum_height_;  
  48.     int datum_width_;  
  49.     int datum_size_;  
  50.     int num_means_;  
  51.     int cur_class_;  
  52.     vector<int> labelinds_;  
  53.     vector<cv::Mat> mean_img_;  
  54.     bool sub_mean_;  // true if the mean should be subtracted
  55.     bool sample_per_cluster_; // sample separately per cluster?
  56.     string root_img_dir_;  
  57.     vector<float> cur_class_img_; // current class index
  58.     int cur_img_; // current image index
  59.     vector<int> img_idx_map_; // current image indices for each class
  60.     // array of lists: one list of image names per class
  61.     vector< vector< pair<string, pair<vector<float>, pair<vector<float>, int> > > > > img_list_;  
  62.     // vector of (image, label) pairs
  63.     vector< pair<string, pair<vector<float>, pair<vector<float>, int> > > > img_label_list_;     
  64. };  
  65. }  
  66. #endif /* CAFFE_HEATMAP_HPP_ */


在介紹詳細實現之前先口述一下實現的流程: 1)首先在SetUp該函式中讀取,proto中的引數,從而獲得一批資料的大小、heatmap的長和寬,對影象進行切割的大小,以及切割後的影象需要縮放到多大,還有就是是否需要對每個類別的影象進行取樣、放置影象的根目錄等資訊。 此外還讀取每個影象檔案的路徑、關節的座標位置、crop的位置、是否進行取樣。 如果在每個類上進行取樣,還會生成一個數組,該陣列對應的是影象的類別索引與影象的索引之間的對映。 此外還從檔案中讀取每個視訊的mean,然後將所讀取的mean放到vector容器中,便於在讀取資料的時候從影象中取出mean。最後還會設定top的形狀 2)在load_batch這個函式中就是真正地讀取資料,並且對資料進行預處理,預處理主要是是否對影象進行分割,對平均值影象進行切割,並將切割的影象塊放大到影象的大小,然後用影象減去該段視訊切割並方法的平均值影象(你會不會覺得很奇怪,我也覺得很奇怪。。。竟然是切割平均值影象的,然後放大到與原影象一樣的大小,然後再用原影象減去該均值影象,主要是原理我沒想明白)。
  1. // Copyright 2015 Tomas Pfisterimg
  2. #include <fstream>  // NOLINT(readability/streams)
  3. #include <iostream>  // NOLINT(readability/streams)
  4. #include <string>
  5. #include <utility>
  6. #include <vector>
  7. #include "caffe/data_layers.hpp"
  8. #include "caffe/layer.hpp"
  9. #include "caffe/util/io.hpp"
  10. #include "caffe/util/math_functions.hpp"
  11. #include "caffe/util/rng.hpp"
  12. #include <stdint.h>
  13. #include <cmath>
  14. #include <opencv2/core/core.hpp>
  15. #include <opencv2/highgui/highgui.hpp>
  16. #include <opencv2/highgui/highgui_c.h>
  17. #include <opencv2/imgproc/imgproc.hpp>
  18. #include "caffe/layers/data_heatmap.hpp"
  19. #include "caffe/util/benchmark.hpp"
  20. #include <unistd.h>
  21. namespace caffe  
  22. {  
  23. template <typename Dtype>  
  24. DataHeatmapLayer<Dtype>::~DataHeatmapLayer<Dtype>() {  
  25.     this->StopInternalThread();  
  26. }  
  27. // 讀取引數檔案中的一些資料什麼的,然後初始化
  28. template<typename Dtype>  
  29. void DataHeatmapLayer<Dtype>::DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  30.         const vector<Blob<Dtype>*>& top) {  
  31.     HeatmapDataParameter heatmap_data_param = this->layer_param_.heatmap_data_param();  
  32.     // Shortcuts
  33.     // 類標索引字串(也就是關節型別?)
  34.     const std::string labelindsStr = heatmap_data_param.labelinds();  
  35.     // batchsize
  36.     constint batchsize = heatmap_data_param.batchsize();  
  37.     // heatmap的寬度
  38.     constint label_width = heatmap_data_param.label_width();  
  39.     // heatmap的高度
  40.     constint label_height = heatmap_data_param.label_height();  
  41.     // crop的大小
  42.     constint size = heatmap_data_param.cropsize();  
  43.     // crop之後再次進行resize之後的大小
  44.     constint outsize = heatmap_data_param.outsize();  
  45.     //  label的batchsize
  46.     constint label_batchsize = batchsize;  
  47.     // 每個cluster都要進行取樣
  48.     sample_per_cluster_ = heatmap_data_param.sample_per_cluster();  
  49.     // 存放影象檔案的根路徑
  50.     root_img_dir_ = heatmap_data_param.root_img_dir();  
  51.     // initialise rng seed
  52.     const unsigned int rng_seed = caffe_rng_rand();  
  53.     srand(rng_seed);  
  54.     // get label inds to be used for training
  55.     // 載入類標索引
  56.     std::istringstream labelss(labelindsStr);  
  57.     LOG(INFO) << "using joint inds:";  
  58.     while (labelss)  
  59.     {  
  60.         std::string s;  
  61.         if (!std::getline(labelss, s, ',')) break;  
  62.         labelinds_.push_back(atof(s.c_str()));  
  63.         LOG(INFO) << atof(s.c_str());  
  64.     }  
  65.     // load GT
  66.     // shuffle file
  67.     // 載入ground truth檔案,即關節座標檔案
  68.     std::string gt_path = heatmap_data_param.source();  
  69.     LOG(INFO) << "Loading annotation from " << gt_path;  
  70.     std::ifstream infile(gt_path.c_str());  
  71.     string img_name, labels, cropInfos, clusterClassStr;  
  72.     if (!sample_per_cluster_)// 是否根據你指定的類別隨機取影象
  73.     {  
  74.         // sequential sampling
  75.         // 檔名,關節位置座標,crop的位置,是否均勻地在clusters上取樣
  76.         while (infile >> img_name >> labels >> cropInfos >> clusterClassStr)  
  77.         {  
  78.             // read comma-separated list of regression labels
  79.             // 讀取關節位置座標
  80.             std::vector <float> label;  
  81.             std::istringstream ss(labels);  
  82.             int labelCounter = 1;  
  83.             while (ss)  
  84.             {  
  85.                 // 讀取一個數字
  86.                 std::string s;  
  87.                 if (!std::getline(ss, s, ',')) break;  
  88.                 // 是否是類標索引中的值
  89.                 // 如果labelinds為空或者為不為空在其中找到
  90.                 if (labelinds_.empty() || std::find(labelinds_.begin(), labelinds_.end(), labelCounter) != labelinds_.end())  
  91.                 {  
  92.                     label.push_back(atof(s.c_str()));  
  93.                 }  
  94.                 labelCounter++;// 個數
  95.             }  
  96.             // read cropping info
  97.             // 讀取crop的資訊
  98.             std::vector <float> cropInfo;  
  99.             std::istringstream ss2(cropInfos);  
  100.             while (ss2)  
  101.             {  
  102.                 std::string s;  
  103.                 if (!std::getline(ss2, s, ',')) break;  
  104.                 cropInfo.push_back(atof(s.c_str()));  
  105.             }  
  106.             int clusterClass = atoi(clusterClassStr.c_str());  
  107.             // 影象路徑,關節座標,crop資訊、類別
  108.             img_label_list_.push_back(std::make_pair(img_name, std::make_pair(label, std::make_pair(cropInfo, clusterClass))));  
  109.         }  
  110.         // initialise image counter to 0
  111.         cur_img_ = 0;  
  112.     }  
  113.     else
  114.     {  
  115.         // uniform sampling w.r.t. classes
  116.         // 根據類別均勻取樣
  117.         // 也就是說影象有若干個類別,然後每個類別下有若干個影象
  118.         // 隨機取其中一個影象
  119.         while (infile >> img_name >> labels >> cropInfos >> clusterClassStr)  
  120.         {  
  121.             // 獲得你指定的類別
  122.             // 如果你制定為0
  123.             int clusterClass = atoi(clusterClassStr.c_str());  
  124.         // 那麼
  125.             if (clusterClass + 1 > img_list_.size())  
  126.             {  
  127.                 // expand the array
  128.                 img_list_.resize(clusterClass + 1);  
  129.             }  
  130.             // read comma-separated list of regression labels
  131.             // 讀取關節的座標位置到label這個vector
  132.             std::vector <float> label;  
  133.             std::istringstream ss(labels);  
  134.             int labelCounter = 1;  
  135.             while (ss)  
  136.             {  
  137.                 std::string s;  
  138.                 if (!std::getline(ss, s, ',')) break;  
  139.                 if (labelinds_.empty() || std::find(labelinds_.begin(), labelinds_.end(), labelCounter) != labelinds_.end())  
  140.                 {  
  141.                     label.push_back(atof(s.c_str()));  
  142.                 }  
  143.                 labelCounter++;  
  144.             }  
  145.             // read cropping info
  146.             // 讀取crop資訊到cropinfo這個vector
  147.             std::vector <float> cropInfo;  
  148.             std::istringstream ss2(cropInfos);  
  149.             while (ss2)  
  150.             {  
  151.                 std::string s;  
  152.                 if (!std::getline(ss2, s, ',')) break;  
  153.                 cropInfo.push_back(atof(s.c_str()));  
  154.             }  
  155.         // 每個clusterClass下都是一個vector,用於裝各種影象
  156.             img_list_[clusterClass].push_back(std::make_pair(img_name, std::make_pair(label, std::make_pair(cropInfo, clusterClass))));  
  157.         }// while結尾
  158.       // 影象的類別個數
  159.         constint num_classes = img_list_.size();  
  160.         // init image sampling
  161.         cur_class_ = 0;  
  162.         // cur_class_img_中存放的是某個類別中隨機取到的影象的索引值
  163.         cur_class_img_.resize(num_classes);  
  164.         // init image indices for each c