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
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【caffe I/O】数据变换器(图像的预处理部分) 代码注释 - Python技术站