caffe.proto中TransformationParameter部分

// Message that stores parameters used to apply transformation
// to the data layer's data
message TransformationParameter {
  // For data pre-processing, we can do simple scaling and subtracting the
  // data mean, if provided. Note that the mean subtraction is always carried
  // out before scaling.
  //像素幅度缩放参数,默认不缩放
  optional float scale = 1 [default = 1];
  // Specify if we want to randomly mirror data.
  //图像随机镜像开关。默认false,不进行随机镜像操作
  optional bool mirror = 2 [default = false];
  // Specify if we would like to randomly crop an image.
  //图像随机切块的大小。默认不切块。
  optional uint32 crop_size = 3 [default = 0];
  // mean_file and mean_value cannot be specified at the same time
  //存储图像均值的文件
  optional string mean_file = 4;
  // if specified can be repeated once (would subtract it from all the channels)
  // or can be repeated the same number of times as channels
  // (would subtract them from the corresponding channel)
  //均值数值。无需读取文件。
  //如果均值数目和通道数一致,则每个通道分别减去对应均值;
  //如果没有均值只有一个,则每个通道减去相同的均值。
  repeated float mean_value = 5;
  // Force the decoded image to have 3 color channels.
  //强制为三通道三色图像输入 默认false
  optional bool force_color = 6 [default = false];
  // Force the decoded image to have 1 color channels.
  //强制为单通道灰度图像输入 默认false
  optional bool force_gray = 7 [default = false];
}

caffe.proto中Datum部分

 

//Datum用来从LMDB/LEVELDB 中读取数据,或将数据写入其中。和BlobProto有相似功能,只是
//BlobProto用于模型权值序列化反序列化,而Datum用于数据或特征图(feature map)提供序列化反序列化
message Datum {
  //数据维度信息:channels*height*width
  optional int32 channels = 1;
  optional int32 height = 2;
  optional int32 width = 3;
  // the actual image data, in bytes
  //图像数据,以字节类型存储
  optional bytes data = 4;
  //标签数据,统一用int32类型存储
  optional int32 label = 5;
  // Optionally, the datum could also hold float data.
  repeated float float_data = 6;
  // If true data contains an encoded image that need to be decoded
  optional bool encoded = 7 [default = false];//是否为编码数据,默认false
}

 

 

 

 include/caffe/data_transformer.hpp

  1 #ifndef CAFFE_DATA_TRANSFORMER_HPP
  2 #define CAFFE_DATA_TRANSFORMER_HPP
  3 
  4 #include <vector>
  5 
  6 #include "caffe/blob.hpp"
  7 #include "caffe/common.hpp"
  8 #include "caffe/proto/caffe.pb.h"
  9 
 10 namespace caffe {
 11 
 12 /**
 13  * @brief Applies common transformations to the input data, such as
 14  * scaling, mirroring, substracting the image mean...
 15  */
 16 template <typename Dtype>
 17 class DataTransformer {
 18  public:
 19   explicit DataTransformer(const TransformationParameter& param, Phase phase);
 20   virtual ~DataTransformer() {}
 21 
 22   /**
 23    * @brief Initialize the Random number generations if needed by the
 24    *    transformation.
 25    */
 26   //初始化随机数种子函数
 27   void InitRand();
 28 
 29   /**
 30    * @brief Applies the transformation defined in the data layer's
 31    * transform_param block to the data.
 32    *
 33    * @param datum
 34    *    Datum containing the data to be transformed.
 35    * @param transformed_blob
 36    *    This is destination blob. It can be part of top blob's data if
 37    *    set_cpu_data() is used. See data_layer.cpp for an example.
 38    */
 39   //数据读取层中transform_param块所声明的变换应用到输入数据中。
 40   //以下为多个重载函数
 41   void Transform(const Datum& datum, Blob<Dtype>* transformed_blob);
 42 
 43   /**
 44    * @brief Applies the transformation defined in the data layer's
 45    * transform_param block to a vector of Datum.
 46    *
 47    * @param datum_vector
 48    *    A vector of Datum containing the data to be transformed.
 49    * @param transformed_blob
 50    *    This is destination blob. It can be part of top blob's data if
 51    *    set_cpu_data() is used. See memory_layer.cpp for an example.
 52    */
 53   void Transform(const vector<Datum> & datum_vector,
 54                 Blob<Dtype>* transformed_blob);
 55 //使用OPENCV
 56 #ifdef USE_OPENCV
 57   /**
 58    * @brief Applies the transformation defined in the data layer's
 59    * transform_param block to a vector of Mat.
 60    *
 61    * @param mat_vector
 62    *    A vector of Mat containing the data to be transformed.
 63    * @param transformed_blob
 64    *    This is destination blob. It can be part of top blob's data if
 65    *    set_cpu_data() is used. See memory_layer.cpp for an example.
 66    */
 67   void Transform(const vector<cv::Mat> & mat_vector,
 68                 Blob<Dtype>* transformed_blob);
 69 
 70   /**
 71    * @brief Applies the transformation defined in the data layer's
 72    * transform_param block to a cv::Mat
 73    *
 74    * @param cv_img
 75    *    cv::Mat containing the data to be transformed.
 76    * @param transformed_blob
 77    *    This is destination blob. It can be part of top blob's data if
 78    *    set_cpu_data() is used. See image_data_layer.cpp for an example.
 79    */
 80   void Transform(const cv::Mat& cv_img, Blob<Dtype>* transformed_blob);
 81 #endif  // USE_OPENCV
 82 
 83   /**
 84    * @brief Applies the same transformation defined in the data layer's
 85    * transform_param block to all the num images in a input_blob.
 86    *
 87    * @param input_blob
 88    *    A Blob containing the data to be transformed. It applies the same
 89    *    transformation to all the num images in the blob.
 90    * @param transformed_blob
 91    *    This is destination blob, it will contain as many images as the
 92    *    input blob. It can be part of top blob's data.
 93    */
 94   void Transform(Blob<Dtype>* input_blob, Blob<Dtype>* transformed_blob);
 95 
 96   /**
 97    * @brief Infers the shape of transformed_blob will have when
 98    *    the transformation is applied to the data.
 99    *
100    * @param datum
101    *    Datum containing the data to be transformed.
102    */
103    //获取执行变换后输出Blob的形状
104   vector<int> InferBlobShape(const Datum& datum);
105   /**
106    * @brief Infers the shape of transformed_blob will have when
107    *    the transformation is applied to the data.
108    *    It uses the first element to infer the shape of the blob.
109    *
110    * @param datum_vector
111    *    A vector of Datum containing the data to be transformed.
112    */
113   vector<int> InferBlobShape(const vector<Datum> & datum_vector);
114   /**
115    * @brief Infers the shape of transformed_blob will have when
116    *    the transformation is applied to the data.
117    *    It uses the first element to infer the shape of the blob.
118    *
119    * @param mat_vector
120    *    A vector of Mat containing the data to be transformed.
121    */
122 #ifdef USE_OPENCV
123   vector<int> InferBlobShape(const vector<cv::Mat> & mat_vector);
124   /**
125    * @brief Infers the shape of transformed_blob will have when
126    *    the transformation is applied to the data.
127    *
128    * @param cv_img
129    *    cv::Mat containing the data to be transformed.
130    */
131   vector<int> InferBlobShape(const cv::Mat& cv_img);
132 #endif  // USE_OPENCV
133 
134  protected:
135    /**
136    * @brief Generates a random integer from Uniform({0, 1, ..., n-1}).
137    *
138    * @param n
139    *    The upperbound (exclusive) value of the random number.
140    * @return
141    *    A uniformly random integer value from ({0, 1, ..., n-1}).
142    */
143   //产生取值0到n-1的随机整数,服从均匀分布
144   virtual int Rand(int n);
145 
146   
147   void Transform(const Datum& datum, Dtype* transformed_data);
148   // Tranformation parameters
149   //变换参数,该数据结构由ProtoBuffer工具自动生成
150   TransformationParameter param_;
151 
152   //随机数生成器。声明在include/caffe/common.hpp中
153   shared_ptr<Caffe::RNG> rng_;
154   Phase phase_;//当前运行阶段。TRAIN或TEST。阶段不同,变换会有差异
155   Blob<Dtype> data_mean_;//均值图像,从均值文件中读取
156   vector<Dtype> mean_values_;//均值数值,从param_中提取
157 };
158 
159 }  // namespace caffe
160 
161 #endif  // CAFFE_DATA_TRANSFORMER_HPP_

 src/caffe/data_transformer.cpp

  1 #ifdef USE_OPENCV
  2 #include <opencv2/core/core.hpp>
  3 #endif  // USE_OPENCV
  4 
  5 #include <string>
  6 #include <vector>
  7 
  8 #include "caffe/data_transformer.hpp"
  9 #include "caffe/util/io.hpp"
 10 #include "caffe/util/math_functions.hpp"
 11 #include "caffe/util/rng.hpp"
 12 
 13 namespace caffe {
 14 
 15 template<typename Dtype>
 16 DataTransformer<Dtype>::DataTransformer(const TransformationParameter& param,
 17     Phase phase)
 18     : param_(param), phase_(phase) {//初始化
 19   // check if we want to use mean_file
 20   //查看是否使用均值文件
 21   if (param_.has_mean_file()) {
 22     //如果param_中指定了均值文件又指定了均值数值则报错,二选一
 23     CHECK_EQ(param_.mean_value_size(), 0) <<
 24       "Cannot specify mean_file and mean_value at the same time";
 25     const string& mean_file = param.mean_file();//获取均值文件名
 26     if (Caffe::root_solver()) {
 27       LOG(INFO) << "Loading mean file from: " << mean_file;
 28     }
 29     BlobProto blob_proto;//从均值文件中读取数据到blob_proto对象中
 30     ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);
 31     data_mean_.FromProto(blob_proto);//从blob_proto将均值反序列化到data_mean_内存中
 32   }
 33   // check if we want to use mean_value
 34   //查看是否使用均值数值
 35   if (param_.mean_value_size() > 0) {
 36     CHECK(param_.has_mean_file() == false) <<
 37       "Cannot specify mean_file and mean_value at the same time";
 38     for (int c = 0; c < param_.mean_value_size(); ++c) {
 39         //读取均值数值,不再读取均值文件
 40       mean_values_.push_back(param_.mean_value(c));
 41     }
 42   }
 43 }
 44 //若干重载变换函数
 45 //该函数以Datum作为输入,结构体在caffe.proto中可见。输出为数据指针
 46 template<typename Dtype>
 47 void DataTransformer<Dtype>::Transform(const Datum& datum,
 48                                        Dtype* transformed_data) {
 49   //获得datum数据字串、维度信息
 50   const string& data = datum.data();
 51   const int datum_channels = datum.channels();
 52   const int datum_height = datum.height();
 53   const int datum_width = datum.width();
 54 
 55   //从param_获得处理参数。如切块大小、幅度缩放、随机镜像、图像均值等
 56   const int crop_size = param_.crop_size();
 57   const Dtype scale = param_.scale();
 58   const bool do_mirror = param_.mirror() && Rand(2);
 59   const bool has_mean_file = param_.has_mean_file();
 60   const bool has_uint8 = data.size() > 0;
 61   const bool has_mean_values = mean_values_.size() > 0;
 62 
 63   CHECK_GT(datum_channels, 0);//保证输入通道数大于零
 64   CHECK_GE(datum_height, crop_size);//保证输入数据的宽和高大于切块尺寸
 65   CHECK_GE(datum_width, crop_size);
 66 
 67   //获得图像均值
 68   Dtype* mean = NULL;
 69   if (has_mean_file) {//若指定了图像均值文件
 70     //保证图像的均值文件的维度和输入图像数据的维度完全相同
 71     CHECK_EQ(datum_channels, data_mean_.channels());
 72     CHECK_EQ(datum_height, data_mean_.height());
 73     CHECK_EQ(datum_width, data_mean_.width());
 74     mean = data_mean_.mutable_cpu_data();//获得图像均值数据控制权
 75   }
 76   if (has_mean_values) {//未指定图像均值文件,直接给出均值数值
 77     //保证均值数据维数是1,或与输入图像数据的通道数相同
 78     CHECK(mean_values_.size() == 1 || mean_values_.size() == datum_channels) <<
 79      "Specify either 1 mean_value or as many as channels: " << datum_channels;
 80     if (datum_channels > 1 && mean_values_.size() == 1) {
 81       // Replicate the mean_value for simplicity
 82       //如果均值数据维度为1,且输入数据通道数大于1,则重复channels次
 83       for (int c = 1; c < datum_channels; ++c) {
 84         mean_values_.push_back(mean_values_[0]);
 85       }
 86     }
 87   }
 88 
 89   //输入图像的宽和高
 90   int height = datum_height;
 91   int width = datum_width;
 92 
 93   //开始图像切块
 94   int h_off = 0;
 95   int w_off = 0;
 96   if (crop_size) {//若不为0则进行切块,为0不切块
 97     height = crop_size;
 98     width = crop_size;
 99     // We only do random crop when we do training.
100     // 在训练的时候随机crop图像块,这里需要自己实现Rand这个函数来确定是如何随机的
101     if (phase_ == TRAIN) {
102       h_off = Rand(datum_height - crop_size + 1);// 产生从0到datum_height - crop_size的随机数
103       w_off = Rand(datum_width - crop_size + 1);//切块的width偏移量
104     } else {//测试阶段只切取图像中心位置
105       h_off = (datum_height - crop_size) / 2;
106       w_off = (datum_width - crop_size) / 2;
107     }
108   }
109 
110   // 对数据进行变换,主要是将原来的像素值减去均值,然后乘以scale这么一个操作  
111   // 如果需要crop则最终转换的Blob的大小即为crop*crop  
112   // 如果不是,则最终的Blob大小即为datum_height*datum_width  
113   Dtype datum_element;//存放输入图像的像素值
114   int top_index, data_index;//分别存放输出index和输入index
115   for (int c = 0; c < datum_channels; ++c) {
116     for (int h = 0; h < height; ++h) {
117       for (int w = 0; w < width; ++w) {
118         data_index = (c * datum_height + h_off + h) * datum_width + w_off + w;
119         if (do_mirror) {//若需要镜像操作,则输出index时设置width反向
120           top_index = (c * height + h) * width + (width - 1 - w);
121         } else {
122           top_index = (c * height + h) * width + w;
123         }
124         if (has_uint8) {// Datum中如果是uint8存储图像数据则转换为float
125           datum_element =
126             static_cast<Dtype>(static_cast<uint8_t>(data[data_index]));
127         } else {//否则为float
128           datum_element = datum.float_data(data_index);
129         }
130         if (has_mean_file) {//若指定了均值文件
131           transformed_data[top_index] =
132             (datum_element - mean[data_index]) * scale;//去均值,幅度缩放
133         } else {
134           if (has_mean_values) {//若指定了均值数值
135             transformed_data[top_index] =
136               (datum_element - mean_values_[c]) * scale;//去均值,幅度缩放
137           } else {
138             transformed_data[top_index] = datum_element * scale;//不去均值,只幅度缩放
139           }
140         }
141       }
142     }
143   }
144 }
145 
146 
147 //与上一个函数类似,只是输出变成blob
148 template<typename Dtype>
149 void DataTransformer<Dtype>::Transform(const Datum& datum,
150                                        Blob<Dtype>* transformed_blob) {
151   // If datum is encoded, decode and transform the cv::image.
152   if (datum.encoded()) {//  检查datum是否经过编码的图像,如果是则解码 
153 #ifdef USE_OPENCV
154     // 先检查是不是两个属性都设置, 如果是则说明参数设置有误 
155     CHECK(!(param_.force_color() && param_.force_gray()))
156         << "cannot set both force_color and force_gray";
157     cv::Mat cv_img;
158     if (param_.force_color() || param_.force_gray()) {
159     // If force_color then decode in color otherwise decode in gray.
160     // 如果强制彩色或者强制灰度图像一个成立则使用DecodeDatumToCVMat解码 
161       cv_img = DecodeDatumToCVMat(datum, param_.force_color());
162     } else {// 否则使用DecodeDatumToCVMatNative解码
163       cv_img = DecodeDatumToCVMatNative(datum);
164     }
165     // Transform the cv::image into blob.将cv::image 变换为 blob
166     return Transform(cv_img, transformed_blob);
167 #else
168     LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";
169 #endif  // USE_OPENCV
170   } else {// 如果没有编码则检查force_color和force_gray是否设置,如果设置则不合法,因为该选项只适合于编码后的数据 
171     if (param_.force_color() || param_.force_gray()) {
172       LOG(ERROR) << "force_color and force_gray only for encoded datum";
173     }
174   }
175 
176   const int crop_size = param_.crop_size();
177   const int datum_channels = datum.channels();
178   const int datum_height = datum.height();
179   const int datum_width = datum.width();
180 
181   // Check dimensions.检查维度
182   const int channels = transformed_blob->channels();
183   const int height = transformed_blob->height();
184   const int width = transformed_blob->width();
185   const int num = transformed_blob->num();
186 
187   CHECK_EQ(channels, datum_channels);
188   CHECK_LE(height, datum_height);
189   CHECK_LE(width, datum_width);
190   CHECK_GE(num, 1);
191 
192   if (crop_size) {
193     CHECK_EQ(crop_size, height);
194     CHECK_EQ(crop_size, width);
195   } else {
196     CHECK_EQ(datum_height, height);
197     CHECK_EQ(datum_width, width);
198   }
199   // 参数变换完毕,调用现有函数
200   Dtype* transformed_data = transformed_blob->mutable_cpu_data();
201   Transform(datum, transformed_data);
202 }
203 
204 //对一组datum数据进行变换
205 template<typename Dtype>
206 void DataTransformer<Dtype>::Transform(const vector<Datum> & datum_vector,
207                                        Blob<Dtype>* transformed_blob) {
208   const int datum_num = datum_vector.size();
209   const int num = transformed_blob->num();// 变换到的目标blob的形状
210   const int channels = transformed_blob->channels();
211   const int height = transformed_blob->height();
212   const int width = transformed_blob->width();
213 
214   CHECK_GT(datum_num, 0) << "There is no datum to add";
215   CHECK_LE(datum_num, num) <<
216     "The size of datum_vector must be no greater than transformed_blob->num()";
217   Blob<Dtype> uni_blob(1, channels, height, width);// 新建一个uni_blob,里面只有一个batch。临时Blob
218   //依次对每一个datum进行变换,放入对应的Blob之中
219   for (int item_id = 0; item_id < datum_num; ++item_id) {
220     int offset = transformed_blob->offset(item_id);
221     uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);
222     Transform(datum_vector[item_id], &uni_blob);
223   }
224 }
225 
226 #ifdef USE_OPENCV
227 //对一组cv::Mat对象进行变换,放入Blob中
228 template<typename Dtype>
229 void DataTransformer<Dtype>::Transform(const vector<cv::Mat> & mat_vector,
230                                        Blob<Dtype>* transformed_blob) {
231   // 获取mat的参数
232   const int mat_num = mat_vector.size();
233   const int num = transformed_blob->num();
234   const int channels = transformed_blob->channels();
235   const int height = transformed_blob->height();
236   const int width = transformed_blob->width();
237 
238   CHECK_GT(mat_num, 0) << "There is no MAT to add";
239   CHECK_EQ(mat_num, num) <<
240     "The size of mat_vector must be equals to transformed_blob->num()";
241   Blob<Dtype> uni_blob(1, channels, height, width);
242   for (int item_id = 0; item_id < mat_num; ++item_id) {
243     int offset = transformed_blob->offset(item_id);
244     uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);
245     Transform(mat_vector[item_id], &uni_blob);
246   }
247 }
248 // 对一个cv::Mat对象进行变换,放入Blob中。
249 // 如果是图像的话,需要减去均值乘以scale,判断是不是需要做镜像处理  
250 // 逻辑与前面类似 
251 template<typename Dtype>
252 void DataTransformer<Dtype>::Transform(const cv::Mat& cv_img,
253                                        Blob<Dtype>* transformed_blob) {
254   const int crop_size = param_.crop_size();
255   const int img_channels = cv_img.channels();
256   const int img_height = cv_img.rows;
257   const int img_width = cv_img.cols;
258 
259   // Check dimensions.检查维度
260   const int channels = transformed_blob->channels();
261   const int height = transformed_blob->height();
262   const int width = transformed_blob->width();
263   const int num = transformed_blob->num();
264 
265   CHECK_EQ(channels, img_channels);
266   CHECK_LE(height, img_height);
267   CHECK_LE(width, img_width);
268   CHECK_GE(num, 1);
269 
270   CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";
271 
272   const Dtype scale = param_.scale();
273   const bool do_mirror = param_.mirror() && Rand(2);
274   const bool has_mean_file = param_.has_mean_file();
275   const bool has_mean_values = mean_values_.size() > 0;
276 
277   CHECK_GT(img_channels, 0);
278   CHECK_GE(img_height, crop_size);
279   CHECK_GE(img_width, crop_size);
280 
281   Dtype* mean = NULL;
282   if (has_mean_file) {
283     CHECK_EQ(img_channels, data_mean_.channels());
284     CHECK_EQ(img_height, data_mean_.height());
285     CHECK_EQ(img_width, data_mean_.width());
286     mean = data_mean_.mutable_cpu_data();
287   }
288   if (has_mean_values) {
289     CHECK(mean_values_.size() == 1 || mean_values_.size() == img_channels) <<
290      "Specify either 1 mean_value or as many as channels: " << img_channels;
291     if (img_channels > 1 && mean_values_.size() == 1) {
292       // Replicate the mean_value for simplicity 复制均值便于操作
293       for (int c = 1; c < img_channels; ++c) {
294         mean_values_.push_back(mean_values_[0]);
295       }
296     }
297   }
298 
299   int h_off = 0;
300   int w_off = 0;
301   cv::Mat cv_cropped_img = cv_img;
302   if (crop_size) {
303     CHECK_EQ(crop_size, height);
304     CHECK_EQ(crop_size, width);
305     // We only do random crop when we do training.只有训练阶段才随机切块
306     if (phase_ == TRAIN) {
307       h_off = Rand(img_height - crop_size + 1);
308       w_off = Rand(img_width - crop_size + 1);
309     } else {
310       h_off = (img_height - crop_size) / 2;
311       w_off = (img_width - crop_size) / 2;
312     }
313     cv::Rect roi(w_off, h_off, crop_size, crop_size);
314     cv_cropped_img = cv_img(roi);
315   } else {
316     CHECK_EQ(img_height, height);
317     CHECK_EQ(img_width, width);
318   }
319 
320   CHECK(cv_cropped_img.data);
321 
322   Dtype* transformed_data = transformed_blob->mutable_cpu_data();
323   int top_index;
324   for (int h = 0; h < height; ++h) {
325     const uchar* ptr = cv_cropped_img.ptr<uchar>(h);
326     int img_index = 0;
327     for (int w = 0; w < width; ++w) {
328       for (int c = 0; c < img_channels; ++c) {
329         if (do_mirror) {
330           top_index = (c * height + h) * width + (width - 1 - w);
331         } else {
332           top_index = (c * height + h) * width + w;
333         }
334         // int top_index = (c * height + h) * width + w;
335         Dtype pixel = static_cast<Dtype>(ptr[img_index++]);
336         if (has_mean_file) {
337           int mean_index = (c * img_height + h_off + h) * img_width + w_off + w;
338           transformed_data[top_index] =
339             (pixel - mean[mean_index]) * scale;
340         } else {
341           if (has_mean_values) {
342             transformed_data[top_index] =
343               (pixel - mean_values_[c]) * scale;
344           } else {
345             transformed_data[top_index] = pixel * scale;
346           }
347         }
348       }
349     }
350   }
351 }
352 #endif  // USE_OPENCV
353 
354 //输入输出都是Blob
355 template<typename Dtype>
356 void DataTransformer<Dtype>::Transform(Blob<Dtype>* input_blob,
357                                        Blob<Dtype>* transformed_blob) {
358   const int crop_size = param_.crop_size();
359   const int input_num = input_blob->num();
360   const int input_channels = input_blob->channels();
361   const int input_height = input_blob->height();
362   const int input_width = input_blob->width();
363 
364   if (transformed_blob->count() == 0) {
365     // Initialize transformed_blob with the right shape.初始化变换后的Blob形状
366     if (crop_size) {
367       transformed_blob->Reshape(input_num, input_channels,
368                                 crop_size, crop_size);
369     } else {
370       transformed_blob->Reshape(input_num, input_channels,
371                                 input_height, input_width);
372     }
373   }
374 
375   const int num = transformed_blob->num();
376   const int channels = transformed_blob->channels();
377   const int height = transformed_blob->height();
378   const int width = transformed_blob->width();
379   const int size = transformed_blob->count();
380 
381   CHECK_LE(input_num, num);
382   CHECK_EQ(input_channels, channels);
383   CHECK_GE(input_height, height);
384   CHECK_GE(input_width, width);
385 
386 
387   const Dtype scale = param_.scale();
388   const bool do_mirror = param_.mirror() && Rand(2);
389   const bool has_mean_file = param_.has_mean_file();
390   const bool has_mean_values = mean_values_.size() > 0;
391 
392   int h_off = 0;
393   int w_off = 0;
394   if (crop_size) {
395     CHECK_EQ(crop_size, height);
396     CHECK_EQ(crop_size, width);
397     // We only do random crop when we do training.只有训练阶段随机切块
398     if (phase_ == TRAIN) {
399       h_off = Rand(input_height - crop_size + 1);
400       w_off = Rand(input_width - crop_size + 1);
401     } else {
402       h_off = (input_height - crop_size) / 2;
403       w_off = (input_width - crop_size) / 2;
404     }
405   } else {
406     CHECK_EQ(input_height, height);
407     CHECK_EQ(input_width, width);
408   }
409 
410   Dtype* input_data = input_blob->mutable_cpu_data();
411   if (has_mean_file) {
412     CHECK_EQ(input_channels, data_mean_.channels());
413     CHECK_EQ(input_height, data_mean_.height());
414     CHECK_EQ(input_width, data_mean_.width());
415     for (int n = 0; n < input_num; ++n) {
416       int offset = input_blob->offset(n);
417       /* 
418        template <typename Dtype> 
419        void caffe_sub(const int N, const Dtype* a, const Dtype* b, Dtype* y); 
420        math_function中定义的caffe_sub目的是矩阵相减input_data(以offset开始的矩阵) = input_data(以offset开始的矩阵) - data_mean_ 
421       */
422       caffe_sub(data_mean_.count(), input_data + offset,
423             data_mean_.cpu_data(), input_data + offset);
424     }
425   }
426 
427   if (has_mean_values) {
428     CHECK(mean_values_.size() == 1 || mean_values_.size() == input_channels) <<
429      "Specify either 1 mean_value or as many as channels: " << input_channels;
430     if (mean_values_.size() == 1) {
431       caffe_add_scalar(input_blob->count(), -(mean_values_[0]), input_data);
432     } else {
433       for (int n = 0; n < input_num; ++n) {
434         for (int c = 0; c < input_channels; ++c) {
435           int offset = input_blob->offset(n, c);
436           // 给input_data[offset]地址开始的每一个元素加上一个-mean_values_[c] 
437           caffe_add_scalar(input_height * input_width, -(mean_values_[c]),
438             input_data + offset);
439         }
440       }
441     }
442   }
443   // 如果什么均值都没有则直接复制 
444   Dtype* transformed_data = transformed_blob->mutable_cpu_data();
445 
446   for (int n = 0; n < input_num; ++n) {
447     int top_index_n = n * channels;
448     int data_index_n = n * channels;
449     for (int c = 0; c < channels; ++c) {
450       int top_index_c = (top_index_n + c) * height;
451       int data_index_c = (data_index_n + c) * input_height + h_off;
452       for (int h = 0; h < height; ++h) {
453         int top_index_h = (top_index_c + h) * width;
454         int data_index_h = (data_index_c + h) * input_width + w_off;
455         if (do_mirror) {
456           int top_index_w = top_index_h + width - 1;
457           for (int w = 0; w < width; ++w) {
458             transformed_data[top_index_w-w] = input_data[data_index_h + w];
459           }
460         } else {
461           for (int w = 0; w < width; ++w) {
462             transformed_data[top_index_h + w] = input_data[data_index_h + w];
463           }
464         }
465       }
466     }
467   }
468   if (scale != Dtype(1)) {
469     DLOG(INFO) << "Scale: " << scale;
470     caffe_scal(size, scale, transformed_data);
471   }
472 }
473 
474 //获得数据变换输出尺寸
475 template<typename Dtype>
476 vector<int> DataTransformer<Dtype>::InferBlobShape(const Datum& datum) {
477   if (datum.encoded()) {
478 #ifdef USE_OPENCV
479 // 如果使用OpenCV则可以用先转换为CVMat,然后推断blob的形状 
480     CHECK(!(param_.force_color() && param_.force_gray()))
481         << "cannot set both force_color and force_gray";
482     cv::Mat cv_img;
483     if (param_.force_color() || param_.force_gray()) {
484     // If force_color then decode in color otherwise decode in gray.
485       cv_img = DecodeDatumToCVMat(datum, param_.force_color());
486     } else {
487       cv_img = DecodeDatumToCVMatNative(datum);
488     }
489     // InferBlobShape using the cv::image.
490     return InferBlobShape(cv_img);
491 #else
492     LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";
493 #endif  // USE_OPENCV
494   }
495   // 否则直接粗暴地从datum里面获取形状的数据 
496   const int crop_size = param_.crop_size();
497   const int datum_channels = datum.channels();
498   const int datum_height = datum.height();
499   const int datum_width = datum.width();
500   // Check dimensions.检查维度
501   CHECK_GT(datum_channels, 0);
502   CHECK_GE(datum_height, crop_size);
503   CHECK_GE(datum_width, crop_size);
504   // Build BlobShape. 创建BlobShape对象
505   vector<int> shape(4);
506   shape[0] = 1;
507   shape[1] = datum_channels;
508   shape[2] = (crop_size)? crop_size: datum_height;
509   shape[3] = (crop_size)? crop_size: datum_width;
510   return shape;
511 }
512 
513 template<typename Dtype>
514 vector<int> DataTransformer<Dtype>::InferBlobShape(
515     const vector<Datum> & datum_vector) {
516   const int num = datum_vector.size();
517   CHECK_GT(num, 0) << "There is no datum to in the vector";
518   // Use first datum in the vector to InferBlobShape.使用第一个推断
519   vector<int> shape = InferBlobShape(datum_vector[0]);
520   // Adjust num to the size of the vector.
521   shape[0] = num;
522   return shape;
523 }
524 
525 #ifdef USE_OPENCV
526 // 如果使用OpenCV  
527 // 使用CVMat中的信息来推断形状 
528 template<typename Dtype>
529 vector<int> DataTransformer<Dtype>::InferBlobShape(const cv::Mat& cv_img) {
530   const int crop_size = param_.crop_size();
531   const int img_channels = cv_img.channels();
532   const int img_height = cv_img.rows;
533   const int img_width = cv_img.cols;
534   // Check dimensions.
535   CHECK_GT(img_channels, 0);
536   CHECK_GE(img_height, crop_size);
537   CHECK_GE(img_width, crop_size);
538   // Build BlobShape.
539   vector<int> shape(4);
540   shape[0] = 1;
541   shape[1] = img_channels;
542   shape[2] = (crop_size)? crop_size: img_height;
543   shape[3] = (crop_size)? crop_size: img_width;
544   return shape;
545 }
546 
547 template<typename Dtype>
548 vector<int> DataTransformer<Dtype>::InferBlobShape(
549     const vector<cv::Mat> & mat_vector) {
550   const int num = mat_vector.size();
551   CHECK_GT(num, 0) << "There is no cv_img to in the vector";
552   // Use first cv_img in the vector to InferBlobShape.
553   vector<int> shape = InferBlobShape(mat_vector[0]);
554   // Adjust num to the size of the vector.
555   shape[0] = num;
556   return shape;
557 }
558 #endif  // USE_OPENCV
559 
560 // 初始化随机数种子  
561 template <typename Dtype>
562 void DataTransformer<Dtype>::InitRand() {
563   // 要么需要镜像要么训练阶段和需要crop同时满足的情况下才初始化随机数种子
564   const bool needs_rand = param_.mirror() ||
565       (phase_ == TRAIN && param_.crop_size());
566   if (needs_rand) {
567     const unsigned int rng_seed = caffe_rng_rand();// 获得随机数种子(通过熵池或者时间生成种子)
568     rng_.reset(new Caffe::RNG(rng_seed));//初始化随机数种子并实例化随机数生成器 
569   } else {
570     rng_.reset();//否则随机数生成器设置为空  
571   }
572 }
573 
574 // 产生从0到n的随机数
575 template <typename Dtype>
576 int DataTransformer<Dtype>::Rand(int n) {
577   CHECK(rng_);
578   CHECK_GT(n, 0);
579   caffe::rng_t* rng =
580       static_cast<caffe::rng_t*>(rng_->generator());
581   return ((*rng)() % n);
582 }
583 
584 INSTANTIATE_CLASS(DataTransformer);
585 /* 
586 初始化类的宏定义
587 #define INSTANTIATE_CLASS(classname) \ 
588   char gInstantiationGuard##classname; \ 
589   template class classname<float>; \ 
590   template class classname<double> 
591 */  
592 }  // namespace caffe

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

摘抄参考赵永科《21天实战caffe》

同时参考http://blog.csdn.net/langb2014/article/details/51050213

posted on 2017-07-07 15:13  想飞的萌猪  阅读(1050)  评论(0编辑  收藏  举报