梳理caffe代碼data_transformer(十二)


梳理caffe代碼data_transformer(十二)

data_transformer詳細注釋看頭文件和實現部分:

頭文件:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. /////////////////TransformationParameter的caffe消息定義  
  2. /* 
  3. // Message that stores parameters used to apply transformation 
  4. // to the data layer's data 
  5. message TransformationParameter { 
  6.   // For data pre-processing, we can do simple scaling and subtracting the 
  7.   // data mean, if provided. Note that the mean subtraction is always carried 
  8.   // out before scaling. 
  9.   optional float scale = 1 [default = 1]; 
  10.   // Specify if we want to randomly mirror data. 
  11.   optional bool mirror = 2 [default = false]; 
  12.   // Specify if we would like to randomly crop an image. 
  13.   optional uint32 crop_size = 3 [default = 0]; 
  14.   // mean_file and mean_value cannot be specified at the same time 
  15.   optional string mean_file = 4; 
  16.   // if specified can be repeated once (would substract it from all the channels) 
  17.   // or can be repeated the same number of times as channels 
  18.   // (would subtract them from the corresponding channel) 
  19.   repeated float mean_value = 5; 
  20.   // Force the decoded image to have 3 color channels. 
  21.   optional bool force_color = 6 [default = false]; 
  22.   // Force the decoded image to have 1 color channels. 
  23.   optional bool force_gray = 7 [default = false]; 
  24. } 
  25. */  
  26. /* 
  27. DataTransformer類主要負責對數據進行預處理, 比如減去均值、進行crop,鏡像,強制設置為彩色強制設置為灰度圖像以及像素值的縮放,此外該類還將Datum、const vector<Datum>、cv::Mat&、vector<cv::Mat> 、Blob<Dtype>*類型的數據變換到目標大小的blob。負責對上述類型的數據推斷其shape。 
  28. */  
  29. #ifndef CAFFE_DATA_TRANSFORMER_HPP  
  30. #define CAFFE_DATA_TRANSFORMER_HPP  
  31. #include <vector>  
  32. #include "caffe/blob.hpp"  
  33. #include "caffe/common.hpp"  
  34. #include "caffe/proto/caffe.pb.h"  
  35.   
  36. namespace caffe {  
  37.   
  38. /** 
  39.  * @brief Applies common transformations to the input data, such as 
  40.  * scaling, mirroring, substracting the image mean... 
  41.  */  
  42. template <typename Dtype>  
  43. class DataTransformer {  
  44.  public:  
  45.   explicit DataTransformer(const TransformationParameter& param, Phase phase);  
  46.   virtual ~DataTransformer() {}  
  47.   
  48.   /** 
  49.    * @brief Initialize the Random number generations if needed by the 
  50.    *    transformation. 
  51.    */  
  52. // 初始化隨機數生成器,因為在對數據進行變換的時候有可能用到,比如說打亂數據的輸入順序  
  53.   void InitRand();  
  54.   
  55.   /** 
  56.    * @brief Applies the transformation defined in the data layer's 
  57.    * transform_param block to the data. 
  58.    * 
  59.    * @param datum 
  60.    *    Datum containing the data to be transformed. 
  61.    * @param transformed_blob 
  62.    *    This is destination blob. It can be part of top blob's data if 
  63.    *    set_cpu_data() is used. See data_layer.cpp for an example. 
  64.    */  
  65. // 對Datum的數據進行變換,放入到transformed_blob中  
  66.   void Transform(const Datum& datum, Blob<Dtype>* transformed_blob);  
  67.   
  68.   /** 
  69.    * @brief Applies the transformation defined in the data layer's 
  70.    * transform_param block to a vector of Datum. 
  71.    * 
  72.    * @param datum_vector 
  73.    *    A vector of Datum containing the data to be transformed. 
  74.    * @param transformed_blob 
  75.    *    This is destination blob. It can be part of top blob's data if 
  76.    *    set_cpu_data() is used. See memory_layer.cpp for an example. 
  77.    */  
  78. // 對Datum容器的數據進行變換翻入到transformed_blob  
  79.   void Transform(const vector<Datum> & datum_vector,  
  80.                 Blob<Dtype>* transformed_blob);  
  81.   
  82. #ifdef USE_OPENCV  
  83.   /** 
  84.    * @brief Applies the transformation defined in the data layer's 
  85.    * transform_param block to a vector of Mat. 
  86.    * 
  87.    * @param mat_vector 
  88.    *    A vector of Mat containing the data to be transformed. 
  89.    * @param transformed_blob 
  90.    *    This is destination blob. It can be part of top blob's data if 
  91.    *    set_cpu_data() is used. See memory_layer.cpp for an example. 
  92.    */  
  93. // 如果定義OpenCV還可能對mat容器數據類型的數據進行變換  
  94.   void Transform(const vector<cv::Mat> & mat_vector,  
  95.                 Blob<Dtype>* transformed_blob);  
  96.   
  97.   /** 
  98.    * @brief Applies the transformation defined in the data layer's 
  99.    * transform_param block to a cv::Mat 
  100.    * 
  101.    * @param cv_img 
  102.    *    cv::Mat containing the data to be transformed. 
  103.    * @param transformed_blob 
  104.    *    This is destination blob. It can be part of top blob's data if 
  105.    *    set_cpu_data() is used. See image_data_layer.cpp for an example. 
  106.    */  
  107. // 將opencv讀取的單個圖像轉換到blob中去  
  108.   void Transform(const cv::Mat& cv_img, Blob<Dtype>* transformed_blob);  
  109. #endif  // USE_OPENCV  
  110.   
  111.   /** 
  112.    * @brief Applies the same transformation defined in the data layer's 
  113.    * transform_param block to all the num images in a input_blob. 
  114.    * 
  115.    * @param input_blob 
  116.    *    A Blob containing the data to be transformed. It applies the same 
  117.    *    transformation to all the num images in the blob. 
  118.    * @param transformed_blob 
  119.    *    This is destination blob, it will contain as many images as the 
  120.    *    input blob. It can be part of top blob's data. 
  121.    */  
  122. // 將輸入的blob進行變換,可能是取出blob的中的一部分數據到新的blob  
  123.   void Transform(Blob<Dtype>* input_blob, Blob<Dtype>* transformed_blob);  
  124.   
  125.   /** 
  126.    * @brief Infers the shape of transformed_blob will have when 
  127.    *    the transformation is applied to the data. 
  128.    * 
  129.    * @param datum 
  130.    *    Datum containing the data to be transformed. 
  131.    */  
  132. // 根據Datum獲取blob的形狀  
  133.   vector<int> InferBlobShape(const Datum& datum);  
  134.   /** 
  135.    * @brief Infers the shape of transformed_blob will have when 
  136.    *    the transformation is applied to the data. 
  137.    *    It uses the first element to infer the shape of the blob. 
  138.    * 
  139.    * @param datum_vector 
  140.    *    A vector of Datum containing the data to be transformed. 
  141.    */  
  142. // 根據Datum容器獲取blob的形狀  
  143.   vector<int> InferBlobShape(const vector<Datum> & datum_vector);  
  144.   /** 
  145.    * @brief Infers the shape of transformed_blob will have when 
  146.    *    the transformation is applied to the data. 
  147.    *    It uses the first element to infer the shape of the blob. 
  148.    * 
  149.    * @param mat_vector 
  150.    *    A vector of Mat containing the data to be transformed. 
  151.    */  
  152. #ifdef USE_OPENCV  
  153. // 根據Mat容器獲取blob的形狀  
  154.   vector<int> InferBlobShape(const vector<cv::Mat> & mat_vector);  
  155.   /** 
  156.    * @brief Infers the shape of transformed_blob will have when 
  157.    *    the transformation is applied to the data. 
  158.    * 
  159.    * @param cv_img 
  160.    *    cv::Mat containing the data to be transformed. 
  161.    */  
  162. // 根據Mat獲取blob的形狀  
  163.   vector<int> InferBlobShape(const cv::Mat& cv_img);  
  164. #endif  // USE_OPENCV  
  165.   
  166.  protected:  
  167.    /** 
  168.    * @brief Generates a random integer from Uniform({0, 1, ..., n-1}). 
  169.    * 
  170.    * @param n 
  171.    *    The upperbound (exclusive) value of the random number. 
  172.    * @return 
  173.    *    A uniformly random integer value from ({0, 1, ..., n-1}). 
  174.    */  
  175. // 生成從0到n-1的服從均勻分布的隨機數,要求繼承他的都必須實現如何生成隨機數  
  176.   virtual int Rand(int n);  
  177. // 將給定的Datum進行轉換  
  178.   void Transform(const Datum& datum, Dtype* transformed_data);  
  179.   
  180.   // 變換所使用的參數  
  181.   TransformationParameter param_;  
  182.   // 隨機數生成器的種子  
  183.   shared_ptr<Caffe::RNG> rng_;  
  184.   // 是訓練還是測試?  
  185.   Phase phase_;  
  186.   // 數據均值 blob  
  187.   Blob<Dtype> data_mean_;  
  188.   // 數據均值blob的容器  
  189.   vector<Dtype> mean_values_;  
  190. };  
  191.   
  192. }  // namespace caffe  
  193.   
  194. #endif  // CAFFE_DATA_TRANSFORMER_HPP_  

實現:

 

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. //DataTransformer需要輸入的是blob,所以需要看一下里面的參數,因此再把這一部分內容的proto貼出來,這是新版的caffe  
  2. /* 
  3. // Specifies the shape (dimensions) of a Blob. 
  4. message BlobShape { 
  5.   repeated int64 dim = 1 [packed = true]; 
  6. } 
  7.  
  8. message BlobProto { 
  9.   optional BlobShape shape = 7; 
  10.   repeated float data = 5 [packed = true]; 
  11.   repeated float diff = 6 [packed = true]; 
  12.   repeated double double_data = 8 [packed = true]; 
  13.   repeated double double_diff = 9 [packed = true]; 
  14.  
  15.   // 4D dimensions -- deprecated.  Use "shape" instead. 
  16.   optional int32 num = 1 [default = 0]; 
  17.   optional int32 channels = 2 [default = 0]; 
  18.   optional int32 height = 3 [default = 0]; 
  19.   optional int32 width = 4 [default = 0]; 
  20. } 
  21. */  
  22. /////////////////TransformationParameter的caffe消息定義  
  23. /* 
  24. // Message that stores parameters used to apply transformation 
  25. // to the data layer's data 
  26. message TransformationParameter { 
  27.   // For data pre-processing, we can do simple scaling and subtracting the 
  28.   // data mean, if provided. Note that the mean subtraction is always carried 
  29.   // out before scaling. 
  30.   optional float scale = 1 [default = 1]; 
  31.   // Specify if we want to randomly mirror data. 
  32.   optional bool mirror = 2 [default = false]; 
  33.   // Specify if we would like to randomly crop an image. 
  34.   optional uint32 crop_size = 3 [default = 0]; 
  35.   // mean_file and mean_value cannot be specified at the same time 
  36.   optional string mean_file = 4; 
  37.   // if specified can be repeated once (would substract it from all the channels) 
  38.   // or can be repeated the same number of times as channels 
  39.   // (would subtract them from the corresponding channel) 
  40.   repeated float mean_value = 5; 
  41.   // Force the decoded image to have 3 color channels. 
  42.   optional bool force_color = 6 [default = false]; 
  43.   // Force the decoded image to have 1 color channels. 
  44.   optional bool force_gray = 7 [default = false]; 
  45. } 
  46. */  
  47. #ifdef USE_OPENCV  
  48. #include <opencv2/core/core.hpp>  
  49. #endif  // USE_OPENCV  
  50.   
  51. #include <string>  
  52. #include <vector>  
  53.   
  54. #include "caffe/data_transformer.hpp"  
  55. #include "caffe/util/io.hpp"  
  56. #include "caffe/util/math_functions.hpp"  
  57. #include "caffe/util/rng.hpp"  
  58.   
  59. namespace caffe {  
  60. // 構造函數  
  61. template<typename Dtype>  
  62. DataTransformer<Dtype>::DataTransformer(const TransformationParameter& param,  
  63.     Phase phase)  
  64.     : param_(param), phase_(phase) {  
  65.   // check if we want to use mean_file  
  66.   // 判斷是否有平均值文件  
  67.   if (param_.has_mean_file()) {  
  68.     CHECK_EQ(param_.mean_value_size(), 0) <<  
  69.       "Cannot specify mean_file and mean_value at the same time";  
  70.     // 平均值文件的路徑  
  71.     const string& mean_file = param.mean_file();  
  72.     if (Caffe::root_solver()) {  
  73.       LOG(INFO) << "Loading mean file from: " << mean_file;  
  74.     }  
  75.     BlobProto blob_proto;// 調用google/protobuf?? ,用於加速運算的數據接口,有時間再詳細了解其應用方法   
  76. //這個函數是實現了從二進制文件中讀取數據到blob_proto中,猜測函數來自第3方庫的google/protobuf模塊   
  77.     ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);  
  78.     data_mean_.FromProto(blob_proto);// 調用Blob類的成員函數FromRroto從BlobProto中加載數據   
  79.   }  
  80.   // check if we want to use mean_value  
  81.   if (param_.mean_value_size() > 0) {  
  82.     CHECK(param_.has_mean_file() == false) <<  
  83.       "Cannot specify mean_file and mean_value at the same time";  
  84.     for (int c = 0; c < param_.mean_value_size(); ++c) {  
  85.       mean_values_.push_back(param_.mean_value(c));//將元素param_.mean_value(c)加入到mean_values_容器的最后一位  
  86.     }  
  87.   }  
  88. }  
  89.   
  90. /*提前先描述一下數據層的Datum, 
  91. Datum數據結構,Caffe並不是把向量和矩陣直接放進數據庫的,而是將數據通過caffe.proto里定義的一個datum類來封裝。數據庫里放的是一個個的datum序列化成的字符串。Datum的定義摘錄如下: 
  92. message Datum { 
  93.   optional int32 channels = 1; 
  94.   optional int32 height = 2; 
  95.   optional int32 width = 3; 
  96.   // the actual image data, in bytes 
  97.   optional bytes data = 4; 
  98.   optional int32 label = 5; 
  99.   // Optionally, the datum could also hold float data. 
  100.   repeated float float_data = 6; 
  101.   // If true data contains an encoded image that need to be decoded 
  102.   optional bool encoded = 7 [default = false]; 
  103. } 
  104. 一個Datum有三個維度,channels, height,和width,可以看做是少了num維度的Blob。存放數據的地方有兩個:byte_data和float_data,分別存放整數型和浮點型數據。圖像數據一般是整形,放在byte_data里,特征向量一般是浮點型,放在float_data里。label存放數據的類別標簽,是整數型。encoded標識數據是否需要被解碼(里面有可能放的是JPEG或者PNG之類經過編碼的數據)。Datum這個數據結構將數據和標簽封裝在一起,兼容整形和浮點型數據。經過Protobuf編譯后,可以在Python和C++中都提供高效的訪問。同時Protubuf還為它提供了序列化與反序列化的功能。存放進LMDB的就是Datum序列化生成的字符串。 
  105. Caffe中關於LMDB的代碼有三類:生成數據集、讀取數據集、生成特征向量。接下來就分別針對三者進行分析。 
  106. 生成數據集: 
  107. 生成數據集的代碼在examples,隨數據集提供,比如MNIST。 
  108. 首先,創建訪問LMDB所需的一些變量: 
  109. MDB_env *mdb_env; 
  110. MDB_dbi mdb_dbi; 
  111. MDB_val mdb_key, mdb_data; 
  112. MDB_txn *mdb_txn; 
  113. ... 
  114. mdb_env是整個數據庫環境的句柄,mdb_dbi是環境中一個數據庫的句柄,mdb_key和mdb_data用來存放向數據庫中輸入數據的“值”。mdb_txn是數據庫事物操作的句柄,”txn”是”transaction”的縮寫。 
  115. 然后,創建數據庫環境,創建並打開數據庫: 
  116. if (db_backend == "lmdb") {  // lmdb 
  117.   LOG(INFO) << "Opening lmdb " << db_path; 
  118.   CHECK_EQ(mkdir(db_path, 0744), 0) 
  119.       << "mkdir " << db_path << "failed"; 
  120.   CHECK_EQ(mdb_env_create(&mdb_env), MDB_SUCCESS) << "mdb_env_create failed"; 
  121.   CHECK_EQ(mdb_env_set_mapsize(mdb_env, 1099511627776), MDB_SUCCESS)  // 1TB 
  122.       << "mdb_env_set_mapsize failed"; 
  123.   CHECK_EQ(mdb_env_open(mdb_env, db_path, 0, 0664), MDB_SUCCESS) 
  124.       << "mdb_env_open failed"; 
  125.   CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS) 
  126.       << "mdb_txn_begin failed"; 
  127.   CHECK_EQ(mdb_open(mdb_txn, NULL, 0, &mdb_dbi), MDB_SUCCESS) 
  128.       << "mdb_open failed. Does the lmdb already exist? "; 
  129. } else { 
  130.   LOG(FATAL) << "Unknown db backend " << db_backend; 
  131. } 
  132. mkdir(db_path, 0744)為數據庫創建文件夾,如果文件夾已經存在,程序會報錯退出。也就是說,程序不會覆蓋已有的數據庫。已有的數據庫如果不要了,需要手動刪除。需要注意的是,LMDB的一個環境中是可以有多個數據庫的,數據庫之間以名字區分。mdb_open()的第二個參數實際上就是數據庫的名稱(char *)。當一個環境中只有一個數據庫的時候,這個參數可以給NULL。最后,為每一個圖像創建Datum對象,向對象內寫入數據,然后將其序列化成字符串,將字符串放入數據庫中: 
  133. Datum datum; 
  134. datum.set_channels(1); 
  135. datum.set_height(rows); 
  136. datum.set_width(cols); 
  137. for (int item_id = 0; item_id < num_items; ++item_id) { 
  138.   image_file.read(pixels, rows * cols); 
  139.   label_file.read(&label, 1); 
  140.   datum.set_data(pixels, rows*cols); 
  141.   datum.set_label(label); 
  142.   snprintf(key_cstr, kMaxKeyLength, "%08d", item_id); 
  143.   datum.SerializeToString(&value); 
  144.   string keystr(key_cstr); 
  145.  
  146.   // Put in db 
  147.   if (db_backend == "lmdb") {  // lmdb 
  148.     mdb_data.mv_size = value.size(); 
  149.     mdb_data.mv_data = reinterpret_cast<void*>(&value[0]); 
  150.     mdb_key.mv_size = keystr.size(); 
  151.     mdb_key.mv_data = reinterpret_cast<void*>(&keystr[0]); 
  152.     CHECK_EQ(mdb_put(mdb_txn, mdb_dbi, &mdb_key, &mdb_data, 0), MDB_SUCCESS) 
  153.         << "mdb_put failed"; 
  154.   } else { 
  155.     LOG(FATAL) << "Unknown db backend " << db_backend; 
  156.   } 
  157.  
  158.   if (++count % 1000 == 0) { 
  159.     // Commit txn 
  160.     if (db_backend == "lmdb") {  // lmdb 
  161.       CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS) 
  162.           << "mdb_txn_commit failed"; 
  163.       CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS) 
  164.           << "mdb_txn_begin failed"; 
  165.     } else { 
  166.       LOG(FATAL) << "Unknown db backend " << db_backend; 
  167.     } 
  168.   } 
  169. } 
  170. 放入數據的Key是圖像的編號,前面補0至8位。MDB_val類型的mdb_data和mdb_key中存放的是數據來源的指針,以及數據的長度。mdb_put()函數將數據存入數據庫。每隔1000個圖像commit一次數據庫。只有commit之后,數據才真正寫入磁盤。 
  171. 讀取數據集: 
  172. Caffe中讀取LMDB數據集的代碼是DataLayer,用在網絡的最下層,提供數據。DataLayer采用順序遍歷的方式讀取數據,不支持打亂數據順序,只能隨機跳過前若干個數據。 
  173. 首先,在DataLayer的DataLayerSetUp方法中,打開數據庫,並獲取迭代器cursor_: 
  174. db_.reset(db::GetDB(this->layer_param_.data_param().backend())); 
  175. db_->Open(this->layer_param_.data_param().source(), db::READ); 
  176. cursor_.reset(db_->NewCursor()); 
  177. 然后,在每一次的數據預取時,InternalThreadEntry()方法中,從數據庫中讀取字符串,反序列化為Datum對象,再從Datum對象中取出數據: 
  178. Datum datum; 
  179. datum.ParseFromString(cursor_->value()); 
  180. 其中,cursor_->value()獲取序列化后的字符串。datum.ParseFromString()方法對字符串進行反序列化。 
  181. 最后,要將cursor_向前推進: 
  182. cursor_->Next(); 
  183. if (!cursor_->valid()) { 
  184.   DLOG(INFO) << "Restarting data prefetching from start." 
  185.       cursor_->SeekToFirst(); 
  186. } 
  187. 如果cursor->valid()返回false,說明數據庫已經遍歷到頭,這時需要將cursor_重置回數據庫開頭。不支持樣本隨機排序應該是DataLayer的致命弱點。如果數據庫的key能夠統一,其實可以通過對key隨機枚舉的方式實現。所以caffe定義了一個隨機生成器RNG。 
  188. */  
  189. template<typename Dtype>  
  190. void DataTransformer<Dtype>::Transform(const Datum& datum,  
  191.                                        Dtype* transformed_data) {  
  192.   // 參考TransformationParameter的定義  
  193.   const string& data = datum.data();  
  194.   const int datum_channels = datum.channels();//數據的channel  
  195.   const int datum_height = datum.height();//數據的行數  
  196.   const int datum_width = datum.width();// 數據的列數  
  197.   
  198.   const int crop_size = param_.crop_size();// crop大小  
  199.   const Dtype scale = param_.scale();// 縮放比例  
  200.   const bool do_mirror = param_.mirror() && Rand(2);// 該參數用於在鏡像位置對數據處理  
  201.   const bool has_mean_file = param_.has_mean_file();// 是否有均值文件  
  202.   const bool has_uint8 = data.size() > 0;// 數據是否為uint8還是float類型的  
  203.   const bool has_mean_values = mean_values_.size() > 0;// 是否有每個channel的均值  
  204.   
  205.   // 檢查合法性  
  206.   CHECK_GT(datum_channels, 0);  
  207.   CHECK_GE(datum_height, crop_size);  
  208.   CHECK_GE(datum_width, crop_size);  
  209.   
  210.   Dtype* mean = NULL;  
  211. /* 
  212. 前面有介紹這一部分CHECK內容,glog提供了多個便利的宏來處理特定關系的判定。具體有: 
  213. 1,判定大小關系 
  214. CHECK_EQ, CHECK_NE, CHECK_LE, CHECK_LT, CHECK_GE, CHECK_GT,使用這些宏需要注意類型一致,如果出現類型不一致的,可使用static_cast轉換。 
  215. 2,判定指針是否為空 
  216. CHECK_NOTNULL(some_ptr),可用於對象初始化的時候。 
  217. 3,判定字符串是否相等 
  218. CHECK_STREQ, CHECK_STRNE, CHECK_STRCASEEQ,CHECK_STRCASENE。可進行大小寫敏感或不敏感字符串來分別判定。 
  219. 4, 判定浮點是否相等或相近 
  220. CHECK_DOUBLE_EQ,CHECK_NEAR。這兩個宏都需要指定一個可容忍的偏差上限。 
  221. */  
  222.   if (has_mean_file) {// 檢查mean_file是否與數據的參數一致  
  223.     CHECK_EQ(datum_channels, data_mean_.channels());  
  224.     CHECK_EQ(datum_height, data_mean_.height());  
  225.     CHECK_EQ(datum_width, data_mean_.width());  
  226.     mean = data_mean_.mutable_cpu_data();  
  227.   }  
  228.   if (has_mean_values) {  
  229.     CHECK(mean_values_.size() == 1 || mean_values_.size() == datum_channels) <<  
  230.      "Specify either 1 mean_value or as many as channels: " << datum_channels;  
  231.     if (datum_channels > 1 && mean_values_.size() == 1) {  
  232.       // Replicate the mean_value for simplicity  
  233.       for (int c = 1; c < datum_channels; ++c) {  
  234.         mean_values_.push_back(mean_values_[0]);  
  235.       }  
  236.     }  
  237.   }  
  238.   
  239.   int height = datum_height;  
  240.   int width = datum_width;  
  241.   
  242.   // 根據是否需要crop來生成h_off和w_off  
  243.   int h_off = 0;  
  244.   int w_off = 0;  
  245.   if (crop_size) {// 如果crop_size不為0  
  246.     height = crop_size;  
  247.     width = crop_size;  
  248.     // We only do random crop when we do training.  
  249.     // 在訓練的時候隨機crop圖像塊,這里需要自己實現Rand這個函數來確定是如何隨機的  
  250.     if (phase_ == TRAIN) {  
  251.       h_off = Rand(datum_height - crop_size + 1);// 產生從0到datum_height - crop_size的隨機數  
  252.       w_off = Rand(datum_width - crop_size + 1);  
  253.     } else {// 測試的時候不用隨機,取圖像的中心  
  254.       h_off = (datum_height - crop_size) / 2;  
  255.       w_off = (datum_width - crop_size) / 2;  
  256.     }  
  257.   }  
  258.   
  259.   // 對數據進行變換,主要是將原來的像素值減去均值,然后乘以scale這么一個操作  
  260.   // 如果需要crop則最終轉換的Blob的大小即為crop*crop  
  261.   // 如果不是,則最終的Blob大小即為datum_height*datum_width  
  262.   Dtype datum_element;  
  263.   int top_index, data_index;  
  264.   for (int c = 0; c < datum_channels; ++c) {  
  265.     for (int h = 0; h < height; ++h) {  
  266.       for (int w = 0; w < width; ++w) {  
  267.         data_index = (c * datum_height + h_off + h) * datum_width + w_off + w;// 獲取數據的索引,我不是很明白怎么計算的?  
  268.         if (do_mirror) {// 是否需要在鏡像位置轉換  
  269.           top_index = (c * height + h) * width + (width - 1 - w);//在寬這個坐標上做文章,來實現鏡像  
  270.         } else {//  
  271.           top_index = (c * height + h) * width + w;  
  272.         }  
  273.         if (has_uint8) {// 數據如果是uint8則進行轉換  
  274.           datum_element =  
  275.             static_cast<Dtype>(static_cast<uint8_t>(data[data_index]));  
  276.         } else {// 否則就是float  
  277.           datum_element = datum.float_data(data_index);  
  278.         }  
  279.         if (has_mean_file) {// 如果有mean_file,則原來的像素值減去均值,然后乘以scale  
  280.           transformed_data[top_index] =  
  281.             (datum_element - mean[data_index]) * scale;  
  282.         } else {  
  283.           if (has_mean_values) {// 否則減去該channel的均值(每個channel有其一個均值),然后乘以scale  
  284.             transformed_data[top_index] =  
  285.               (datum_element - mean_values_[c]) * scale;  
  286.           } else {// 否則如果沒有均值那么就直接乘以scale即可  
  287.             transformed_data[top_index] = datum_element * scale;  
  288.           }  
  289.         }  
  290.       }  
  291.     }  
  292.   }  
  293. }  
  294.   
  295.   
  296. template<typename Dtype>  
  297. void DataTransformer<Dtype>::Transform(const Datum& datum,  
  298.                                        Blob<Dtype>* transformed_blob) {  
  299.   // If datum is encoded, decoded and transform the cv::image.  
  300.   if (datum.encoded()) {//  檢查是否編碼了,如果是則解碼  
  301. #ifdef USE_OPENCV  
  302.     // 先檢查是不是兩個屬性都設置, 如果是則說明參數設置有誤  
  303.     CHECK(!(param_.force_color() && param_.force_gray()))  
  304.         << "cannot set both force_color and force_gray";  
  305.     cv::Mat cv_img;  
  306.     if (param_.force_color() || param_.force_gray()) {  
  307.         // 如果強制彩色或者強制灰度圖像一個成立則使用DecodeDatumToCVMat解碼  
  308.     // If force_color then decode in color otherwise decode in gray.  
  309.       cv_img = DecodeDatumToCVMat(datum, param_.force_color());  
  310.     } else {// 否則使用DecodeDatumToCVMatNative解碼  
  311.       cv_img = DecodeDatumToCVMatNative(datum);  
  312.     }  
  313.     // Transform the cv::image into blob.  
  314.     // 變換  
  315.     return Transform(cv_img, transformed_blob);  
  316. #else  
  317.     LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";  
  318. #endif  // USE_OPENCV  
  319.   } else {// 如果沒有編碼則,檢查force_color和force_gray是否設置,如果設置則不合法,因為該選項只適合於編碼后的數據  
  320.     if (param_.force_color() || param_.force_gray()) {  
  321.       LOG(ERROR) << "force_color and force_gray only for encoded datum";  
  322.     }  
  323.   }  
  324.   
  325.   const int crop_size = param_.crop_size();  
  326.   const int datum_channels = datum.channels();  
  327.   const int datum_height = datum.height();  
  328.   const int datum_width = datum.width();  
  329.   
  330.   // Check dimensions.  
  331.   const int channels = transformed_blob->channels();  
  332.   const int height = transformed_blob->height();  
  333.   const int width = transformed_blob->width();  
  334.   const int num = transformed_blob->num();  
  335.   
  336.   CHECK_EQ(channels, datum_channels);  
  337.   CHECK_LE(height, datum_height);  
  338.   CHECK_LE(width, datum_width);  
  339.   CHECK_GE(num, 1);  
  340.   
  341.   if (crop_size) {  
  342.     CHECK_EQ(crop_size, height);  
  343.     CHECK_EQ(crop_size, width);  
  344.   } else {  
  345.     CHECK_EQ(datum_height, height);  
  346.     CHECK_EQ(datum_width, width);  
  347.   }  
  348.   // 繼續變換數據  
  349.   Dtype* transformed_data = transformed_blob->mutable_cpu_data();  
  350.   Transform(datum, transformed_data);  
  351. }  
  352.   
  353. template<typename Dtype>  
  354. void DataTransformer<Dtype>::Transform(const vector<Datum> & datum_vector,  
  355.                                        Blob<Dtype>* transformed_blob) {  
  356.   const int datum_num = datum_vector.size();  
  357.   // 變換到的目標blob的形狀  
  358.   const int num = transformed_blob->num();  
  359.   const int channels = transformed_blob->channels();  
  360.   const int height = transformed_blob->height();  
  361.   const int width = transformed_blob->width();  
  362.   
  363.   CHECK_GT(datum_num, 0) << "There is no datum to add";  
  364.   CHECK_LE(datum_num, num) <<  
  365.     "The size of datum_vector must be no greater than transformed_blob->num()";  
  366.   // 新建一個uni_blob,里面只有一個batch  
  367.   Blob<Dtype> uni_blob(1, channels, height, width);  
  368.   for (int item_id = 0; item_id < datum_num; ++item_id) {  
  369.     int offset = transformed_blob->offset(item_id);  
  370.     uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);  
  371.     Transform(datum_vector[item_id], &uni_blob);  
  372.   }  
  373. }  
  374.   
  375. #ifdef USE_OPENCV  
  376. template<typename Dtype>  
  377. void DataTransformer<Dtype>::Transform(const vector<cv::Mat> & mat_vector,  
  378.                                        Blob<Dtype>* transformed_blob) {  
  379.   // 獲取mat的參數  
  380.   const int mat_num = mat_vector.size();  
  381.   const int num = transformed_blob->num();  
  382.   const int channels = transformed_blob->channels();  
  383.   const int height = transformed_blob->height();  
  384.   const int width = transformed_blob->width();  
  385.   
  386.   CHECK_GT(mat_num, 0) << "There is no MAT to add";  
  387.   CHECK_EQ(mat_num, num) <<  
  388.     "The size of mat_vector must be equals to transformed_blob->num()";  
  389.   //  同上  
  390.   Blob<Dtype> uni_blob(1, channels, height, width);  
  391.   for (int item_id = 0; item_id < mat_num; ++item_id) {  
  392.     int offset = transformed_blob->offset(item_id);  
  393.     uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);  
  394.     Transform(mat_vector[item_id], &uni_blob);  
  395.   }  
  396. }  
  397.   
  398. // 如果是圖像的話,需要減去均值乘以scale,判斷是不是需要做鏡像處理  
  399. // 邏輯與前面類似  
  400. template<typename Dtype>  
  401. void DataTransformer<Dtype>::Transform(const cv::Mat& cv_img,  
  402.                                        Blob<Dtype>* transformed_blob) {  
  403.   const int crop_size = param_.crop_size();  
  404.   const int img_channels = cv_img.channels();  
  405.   const int img_height = cv_img.rows;  
  406.   const int img_width = cv_img.cols;  
  407.   
  408.   // Check dimensions.  
  409.   const int channels = transformed_blob->channels();  
  410.   const int height = transformed_blob->height();  
  411.   const int width = transformed_blob->width();  
  412.   const int num = transformed_blob->num();  
  413.   
  414.   CHECK_EQ(channels, img_channels);  
  415.   CHECK_LE(height, img_height);  
  416.   CHECK_LE(width, img_width);  
  417.   CHECK_GE(num, 1);  
  418.   
  419.   CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";  
  420.   
  421.   const Dtype scale = param_.scale();  
  422.   const bool do_mirror = param_.mirror() && Rand(2);  
  423.   const bool has_mean_file = param_.has_mean_file();  
  424.   const bool has_mean_values = mean_values_.size() > 0;  
  425.   
  426.   CHECK_GT(img_channels, 0);  
  427.   CHECK_GE(img_height, crop_size);  
  428.   CHECK_GE(img_width, crop_size);  
  429.   
  430.   Dtype* mean = NULL;  
  431.   if (has_mean_file) {  
  432.     CHECK_EQ(img_channels, data_mean_.channels());  
  433.     CHECK_EQ(img_height, data_mean_.height());  
  434.     CHECK_EQ(img_width, data_mean_.width());  
  435.     mean = data_mean_.mutable_cpu_data();  
  436.   }  
  437.   if (has_mean_values) {  
  438.     CHECK(mean_values_.size() == 1 || mean_values_.size() == img_channels) <<  
  439.      "Specify either 1 mean_value or as many as channels: " << img_channels;  
  440.     if (img_channels > 1 && mean_values_.size() == 1) {  
  441.       // Replicate the mean_value for simplicity  
  442.       for (int c = 1; c < img_channels; ++c) {  
  443.         mean_values_.push_back(mean_values_[0]);  
  444.       }  
  445.     }  
  446.   }  
  447.   
  448.   int h_off = 0;  
  449.   int w_off = 0;  
  450.   cv::Mat cv_cropped_img = cv_img;  
  451.   if (crop_size) {  
  452.     CHECK_EQ(crop_size, height);  
  453.     CHECK_EQ(crop_size, width);  
  454.     // We only do random crop when we do training.  
  455.     if (phase_ == TRAIN) {  
  456.       h_off = Rand(img_height - crop_size + 1);  
  457.       w_off = Rand(img_width - crop_size + 1);  
  458.     } else {  
  459.       h_off = (img_height - crop_size) / 2;  
  460.       w_off = (img_width - crop_size) / 2;  
  461.     }  
  462.     cv::Rect roi(w_off, h_off, crop_size, crop_size);  
  463.     cv_cropped_img = cv_img(roi);  
  464.   } else {  
  465.     CHECK_EQ(img_height, height);  
  466.     CHECK_EQ(img_width, width);  
  467.   }  
  468.   
  469.   CHECK(cv_cropped_img.data);  
  470.   
  471.   Dtype* transformed_data = transformed_blob->mutable_cpu_data();  
  472.   int top_index;  
  473.   for (int h = 0; h < height; ++h) {  
  474.     const uchar* ptr = cv_cropped_img.ptr<uchar>(h);  
  475.     int img_index = 0;  
  476.     for (int w = 0; w < width; ++w) {  
  477.       for (int c = 0; c < img_channels; ++c) {  
  478.         if (do_mirror) {  
  479.           top_index = (c * height + h) * width + (width - 1 - w);  
  480.         } else {  
  481.           top_index = (c * height + h) * width + w;  
  482.         }  
  483.         // int top_index = (c * height + h) * width + w;  
  484.         Dtype pixel = static_cast<Dtype>(ptr[img_index++]);  
  485.         if (has_mean_file) {  
  486.           int mean_index = (c * img_height + h_off + h) * img_width + w_off + w;  
  487.           transformed_data[top_index] =  
  488.             (pixel - mean[mean_index]) * scale;  
  489.         } else {  
  490.           if (has_mean_values) {  
  491.             transformed_data[top_index] =  
  492.               (pixel - mean_values_[c]) * scale;  
  493.           } else {  
  494.             transformed_data[top_index] = pixel * scale;  
  495.           }  
  496.         }  
  497.       }  
  498.     }  
  499.   }  
  500. }  
  501. #endif  // USE_OPENCV  
  502.   
  503. template<typename Dtype>  
  504. void DataTransformer<Dtype>::Transform(Blob<Dtype>* input_blob,  
  505.                                        Blob<Dtype>* transformed_blob) {  
  506.   const int crop_size = param_.crop_size();  
  507.   const int input_num = input_blob->num();  
  508.   const int input_channels = input_blob->channels();  
  509.   const int input_height = input_blob->height();  
  510.   const int input_width = input_blob->width();  
  511.   
  512.   if (transformed_blob->count() == 0) {  
  513.     // Initialize transformed_blob with the right shape.  
  514.     if (crop_size) {  
  515.       transformed_blob->Reshape(input_num, input_channels,  
  516.                                 crop_size, crop_size);  
  517.     } else {  
  518.       transformed_blob->Reshape(input_num, input_channels,  
  519.                                 input_height, input_width);  
  520.     }  
  521.   }  
  522.   
  523.   const int num = transformed_blob->num();  
  524.   const int channels = transformed_blob->channels();  
  525.   const int height = transformed_blob->height();  
  526.   const int width = transformed_blob->width();  
  527.   const int size = transformed_blob->count();  
  528.   
  529.   CHECK_LE(input_num, num);  
  530.   CHECK_EQ(input_channels, channels);  
  531.   CHECK_GE(input_height, height);  
  532.   CHECK_GE(input_width, width);  
  533.   
  534.   
  535.   const Dtype scale = param_.scale();  
  536.   const bool do_mirror = param_.mirror() && Rand(2);  
  537.   const bool has_mean_file = param_.has_mean_file();  
  538.   const bool has_mean_values = mean_values_.size() > 0;  
  539.   
  540.   int h_off = 0;  
  541.   int w_off = 0;  
  542.   if (crop_size) {  
  543.     CHECK_EQ(crop_size, height);  
  544.     CHECK_EQ(crop_size, width);  
  545.     // We only do random crop when we do training.  
  546.     if (phase_ == TRAIN) {  
  547.       h_off = Rand(input_height - crop_size + 1);  
  548.       w_off = Rand(input_width - crop_size + 1);  
  549.     } else {  
  550.       h_off = (input_height - crop_size) / 2;  
  551.       w_off = (input_width - crop_size) / 2;  
  552.     }  
  553.   } else {  
  554.     CHECK_EQ(input_height, height);  
  555.     CHECK_EQ(input_width, width);  
  556.   }  
  557.   
  558.   // 如果有均值文件則  
  559.   Dtype* input_data = input_blob->mutable_cpu_data();  
  560.   if (has_mean_file) {  
  561.     CHECK_EQ(input_channels, data_mean_.channels());  
  562.     CHECK_EQ(input_height, data_mean_.height());  
  563.     CHECK_EQ(input_width, data_mean_.width());  
  564.     for (int n = 0; n < input_num; ++n) {  
  565.       int offset = input_blob->offset(n);  
  566.       /* 
  567.          template <typename Dtype> 
  568.        void caffe_sub(const int N, const Dtype* a, const Dtype* b, Dtype* y); 
  569.        math_function中定義的caffe_sub目的是矩陣相減input_data(以offset開始的矩陣) = input_data(以offset開始的矩陣) - data_mean_ 
  570.     */  
  571.       caffe_sub(data_mean_.count(), input_data + offset,  
  572.             data_mean_.cpu_data(), input_data + offset);  
  573.     }  
  574.   }  
  575.   // 如果每個channel有均值則  
  576.   if (has_mean_values) {  
  577.     CHECK(mean_values_.size() == 1 || mean_values_.size() == input_channels) <<  
  578.      "Specify either 1 mean_value or as many as channels: " << input_channels;  
  579.     if (mean_values_.size() == 1) {  
  580.       caffe_add_scalar(input_blob->count(), -(mean_values_[0]), input_data);  
  581.     } else {  
  582.       for (int n = 0; n < input_num; ++n) {  
  583.         for (int c = 0; c < input_channels; ++c) {  
  584.           int offset = input_blob->offset(n, c);  
  585.           // 給nput_data[offset]地址開始的每一個元素加上一個-mean_values_[c]  
  586.           caffe_add_scalar(input_height * input_width, -(mean_values_[c]),  
  587.             input_data + offset);  
  588.         }  
  589.       }  
  590.     }  
  591.   }  
  592.   
  593.   // 如果啥均值都沒有則直接復制  
  594.   Dtype* transformed_data = transformed_blob->mutable_cpu_data();  
  595.   
  596.   for (int n = 0; n < input_num; ++n) {  
  597.     int top_index_n = n * channels;  
  598.     int data_index_n = n * channels;  
  599.     for (int c = 0; c < channels; ++c) {  
  600.       int top_index_c = (top_index_n + c) * height;  
  601.       int data_index_c = (data_index_n + c) * input_height + h_off;  
  602.       for (int h = 0; h < height; ++h) {  
  603.         int top_index_h = (top_index_c + h) * width;  
  604.         int data_index_h = (data_index_c + h) * input_width + w_off;  
  605.         if (do_mirror) {  
  606.           int top_index_w = top_index_h + width - 1;  
  607.           for (int w = 0; w < width; ++w) {  
  608.             transformed_data[top_index_w-w] = input_data[data_index_h + w];  
  609.           }  
  610.         } else {  
  611.           for (int w = 0; w < width; ++w) {  
  612.             transformed_data[top_index_h + w] = input_data[data_index_h + w];  
  613.           }  
  614.         }  
  615.       }  
  616.     }  
  617.   }  
  618.   if (scale != Dtype(1)) {  
  619.     DLOG(INFO) << "Scale: " << scale;  
  620.     caffe_scal(size, scale, transformed_data);  
  621.   }  
  622. }  
  623.   
  624. template<typename Dtype>  
  625. vector<int> DataTransformer<Dtype>::InferBlobShape(const Datum& datum) {  
  626.   if (datum.encoded()) {  
  627. #ifdef USE_OPENCV // 如果使用OpenCV則可以用先轉換為CVMat,然后在推斷blob的形狀  
  628.     CHECK(!(param_.force_color() && param_.force_gray()))  
  629.         << "cannot set both force_color and force_gray";  
  630.     cv::Mat cv_img;  
  631.     if (param_.force_color() || param_.force_gray()) {  
  632.     // If force_color then decode in color otherwise decode in gray.  
  633.       cv_img = DecodeDatumToCVMat(datum, param_.force_color());  
  634.     } else {  
  635.       cv_img = DecodeDatumToCVMatNative(datum);  
  636.     }  
  637.     // InferBlobShape using the cv::image.  
  638.     return InferBlobShape(cv_img);  
  639. #else  
  640.     LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";  
  641. #endif  // USE_OPENCV  
  642.   }  
  643.   
  644.   // 否則直接粗暴地從datum里面獲取形狀的數據  
  645.   const int crop_size = param_.crop_size();  
  646.   const int datum_channels = datum.channels();  
  647.   const int datum_height = datum.height();  
  648.   const int datum_width = datum.width();  
  649.   // Check dimensions.  
  650.   CHECK_GT(datum_channels, 0);  
  651.   CHECK_GE(datum_height, crop_size);  
  652.   CHECK_GE(datum_width, crop_size);  
  653.   // Build BlobShape.  
  654.   vector<int> shape(4);  
  655.   shape[0] = 1;  
  656.   shape[1] = datum_channels;  
  657.   shape[2] = (crop_size)? crop_size: datum_height;  
  658.   shape[3] = (crop_size)? crop_size: datum_width;  
  659.   return shape;  
  660. }  
  661.   
  662. template<typename Dtype>  
  663. vector<int> DataTransformer<Dtype>::InferBlobShape(  
  664.     const vector<Datum> & datum_vector) {  
  665.   const int num = datum_vector.size();  
  666.   CHECK_GT(num, 0) << "There is no datum to in the vector";  
  667.   // Use first datum in the vector to InferBlobShape.  
  668.   // 使用第一個來進行推斷  
  669.   vector<int> shape = InferBlobShape(datum_vector[0]);  
  670.   // Adjust num to the size of the vector.  
  671.   shape[0] = num;  
  672.   return shape;  
  673. }  
  674.   
  675. #ifdef USE_OPENCV  
  676. // 如果使用OpenCV  
  677. // 使用CVMat中的信息來推斷形狀  
  678. template<typename Dtype>  
  679. vector<int> DataTransformer<Dtype>::InferBlobShape(const cv::Mat& cv_img) {  
  680.   const int crop_size = param_.crop_size();  
  681.   const int img_channels = cv_img.channels();  
  682.   const int img_height = cv_img.rows;  
  683.   const int img_width = cv_img.cols;  
  684.   // Check dimensions.  
  685.   CHECK_GT(img_channels, 0);  
  686.   CHECK_GE(img_height, crop_size);  
  687.   CHECK_GE(img_width, crop_size);  
  688.   // Build BlobShape.  
  689.   vector<int> shape(4);  
  690.   shape[0] = 1;  
  691.   shape[1] = img_channels;  
  692.   shape[2] = (crop_size)? crop_size: img_height;  
  693.   shape[3] = (crop_size)? crop_size: img_width;  
  694.   return shape;  
  695. }  
  696.   
  697. template<typename Dtype>  
  698. vector<int> DataTransformer<Dtype>::InferBlobShape(  
  699.     const vector<cv::Mat> & mat_vector) {  
  700.   const int num = mat_vector.size();  
  701.   CHECK_GT(num, 0) << "There is no cv_img to in the vector";  
  702.   // Use first cv_img in the vector to InferBlobShape.  
  703.   // 使用第一個來推斷  
  704.   vector<int> shape = InferBlobShape(mat_vector[0]);  
  705.   // Adjust num to the size of the vector.  
  706.   shape[0] = num;  
  707.   return shape;  
  708. }  
  709. #endif  // USE_OPENCV  
  710.   
  711. // 初始化隨機數種子  
  712. template <typename Dtype>  
  713. void DataTransformer<Dtype>::InitRand() {  
  714.   // 要么需要鏡像要么訓練階段和需要crop同時滿足的情況下才初始化隨機數種子  
  715.   const bool needs_rand = param_.mirror() ||  
  716.       (phase_ == TRAIN && param_.crop_size());  
  717.   if (needs_rand) {  
  718.     const unsigned int rng_seed = caffe_rng_rand();// 獲得隨機數種子(通過熵池或者時間生成種子)  
  719.     rng_.reset(new Caffe::RNG(rng_seed));//初始化隨機數種子並實例化隨機數生成器  
  720.   } else {  
  721.     rng_.reset();//否則隨機數生成器設置為空  
  722.   }  
  723. }  
  724.   
  725. // 產生從0到n的隨機數  
  726. template <typename Dtype>  
  727. int DataTransformer<Dtype>::Rand(int n) {  
  728.   CHECK(rng_);  
  729.   CHECK_GT(n, 0);  
  730.   caffe::rng_t* rng =  
  731.       static_cast<caffe::rng_t*>(rng_->generator());  
  732.   return ((*rng)() % n);  
  733. }  
  734.   
  735. INSTANTIATE_CLASS(DataTransformer);  
  736. /* 
  737. 初始化類的宏定義是這樣的,前面有講過,這里再給出來 
  738. #define INSTANTIATE_CLASS(classname) \ 
  739.   char gInstantiationGuard##classname; \ 
  740.   template class classname<float>; \ 
  741.   template class classname<double> 
  742. */  
  743.   
  744. }  // namespace caffe  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM