• 【caffe I/O】数据变换器(图像的预处理部分) 代码注释


    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

  • 相关阅读:
    sqlserver2005存储汉字成问号解决办法:
    .net c# 日期格式和常用处理
    文件夹无法访问拒绝访问,无法删除文件的,快速有效解决方法
    打印出所有的 info.plist 中的 keys、values
    利用时间戳来准确计算某个时间点具现在的时间差
    项目中的技巧经验汇总
    "Base SDK Missing"问题的解决
    UIPopoverController的使用
    给ios自带控件添加圆角边的方法
    实现程序内截屏功能的代码
  • 原文地址:https://www.cnblogs.com/xiangfeidemengzhu/p/7132328.html
Copyright © 2020-2023  润新知