Caffe3——ImageNet數據集創建lmdb類型的數據

ImageNet數據集和cifar,mnist數據集最大的不同,就是數據量特別大;單張圖片尺寸大,訓練樣本個數多;面對如此大的數據集,在轉換成lmdb文件時;使用了很多新的類型對象。

1,動態擴容的數組“vector”,動態地添加新元素

2,pair類型數據對,用於存儲成對的對象,例如存儲文件名和對應標籤

3,利用OpenCV中的圖像處理函數,來讀取和處理大尺寸圖像

一:程序開始

由於要向imageNet數據集中設置resize和是否亂序等參數,所以本文使用gflags命令行解析工具;在Create.sh文件中,調用convert_imageset.bin語句爲:

[cpp] view plain copy
  1. <pre name="code" class="cpp">GLOG_logtostderr=1$TOOLS/convert_imageset \  
  2.     --resize_height=$RESIZE_HEIGHT \  
  3.     --resize_width=$RESIZE_WIDTH \  
  4.     --shuffle \  
  5.     $TRAIN_DATA_ROOT \  圖像數據集存放的根目錄  
  6. $DATA/train.txt \       圖像的ID和對應的分類標籤數字  
  7. $EXAMPLE/ilsvrc12_train_lmdb  lmdb文件保存的路徑  


由於train.txt文件太大,電腦打不開,故打開val.txt一窺之;val.txt中的某個數據爲:

65ILSVRC2012_val_00000002.JPEG ,65應該是對應的標籤,後面的是圖像的編號id。

二:數據轉換流程圖


三:convert_imageset.cpp函數分析

1引入必要的頭文件和命名空間

[cpp] view plain copy
  1. #include<algorithm>//輸出數組的內容、對數組進行升冪排序、反轉數組內容、複製數組內容等操作,  
  2. #include <fstream>  // NOLINT(readability/streams)  
  3. #include <string>  
  4. #include<utility>//utility頭文件定義了一個pair類型,pair類型用於存儲一對數據  
  5. #include<vector>//會自動擴展容量的數組  
  6. #include "boost/scoped_ptr.hpp"//智能指針頭文件  
  7. #include "gflags/gflags.h"  
  8. #include "glog/logging.h"  
  9. #include"caffe/proto/caffe.pb.h"  
  10. #include "caffe/util/db.hpp" //引入包裝好的lmdb操作函數  
  11. #include "caffe/util/io.hpp" //引入opencv中的圖像操作函數  
  12. #include "caffe/util/rng.hpp"  
頭文件和convert_cifar_data.cpp的區別:

1,引入gflags命令行解析工具;

2,引入utility頭文件,裏面提供了數組洗牌等操作

[cpp] view plain copy
  1. using namespace caffe;  // NOLINT(build/namespaces)  
  2. using std::pair;  
  3. using boost::scoped_ptr;  
命名空間區別:

1,引入全部caffe命名空間

2,引入pair對命名空間

2 gflags宏定義參數

//通過gflags宏定義一些程序的參數變量

[cpp] view plain copy
  1. DEFINE_bool(gray, false,"When thisoption is on, treat images as grayscale ones");//是否爲灰度圖片  
  2. DEFINE_bool(shuffle, false,"Randomlyshuffle the order of images and their labels");//定義洗牌變量,是否隨機打亂數據集的順序  
  3. DEFINE_string(backend, "lmdb","The backend {lmdb, leveldb} for storing the result");//默認轉換的數據類型  
  4. DEFINE_int32(resize_width, 0, "Width images areresized to");//定義resize的尺寸,默認爲0,不轉換尺寸  
  5. DEFINE_int32(resize_height, 0, "Height imagesare resized to");  
  6. DEFINE_bool(check_size, false,"When this optionis on, check that all the datum have the samesize");  
  7. DEFINE_bool(encoded, false,"When this option ison, the encoded image will be save in datum");//用於轉換數據格式的  
  8. DEFINE_string(encode_type, "","Optional:What type should we encode the image as ('png','jpg',...).");//要轉換的數據格式  

3 main()函數

沒有想cifar和mnist的main函數,通過調用convert_data()函數來轉換數據,而是直接在main函數內完成了所有數據轉換代碼。

3.1 通過gflags宏定義接收命令行中傳入的參數

[cpp] view plain copy
  1. const boolis_color = !FLAGS_gray;  //通過gflags把宏定義變量的值,賦值給常值變量  
  2. const boolcheck_size = FLAGS_check_size; //檢查圖像的size  
  3. const boolencoded = FLAGS_encoded;//是否編譯(轉換)圖像格式  
  4. const stringencode_type = FLAGS_encode_type;//要編譯的圖像格式  
3.2讀取源數據

3.2.1創建讀取對象變量

std::ifstream infile(argv[2]);//創建指向train.txt文件的文件讀入流

std::vector<std::pair<std::string, int> > lines;//定義向量變量,向量中每個元素爲一個pair對,pair對有兩個成員變量,一個爲string類型,一個爲int類型;其中string類型用於存儲文件名,int類型,感覺用於存數對應類別的id

如val.txt中前幾個字符爲“ILSVRC2012_val_00000001.JPEG65ILSVRC2012_val_00000002.JPEG”;感覺這個string= ILSVRC2012_val_00000001.JPEG   int=65

std::stringfilename;

int label;

3.2.2 讀取數據

  //下面一條while語句是把train.txt文件中存放的所有文件名和標籤,都存放到vextor類型變量lines中;lines中存放圖片的名字和對應的標籤,不存儲真正的圖片數據

[cpp] view plain copy
  1. while (infile>> filename >> label) {  
  2. nes.push_back(std::make_pair(filename, label));  
//make_pair是pair模板中定義的給pair對象賦值的函數,push_back()函數是vector對象的一個成員函數,用來在末端添加新元素}

3.3判斷是否進行洗牌操作

[cpp] view plain copy
  1. if(FLAGS_shuffle) {  
  2.   // randomlyshuffle data  
  3.   LOG(INFO)<< "Shuffling data";  
[cpp] view plain copy
  1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">//洗牌函數,使用隨機生成器g對元素[first,last)容器內部元素進行隨機排列</span>  

 shuffle(lines.begin(), lines.end());//vector.begin() - 回傳一個Iterator迭代器,它指向 vector 第一個元素。}

3.4以智能指針的方式創建db::DB類型的對象 db

[cpp] view plain copy
  1. scoped_ptr<db::DB>db(db::GetDB(FLAGS_backend));  
  2. //智能指針的創建方式類似泛型的格式,上面通過db.cpp內定義的命名的子命名空間中db的“成員函數”GetDB函數來初始化db對象  
  3. db->Open(argv[3], db::NEW);//argv[3]的文件夾下創建並打開lmdb的操作環境  
  4. scoped_ptr<db::Transaction>txn(db->NewTransaction());//創建lmdb文件的操作句柄  

3.5 源數據中提取圖像數據

3.5.1 通過ReadImageToDatum函數把圖像數據讀取到datum中

//到源數據位置讀取每張圖片的數據。(../imagenet/xxx.jpeg,65,256,256,true,jpeg,&datum)

[cpp] view plain copy
  1. status= ReadImageToDatum(root_folder + lines[line_id].first,lines[line_id].second, resize_height,resize_width, is_color,enc, &datum); //把圖像數據讀取到datum中  
3.5.2  ReadImageToDatum函數說明

ReadImageToDatum函數爲io.cpp文件中定義的函數;io.cpp主要實現了3部分功能:

1,從text文件或者二進制文件中讀寫proto文件;

2,利用opencv的Mat矩陣,把圖像數據讀到Mat矩陣中;

3,把Mat矩陣中的值放入到datum中

3.5.3 檢查數據尺寸

[cpp] view plain copy
  1. if (check_size) {//檢查圖片尺寸  
  2.       if (!data_size_initialized) {//若data_size_initialized沒有初始化  
  3.         data_size = datum.channels() *datum.height() * datum.width();  
  4.         data_size_initialized = true;  
  5.       } else {  
  6.         const std::string& data =datum.data();  
  7.         CHECK_EQ(data.size(), data_size)<< "Incorrect data field size "<< data.size();  
  8.       }  

3.6   序列化鍵和值並放入臨時數據庫

[cpp] view plain copy
  1. // sequential  
  2. intlength = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id,lines[line_id].first.c_str());//若line_id=1234,lines[line_id].first=“abc.jpeg” 則 key_str=00001234_abc.jpeg,length=00001234_abc.jpeg字符串的長度  
  3.     // Put in db  
  4.  string out;  
  5.     CHECK(datum.SerializeToString(&out));//datum數據,序列化到字符串中  
  6.     txn->Put(string(key_cstr, length), out);//把鍵值對放入到數據庫  
3.7 批量提交到lmdb文件

[cpp] view plain copy
  1. if (++count % 1000 == 0) {  
  2.       // Commit db  
  3.       txn->Commit();//保存到lmdb類型的文件  
  4.       txn.reset(db->NewTransaction());//重新初始化操作句柄  
  5.       LOG(ERROR) << "Processed" << count << " files.";  
  6.     }  

四,相關文件

4.1 Convert_imageset.cpp文件

[cpp] view plain copy
  1. // This program converts a set of images to a lmdb/leveldb by storing them  
  2. // as Datum proto buffers.  
  3. // Usage:  
  4. //   convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME  
  5. //  
  6. // where ROOTFOLDER is the root folder that holds all the images, and LISTFILE  
  7. // should be a list of files as well as their labels, in the format as  
  8. //   subfolder1/file1.JPEG 7  
  9. //   ....  
  10.   
  11. #include <algorithm>//輸出數組的內容、對數組進行升冪排序、反轉數組內容、複製數組內容等操作,  
  12. #include <fstream>  // NOLINT(readability/streams)  
  13. #include <string>  
  14. #include <utility>//utility頭文件定義了一個pair類型  
  15. #include <vector>//會自動擴展容量的數組  
  16.   
  17. #include "boost/scoped_ptr.hpp"  
  18. #include "gflags/gflags.h"  
  19. #include "glog/logging.h"  
  20.   
  21. #include "caffe/proto/caffe.pb.h"  
  22. #include "caffe/util/db.hpp"  
  23. #include "caffe/util/io.hpp"  
  24. #include "caffe/util/rng.hpp"  
  25.   
  26. using namespace caffe;  // NOLINT(build/namespaces)  
  27. using std::pair;  
  28. using boost::scoped_ptr;  
  29.   
  30.   
  31. //通過gflags宏定義一些程序的參數變量  
  32. DEFINE_bool(gray, false,  
  33.     "When this option is on, treat images as grayscale ones");  
  34. DEFINE_bool(shuffle, false,  
  35.     "Randomly shuffle the order of images and their labels");//洗牌,隨機打亂數據集的順序  
  36. DEFINE_string(backend, "lmdb",  
  37.         "The backend {lmdb, leveldb} for storing the result");  
  38. DEFINE_int32(resize_width, 0, "Width images are resized to");  
  39. DEFINE_int32(resize_height, 0, "Height images are resized to");  
  40. DEFINE_bool(check_size, false,  
  41.     "When this option is on, check that all the datum have the same size");  
  42. DEFINE_bool(encoded, false,  
  43.     "When this option is on, the encoded image will be save in datum");//用於轉換數據格式的  
  44. DEFINE_string(encode_type, "",  
  45.     "Optional: What type should we encode the image as ('png','jpg',...).");//要轉換的數據格式  
  46.   
  47. int main(int argc, char** argv) {  
  48.   ::google::InitGoogleLogging(argv[0]);  
  49.   
  50. #ifndef GFLAGS_GFLAGS_H_  
  51.   namespace gflags = google;  
  52. #endif  
  53.   
  54.   gflags::SetUsageMessage("Convert a set of images to the leveldb/lmdb\n"  
  55.         "format used as input for Caffe.\n"  
  56.         "Usage:\n"  
  57.         "    convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME\n"  
  58.         "The ImageNet dataset for the training demo is at\n"  
  59.         "    http://www.image-net.org/download-images\n");  
  60.   gflags::ParseCommandLineFlags(&argc, &argv, true);  
  61.   
  62.   if (argc < 4) {  
  63.     gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/convert_imageset");  
  64.     return 1;  
  65.   }  
  66.   //arg[1] 訓練集存放的地址,arg[2] train.txt(估計是訓練集中所有圖片的文件名稱),arg[3] 要保存的文件名稱xxlmdb  
  67.   const bool is_color = !FLAGS_gray;  //通過gflags把宏定義變量的值,賦值給常值變量  
  68.   const bool check_size = FLAGS_check_size; //檢查圖像的size  
  69.   const bool encoded = FLAGS_encoded;//是否編譯(轉換)圖像格式  
  70.   const string encode_type = FLAGS_encode_type;//要編譯的圖像格式  
  71.   
  72.   std::ifstream infile(argv[2]);//定義指向train.txt數據文件的文件讀入流  
  73.   std::vector<std::pair<std::string, int> > lines;//定義向量變量,向量中每個元素爲一個pair對,pair對有兩個成員變量,一個爲string類型,一個爲int類型  
  74.   std::string filename;  
  75.   int label;  
  76.   //下面一條while語句是把train.txt文件中存數的數據和標籤,都存放到vextor類型變量中lines中;lines中存放圖片的名字和對應的標籤,不存儲真正的圖片數據  
  77.   while (infile >> filename >> label) {  
  78.     lines.push_back(std::make_pair(filename, label));//make_pair是pair模板中定義的給pair對象賦值的函數,push_back()函數是vector對象的一個成員函數,用來在末端添加新元素  
  79.   }  
  80.   if (FLAGS_shuffle) {  
  81.     // randomly shuffle data  
  82.     LOG(INFO) << "Shuffling data";  
  83.     //洗牌函數,使用隨機生成器g對元素[first, last)容器內部元素進行隨機排列  
  84.     shuffle(lines.begin(), lines.end());//vector.begin() - 回傳一個Iterator迭代器,它指向 vector 第一個元素。  
  85.   }  
  86.   LOG(INFO) << "A total of " << lines.size() << " images.";  
  87.   
  88.   if (encode_type.size() && !encoded)  
  89.     LOG(INFO) << "encode_type specified, assuming encoded=true.";  
  90.   
  91.   int resize_height = std::max<int>(0, FLAGS_resize_height);  
  92.   int resize_width = std::max<int>(0, FLAGS_resize_width);  
  93.   
  94.   // Create new DB  
  95.   scoped_ptr<db::DB> db(db::GetDB(FLAGS_backend));  
  96.   db->Open(argv[3], db::NEW);//argv[3]的文件夾下打開創建lmdb的操作環境  
  97.   scoped_ptr<db::Transaction> txn(db->NewTransaction());//創建lmdb文件的操作句柄  
  98.   
  99.   // Storing to db  
  100.   std::string root_folder(argv[1]);//把源數據文件的地址複製給root_folder  
  101.   Datum datum;//聲明數據“轉換”對象  
  102.   int count = 0;  
  103.   const int kMaxKeyLength = 256;  
  104.   char key_cstr[kMaxKeyLength];  
  105.   int data_size = 0;  
  106.   bool data_size_initialized = false;  
  107.   
  108.   for (int line_id = 0; line_id < lines.size(); ++line_id) {  
  109.     bool status;  
  110.     std::string enc = encode_type; //enc爲空串,則enc.size()=false;否則爲true  
  111.     if (encoded && !enc.size()) {  
  112.       // Guess the encoding type from the file name  
  113.       string fn = lines[line_id].first;//把圖像的文件名賦值給fn(filename)  
  114.       size_t p = fn.rfind('.');//rfind函數的返回值是一個整形的索引值,直線要查找的字符在字符串中的位置;若沒有找到,返回string::npos  
  115.       if ( p == fn.npos )  
  116.         LOG(WARNING) << "Failed to guess the encoding of '" << fn << "'";  
  117.       enc = fn.substr(p);//找到了,就截取文件名”.“後面的字符串,以獲得圖像格式字符串  
  118.       std::transform(enc.begin(), enc.end(), enc.begin(), ::tolower);//將enc字符串轉換成小寫  
  119.     }  
  120.     //到源數據位置,以此讀取每張圖片的數據。(../imagenet/xxx.jpeg,65,256,256,true,jpeg,&datum)  
  121.     status = ReadImageToDatum(root_folder + lines[line_id].first,  
  122.         lines[line_id].second, resize_height, resize_width, is_color,enc, &datum);  //把圖像數據讀取到datum中  
  123.     if (status == falsecontinue;//status=false,說明此張圖片讀取錯誤;“跳過”繼續下一張  
  124.     if (check_size) {//檢查圖片尺寸  
  125.       if (!data_size_initialized) {//若data_size_initialized沒有初始化  
  126.         data_size = datum.channels() * datum.height() * datum.width();  
  127.         data_size_initialized = true;  
  128.       } else {  
  129.         const std::string& data = datum.data();  
  130.         CHECK_EQ(data.size(), data_size) << "Incorrect data field size "  
  131.             << data.size();  
  132.       }  
  133.     }  
  134.     // sequential  
  135.     int length = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id,   
  136.         lines[line_id].first.c_str());//若line_id=1234,lines[line_id].first=“abc.jpeg” 則 key_str=00001234_abc.jpeg,length=00001234_abc.jpeg字符串的長度  
  137.   
  138.     // Put in db  
  139.     string out;  
  140.     CHECK(datum.SerializeToString(&out));//datum數據,序列化到字符串中  
  141.     txn->Put(string(key_cstr, length), out);//把鍵值對放入到數據庫  
  142.   
  143.     if (++count % 1000 == 0) {  
  144.       // Commit db  
  145.       txn->Commit();//保存到lmdb類型的文件  
  146.       txn.reset(db->NewTransaction());//重新初始化操作句柄  
  147.       LOG(ERROR) << "Processed " << count << " files.";  
  148.     }  
  149.   }  
  150.   // write the last batch  
  151.   if (count % 1000 != 0) {  
  152.     txn->Commit();  
  153.     LOG(ERROR) << "Processed " << count << " files.";  
  154.   }  
  155.   return 0;  
  156. }  
4.2 io.cpp文件

[cpp] view plain copy
  1. #include <fcntl.h>  
  2. #include <google/protobuf/io/coded_stream.h>  
  3. #include <google/protobuf/io/zero_copy_stream_impl.h>  
  4. #include <google/protobuf/text_format.h>  
  5. #include <opencv2/core/core.hpp>  
  6. #include <opencv2/highgui/highgui.hpp>  
  7. #include <opencv2/highgui/highgui_c.h>  
  8. #include <opencv2/imgproc/imgproc.hpp>  
  9. #include <stdint.h>  
  10.   
  11. #include <algorithm>  
  12. #include <fstream>  // NOLINT(readability/streams)  
  13. #include <string>  
  14. #include <vector>  
  15.   
  16. #include "caffe/common.hpp"  
  17. #include "caffe/proto/caffe.pb.h"  
  18. #include "caffe/util/io.hpp"  
  19.   
  20. const int kProtoReadBytesLimit = INT_MAX;  // Max size of 2 GB minus 1 byte.  
  21.   
  22. namespace caffe {  
  23.   
  24. using google::protobuf::io::FileInputStream;//文件輸入流  
  25. using google::protobuf::io::FileOutputStream;//文件輸出流  
  26. using google::protobuf::io::ZeroCopyInputStream;//These interfaces are different from classic I/O streams in that they try to minimize the amount of data copying that needs to be done  
  27. using google::protobuf::io::CodedInputStream;  
  28. using google::protobuf::io::ZeroCopyOutputStream;  
  29. using google::protobuf::io::CodedOutputStream;  
  30. using google::protobuf::Message;  
  31.   
  32. bool ReadProtoFromTextFile(const char* filename, Message* proto) {//從文本文件中讀入proto文件  
  33.   int fd = open(filename, O_RDONLY);  
  34.   CHECK_NE(fd, -1) << "File not found: " << filename;  
  35.   FileInputStream* input = new FileInputStream(fd);  
  36.   bool success = google::protobuf::TextFormat::Parse(input, proto);  
  37.   delete input;  
  38.   close(fd);  
  39.   return success;  
  40. }  
  41.   
  42. void WriteProtoToTextFile(const Message& proto, const char* filename) {//想文本文件中寫入proto文件  
  43.   int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);  
  44.   FileOutputStream* output = new FileOutputStream(fd);  
  45.   CHECK(google::protobuf::TextFormat::Print(proto, output));  
  46.   delete output;  
  47.   close(fd);  
  48. }  
  49.   
  50. bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {//從二進制文件讀入proto  
  51.   int fd = open(filename, O_RDONLY);  
  52.   CHECK_NE(fd, -1) << "File not found: " << filename;  
  53.   ZeroCopyInputStream* raw_input = new FileInputStream(fd);  
  54.   CodedInputStream* coded_input = new CodedInputStream(raw_input);  
  55.   coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);  
  56.   
  57.   bool success = proto->ParseFromCodedStream(coded_input);  
  58.   
  59.   delete coded_input;  
  60.   delete raw_input;  
  61.   close(fd);  
  62.   return success;  
  63. }  
  64.   
  65. void WriteProtoToBinaryFile(const Message& proto, const char* filename) {//把proto寫入二進制文件中  
  66.   fstream output(filename, ios::out | ios::trunc | ios::binary);  
  67.   CHECK(proto.SerializeToOstream(&output));  
  68. }  
  69.   
  70.   
  71. //基本上講 Mat 是一個類,由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和  
  72. //一個指向存儲所有像素值的矩陣的指針(根據所選存儲方法的不同矩陣可以是不同的維數)。  
  73. //矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依圖像的不同而不同,通常比矩陣頭的尺寸大數個數量級。因此,當在程序中傳遞圖像並創建拷貝時,  
  74. //大的開銷是由矩陣造成的,而不是信息頭。OpenCV是一個圖像處理庫,囊括了大量的圖像處理函數,爲了解決問題通常要使用庫中的多個函數,  
  75. //因此在函數中傳遞圖像是家常便飯。同時不要忘了我們正在討論的是計算量很大的圖像處理算法,因此,除非萬不得已,我們不應該拷貝 大 的圖像,因爲這會降低程序速度。  
  76. cv::Mat ReadImageToCVMat(const string& filename,  
  77.     const int height, const int width, const bool is_color) {//讀取圖片到CVMat中,cv::Mat ,Mat數據結構式opencv2.0以後的特定的數據類型  
  78.   cv::Mat cv_img;  
  79.   int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :  
  80.     CV_LOAD_IMAGE_GRAYSCALE);  
  81.   cv::Mat cv_img_origin = cv::imread(filename, cv_read_flag);//讀取圖片內容  
  82.   if (!cv_img_origin.data) {  
  83.     LOG(ERROR) << "Could not open or find file " << filename;  
  84.     return cv_img_origin;  
  85.   }  
  86.   if (height > 0 && width > 0) {  
  87.     cv::resize(cv_img_origin, cv_img, cv::Size(width, height));  
  88.   } else {  
  89.     cv_img = cv_img_origin;  
  90.   }  
  91.   return cv_img;  
  92. }  
  93.   
  94. cv::Mat ReadImageToCVMat(const string& filename,//讀取圖片到CVMat中,重載1  
  95.     const int height, const int width) {  
  96.   return ReadImageToCVMat(filename, height, width, true);  
  97. }  
  98.   
  99. cv::Mat ReadImageToCVMat(const string& filename,//讀取圖片到CVMat中,重載2  
  100.     const bool is_color) {  
  101.   return ReadImageToCVMat(filename, 0, 0, is_color);  
  102. }  
  103.   
  104. cv::Mat ReadImageToCVMat(const string& filename) {//讀取圖片到CVMat中,重載3  
  105.   return ReadImageToCVMat(filename, 0, 0, true);  
  106. }  
  107. // Do the file extension and encoding match?  
  108. static bool matchExt(const std::string & fn, //匹配拓展名稱?  
  109.                      std::string en) {  
  110.   size_t p = fn.rfind('.');//查找"."字符,若找到則返回“.”在字符串中的位置,找不到則返回npos  
  111.   std::string ext = p != fn.npos ? fn.substr(p) : fn;//如果字符串fn中存在".“,則截取字符串p  
  112.   std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);//把ext變成小寫  
  113.   std::transform(en.begin(), en.end(), en.begin(), ::tolower);  
  114.   if ( ext == en )  
  115.     return true;  
  116.   if ( en == "jpg" && ext == "jpeg" )  
  117.     return true;  
  118.   return false;  
  119. }  
  120.   
  121. bool ReadImageToDatum(const string& filename, const int label,//把圖片讀到 Datum中  
  122.     const int height, const int width, const bool is_color,  
  123.     const std::string & encoding, Datum* datum) {  
  124.   cv::Mat cv_img = ReadImageToCVMat(filename, height, width, is_color);//先把數據讀到cv::Mat類型矩陣中  
  125.   if (cv_img.data) {//Mat矩陣中數據指針Mat.data是uchar類型指針,矩陣中的元素應該是uchar類型;該語句是判斷cv_img中是否有數據  
  126.     if (encoding.size()) {//是否需要編碼  
  127.       if ( (cv_img.channels() == 3) == is_color && !height && !width &&  
  128.           matchExt(filename, encoding) )  
  129.         return ReadFileToDatum(filename, label, datum);  
  130.   
  131.       std::vector<uchar> buf;  
  132.       cv::imencode("."+encoding, cv_img, buf);//感覺這行代碼的作用是把cv_img中的值賦值給buf  
  133.       datum->set_data(std::string(reinterpret_cast<char*>(&buf[0]),  
  134.                       buf.size()));  
  135.       datum->set_label(label);  
  136.       datum->set_encoded(true);//感覺是一種編碼函數  
  137.       return true;  
  138.     }  
  139.     CVMatToDatum(cv_img, datum);  
  140.     datum->set_label(label);  
  141.     return true;  
  142.   } else {  
  143.     return false;  
  144.   }  
  145. }  
  146.   
  147. bool ReadFileToDatum(const string& filename, const int label,  
  148.     Datum* datum) {  
  149.   std::streampos size;  
  150.   
  151.   fstream file(filename.c_str(), ios::in|ios::binary|ios::ate);  
  152.   if (file.is_open()) {  
  153.     size = file.tellg();  
  154.     std::string buffer(size, ' ');  
  155.     file.seekg(0, ios::beg);  
  156.     file.read(&buffer[0], size);  
  157.     file.close();  
  158.     datum->set_data(buffer);  
  159.     datum->set_label(label);  
  160.     datum->set_encoded(true);  
  161.     return true;  
  162.   } else {  
  163.     return false;  
  164.   }  
  165. }  
  166.   
  167. cv::Mat DecodeDatumToCVMatNative(const Datum& datum) {  
  168.   cv::Mat cv_img;  
  169.   CHECK(datum.encoded()) << "Datum not encoded";  
  170.   const string& data = datum.data();  
  171.   std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());  
  172.   cv_img = cv::imdecode(vec_data, -1);  
  173.   if (!cv_img.data) {  
  174.     LOG(ERROR) << "Could not decode datum ";  
  175.   }  
  176.   return cv_img;  
  177. }  
  178. cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color) {  
  179.   cv::Mat cv_img;  
  180.   CHECK(datum.encoded()) << "Datum not encoded";  
  181.   const string& data = datum.data();  
  182.   std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());  
  183.   int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :  
  184.     CV_LOAD_IMAGE_GRAYSCALE);  
  185.   cv_img = cv::imdecode(vec_data, cv_read_flag);  
  186.   if (!cv_img.data) {  
  187.     LOG(ERROR) << "Could not decode datum ";  
  188.   }  
  189.   return cv_img;  
  190. }  
  191.   
  192. // If Datum is encoded will decoded using DecodeDatumToCVMat and CVMatToDatum  
  193. // If Datum is not encoded will do nothing  
  194. bool DecodeDatumNative(Datum* datum) {  
  195.   if (datum->encoded()) {  
  196.     cv::Mat cv_img = DecodeDatumToCVMatNative((*datum));  
  197.     CVMatToDatum(cv_img, datum);  
  198.     return true;  
  199.   } else {  
  200.     return false;  
  201.   }  
  202. }  
  203. bool DecodeDatum(Datum* datum, bool is_color) {  
  204.   if (datum->encoded()) {  
  205.     cv::Mat cv_img = DecodeDatumToCVMat((*datum), is_color);  
  206.     CVMatToDatum(cv_img, datum);  
  207.     return true;  
  208.   } else {  
  209.     return false;  
  210.   }  
  211. }  
  212.   
  213. void CVMatToDatum(const cv::Mat& cv_img, Datum* datum) {  
  214.   CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";  
  215.   datum->set_channels(cv_img.channels());  
  216.   datum->set_height(cv_img.rows);  
  217.   datum->set_width(cv_img.cols);  
  218.   datum->clear_data();  
  219.   datum->clear_float_data();  
  220.   datum->set_encoded(false);  
  221.   int datum_channels = datum->channels();  
  222.   int datum_height = datum->height();  
  223.   int datum_width = datum->width();  
  224.   int datum_size = datum_channels * datum_height * datum_width;  
  225.   std::string buffer(datum_size, ' ');  
  226.   for (int h = 0; h < datum_height; ++h) {  
  227.     const uchar* ptr = cv_img.ptr<uchar>(h);  
  228.     int img_index = 0;  
  229.     for (int w = 0; w < datum_width; ++w) {  
  230.       for (int c = 0; c < datum_channels; ++c) {  
  231.         int datum_index = (c * datum_height + h) * datum_width + w;  
  232.         buffer[datum_index] = static_cast<char>(ptr[img_index++]);  
  233.       }  
  234.     }  
  235.   }  
  236.   datum->set_data(buffer);  
  237. }  
  238. 。。。。。  
五,以上代碼註釋爲個人理解,如有遺漏,錯誤還望大家多多交流,指正,以便共同學習,進步!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章