Caffe3——ImageNet数据集创建lmdb类型的数据

Caffe3——ImageNet数据集创建lmdb类型的数据

ImageNet数据集和cifar,mnist数据集最大的不同,就是数据量特别大;单张图片尺寸大,训练样本个数多;面对如此大的数据集,在转换成lmdb文件时;使用了很多新的类型对象。

1,动态扩容的数组“vector”,动态地添加新元素

2,pair类型数据对,用于存储成对的对象,例如存储文件名和对应标签

3,利用opencv中的图像处理函数,来读取和处理大尺寸图像

一:程序开始

由于要向imageNet数据集中设置resize和是否乱序等参数,所以本文使用gflags命令行解析工具;在Create.sh文件中,调用convert_imageset.bin语句为:

[cpp] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  1. if(FLAGS_shuffle) {
  2. // randomlyshuffle data
  3. LOG(INFO)<< "Shuffling data";

[cpp] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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. #include <algorithm>//输出数组的内容、对数组进行升幂排序、反转数组内容、复制数组内容等操作,
  11. #include <fstream>  // NOLINT(readability/streams)
  12. #include <string>
  13. #include <utility>//utility头文件定义了一个pair类型
  14. #include <vector>//会自动扩展容量的数组
  15. #include "boost/scoped_ptr.hpp"
  16. #include "gflags/gflags.h"
  17. #include "glog/logging.h"
  18. #include "caffe/proto/caffe.pb.h"
  19. #include "caffe/util/db.hpp"
  20. #include "caffe/util/io.hpp"
  21. #include "caffe/util/rng.hpp"
  22. using namespace caffe;  // NOLINT(build/namespaces)
  23. using std::pair;
  24. using boost::scoped_ptr;
  25. //通过gflags宏定义一些程序的参数变量
  26. DEFINE_bool(gray, false,
  27. "When this option is on, treat images as grayscale ones");
  28. DEFINE_bool(shuffle, false,
  29. "Randomly shuffle the order of images and their labels");//洗牌,随机打乱数据集的顺序
  30. DEFINE_string(backend, "lmdb",
  31. "The backend {lmdb, leveldb} for storing the result");
  32. DEFINE_int32(resize_width, 0, "Width images are resized to");
  33. DEFINE_int32(resize_height, 0, "Height images are resized to");
  34. DEFINE_bool(check_size, false,
  35. "When this option is on, check that all the datum have the same size");
  36. DEFINE_bool(encoded, false,
  37. "When this option is on, the encoded image will be save in datum");//用于转换数据格式的
  38. DEFINE_string(encode_type, "",
  39. "Optional: What type should we encode the image as (‘png‘,‘jpg‘,...).");//要转换的数据格式
  40. int main(int argc, char** argv) {
  41. ::google::InitGoogleLogging(argv[0]);
  42. #ifndef GFLAGS_GFLAGS_H_
  43. namespace gflags = google;
  44. #endif
  45. gflags::SetUsageMessage("Convert a set of images to the leveldb/lmdb\n"
  46. "format used as input for Caffe.\n"
  47. "Usage:\n"
  48. "    convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME\n"
  49. "The ImageNet dataset for the training demo is at\n"
  50. "    http://www.image-net.org/download-images\n");
  51. gflags::ParseCommandLineFlags(&argc, &argv, true);
  52. if (argc < 4) {
  53. gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/convert_imageset");
  54. return 1;
  55. }
  56. //arg[1] 训练集存放的地址,arg[2] train.txt(估计是训练集中所有图片的文件名称),arg[3] 要保存的文件名称xxlmdb
  57. const bool is_color = !FLAGS_gray;  //通过gflags把宏定义变量的值,赋值给常值变量
  58. const bool check_size = FLAGS_check_size; //检查图像的size
  59. const bool encoded = FLAGS_encoded;//是否编译(转换)图像格式
  60. const string encode_type = FLAGS_encode_type;//要编译的图像格式
  61. std::ifstream infile(argv[2]);//定义指向train.txt数据文件的文件读入流
  62. std::vector<std::pair<std::string, int> > lines;//定义向量变量,向量中每个元素为一个pair对,pair对有两个成员变量,一个为string类型,一个为int类型
  63. std::string filename;
  64. int label;
  65. //下面一条while语句是把train.txt文件中存数的数据和标签,都存放到vextor类型变量中lines中;lines中存放图片的名字和对应的标签,不存储真正的图片数据
  66. while (infile >> filename >> label) {
  67. lines.push_back(std::make_pair(filename, label));//make_pair是pair模板中定义的给pair对象赋值的函数,push_back()函数是vector对象的一个成员函数,用来在末端添加新元素
  68. }
  69. if (FLAGS_shuffle) {
  70. // randomly shuffle data
  71. LOG(INFO) << "Shuffling data";
  72. //洗牌函数,使用随机生成器g对元素[first, last)容器内部元素进行随机排列
  73. shuffle(lines.begin(), lines.end());//vector.begin() - 回传一个Iterator迭代器,它指向 vector 第一个元素。
  74. }
  75. LOG(INFO) << "A total of " << lines.size() << " images.";
  76. if (encode_type.size() && !encoded)
  77. LOG(INFO) << "encode_type specified, assuming encoded=true.";
  78. int resize_height = std::max<int>(0, FLAGS_resize_height);
  79. int resize_width = std::max<int>(0, FLAGS_resize_width);
  80. // Create new DB
  81. scoped_ptr<db::DB> db(db::GetDB(FLAGS_backend));
  82. db->Open(argv[3], db::NEW);//argv[3]的文件夹下打开创建lmdb的操作环境
  83. scoped_ptr<db::Transaction> txn(db->NewTransaction());//创建lmdb文件的操作句柄
  84. // Storing to db
  85. std::string root_folder(argv[1]);//把源数据文件的地址复制给root_folder
  86. Datum datum;//声明数据“转换”对象
  87. int count = 0;
  88. const int kMaxKeyLength = 256;
  89. char key_cstr[kMaxKeyLength];
  90. int data_size = 0;
  91. bool data_size_initialized = false;
  92. for (int line_id = 0; line_id < lines.size(); ++line_id) {
  93. bool status;
  94. std::string enc = encode_type; //enc为空串,则enc.size()=false;否则为true
  95. if (encoded && !enc.size()) {
  96. // Guess the encoding type from the file name
  97. string fn = lines[line_id].first;//把图像的文件名赋值给fn(filename)
  98. size_t p = fn.rfind(‘.‘);//rfind函数的返回值是一个整形的索引值,直线要查找的字符在字符串中的位置;若没有找到,返回string::npos
  99. if ( p == fn.npos )
  100. LOG(WARNING) << "Failed to guess the encoding of ‘" << fn << "‘";
  101. enc = fn.substr(p);//找到了,就截取文件名”.“后面的字符串,以获得图像格式字符串
  102. std::transform(enc.begin(), enc.end(), enc.begin(), ::tolower);//将enc字符串转换成小写
  103. }
  104. //到源数据位置,以此读取每张图片的数据。(../imagenet/xxx.jpeg,65,256,256,true,jpeg,&datum)
  105. status = ReadImageToDatum(root_folder + lines[line_id].first,
  106. lines[line_id].second, resize_height, resize_width, is_color,enc, &datum);  //把图像数据读取到datum中
  107. if (status == false) continue;//status=false,说明此张图片读取错误;“跳过”继续下一张
  108. if (check_size) {//检查图片尺寸
  109. if (!data_size_initialized) {//若data_size_initialized没有初始化
  110. data_size = datum.channels() * datum.height() * datum.width();
  111. data_size_initialized = true;
  112. } else {
  113. const std::string& data = datum.data();
  114. CHECK_EQ(data.size(), data_size) << "Incorrect data field size "
  115. << data.size();
  116. }
  117. }
  118. // sequential
  119. int length = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id,
  120. lines[line_id].first.c_str());//若line_id=1234,lines[line_id].first=“abc.jpeg” 则 key_str=00001234_abc.jpeg,length=00001234_abc.jpeg字符串的长度
  121. // Put in db
  122. string out;
  123. CHECK(datum.SerializeToString(&out));//datum数据,序列化到字符串中
  124. txn->Put(string(key_cstr, length), out);//把键值对放入到数据库
  125. if (++count % 1000 == 0) {
  126. // Commit db
  127. txn->Commit();//保存到lmdb类型的文件
  128. txn.reset(db->NewTransaction());//重新初始化操作句柄
  129. LOG(ERROR) << "Processed " << count << " files.";
  130. }
  131. }
  132. // write the last batch
  133. if (count % 1000 != 0) {
  134. txn->Commit();
  135. LOG(ERROR) << "Processed " << count << " files.";
  136. }
  137. return 0;
  138. }

4.2 io.cpp文件

[cpp] view plaincopy

  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. #include <algorithm>
  11. #include <fstream>  // NOLINT(readability/streams)
  12. #include <string>
  13. #include <vector>
  14. #include "caffe/common.hpp"
  15. #include "caffe/proto/caffe.pb.h"
  16. #include "caffe/util/io.hpp"
  17. const int kProtoReadBytesLimit = INT_MAX;  // Max size of 2 GB minus 1 byte.
  18. namespace caffe {
  19. using google::protobuf::io::FileInputStream;//文件输入流
  20. using google::protobuf::io::FileOutputStream;//文件输出流
  21. 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
  22. using google::protobuf::io::CodedInputStream;
  23. using google::protobuf::io::ZeroCopyOutputStream;
  24. using google::protobuf::io::CodedOutputStream;
  25. using google::protobuf::Message;
  26. bool ReadProtoFromTextFile(const char* filename, Message* proto) {//从文本文件中读入proto文件
  27. int fd = open(filename, O_RDONLY);
  28. CHECK_NE(fd, -1) << "File not found: " << filename;
  29. FileInputStream* input = new FileInputStream(fd);
  30. bool success = google::protobuf::TextFormat::Parse(input, proto);
  31. delete input;
  32. close(fd);
  33. return success;
  34. }
  35. void WriteProtoToTextFile(const Message& proto, const char* filename) {//想文本文件中写入proto文件
  36. int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  37. FileOutputStream* output = new FileOutputStream(fd);
  38. CHECK(google::protobuf::TextFormat::Print(proto, output));
  39. delete output;
  40. close(fd);
  41. }
  42. bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {//从二进制文件读入proto
  43. int fd = open(filename, O_RDONLY);
  44. CHECK_NE(fd, -1) << "File not found: " << filename;
  45. ZeroCopyInputStream* raw_input = new FileInputStream(fd);
  46. CodedInputStream* coded_input = new CodedInputStream(raw_input);
  47. coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);
  48. bool success = proto->ParseFromCodedStream(coded_input);
  49. delete coded_input;
  50. delete raw_input;
  51. close(fd);
  52. return success;
  53. }
  54. void WriteProtoToBinaryFile(const Message& proto, const char* filename) {//把proto写入二进制文件中
  55. fstream output(filename, ios::out | ios::trunc | ios::binary);
  56. CHECK(proto.SerializeToOstream(&output));
  57. }
  58. //基本上讲 Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和
  59. //一个指向存储所有像素值的矩阵的指针(根据所选存储方法的不同矩阵可以是不同的维数)。
  60. //矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝时,
  61. //大的开销是由矩阵造成的,而不是信息头。OpenCV是一个图像处理库,囊括了大量的图像处理函数,为了解决问题通常要使用库中的多个函数,
  62. //因此在函数中传递图像是家常便饭。同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝 大 的图像,因为这会降低程序速度。
  63. cv::Mat ReadImageToCVMat(const string& filename,
  64. const int height, const int width, const bool is_color) {//读取图片到CVMat中,cv::Mat ,Mat数据结构式opencv2.0以后的特定的数据类型
  65. cv::Mat cv_img;
  66. int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :
  67. CV_LOAD_IMAGE_GRAYSCALE);
  68. cv::Mat cv_img_origin = cv::imread(filename, cv_read_flag);//读取图片内容
  69. if (!cv_img_origin.data) {
  70. LOG(ERROR) << "Could not open or find file " << filename;
  71. return cv_img_origin;
  72. }
  73. if (height > 0 && width > 0) {
  74. cv::resize(cv_img_origin, cv_img, cv::Size(width, height));
  75. } else {
  76. cv_img = cv_img_origin;
  77. }
  78. return cv_img;
  79. }
  80. cv::Mat ReadImageToCVMat(const string& filename,//读取图片到CVMat中,重载1
  81. const int height, const int width) {
  82. return ReadImageToCVMat(filename, height, width, true);
  83. }
  84. cv::Mat ReadImageToCVMat(const string& filename,//读取图片到CVMat中,重载2
  85. const bool is_color) {
  86. return ReadImageToCVMat(filename, 0, 0, is_color);
  87. }
  88. cv::Mat ReadImageToCVMat(const string& filename) {//读取图片到CVMat中,重载3
  89. return ReadImageToCVMat(filename, 0, 0, true);
  90. }
  91. // Do the file extension and encoding match?
  92. static bool matchExt(const std::string & fn, //匹配拓展名称?
  93. std::string en) {
  94. size_t p = fn.rfind(‘.‘);//查找"."字符,若找到则返回“.”在字符串中的位置,找不到则返回npos
  95. std::string ext = p != fn.npos ? fn.substr(p) : fn;//如果字符串fn中存在".“,则截取字符串p
  96. std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);//把ext变成小写
  97. std::transform(en.begin(), en.end(), en.begin(), ::tolower);
  98. if ( ext == en )
  99. return true;
  100. if ( en == "jpg" && ext == "jpeg" )
  101. return true;
  102. return false;
  103. }
  104. bool ReadImageToDatum(const string& filename, const int label,//把图片读到 Datum中
  105. const int height, const int width, const bool is_color,
  106. const std::string & encoding, Datum* datum) {
  107. cv::Mat cv_img = ReadImageToCVMat(filename, height, width, is_color);//先把数据读到cv::Mat类型矩阵中
  108. if (cv_img.data) {//Mat矩阵中数据指针Mat.data是uchar类型指针,矩阵中的元素应该是uchar类型;该语句是判断cv_img中是否有数据
  109. if (encoding.size()) {//是否需要编码
  110. if ( (cv_img.channels() == 3) == is_color && !height && !width &&
  111. matchExt(filename, encoding) )
  112. return ReadFileToDatum(filename, label, datum);
  113. std::vector<uchar> buf;
  114. cv::imencode("."+encoding, cv_img, buf);//感觉这行代码的作用是把cv_img中的值赋值给buf
  115. datum->set_data(std::string(reinterpret_cast<char*>(&buf[0]),
  116. buf.size()));
  117. datum->set_label(label);
  118. datum->set_encoded(true);//感觉是一种编码函数
  119. return true;
  120. }
  121. CVMatToDatum(cv_img, datum);
  122. datum->set_label(label);
  123. return true;
  124. } else {
  125. return false;
  126. }
  127. }
  128. bool ReadFileToDatum(const string& filename, const int label,
  129. Datum* datum) {
  130. std::streampos size;
  131. fstream file(filename.c_str(), ios::in|ios::binary|ios::ate);
  132. if (file.is_open()) {
  133. size = file.tellg();
  134. std::string buffer(size, ‘ ‘);
  135. file.seekg(0, ios::beg);
  136. file.read(&buffer[0], size);
  137. file.close();
  138. datum->set_data(buffer);
  139. datum->set_label(label);
  140. datum->set_encoded(true);
  141. return true;
  142. } else {
  143. return false;
  144. }
  145. }
  146. cv::Mat DecodeDatumToCVMatNative(const Datum& datum) {
  147. cv::Mat cv_img;
  148. CHECK(datum.encoded()) << "Datum not encoded";
  149. const string& data = datum.data();
  150. std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());
  151. cv_img = cv::imdecode(vec_data, -1);
  152. if (!cv_img.data) {
  153. LOG(ERROR) << "Could not decode datum ";
  154. }
  155. return cv_img;
  156. }
  157. cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color) {
  158. cv::Mat cv_img;
  159. CHECK(datum.encoded()) << "Datum not encoded";
  160. const string& data = datum.data();
  161. std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());
  162. int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :
  163. CV_LOAD_IMAGE_GRAYSCALE);
  164. cv_img = cv::imdecode(vec_data, cv_read_flag);
  165. if (!cv_img.data) {
  166. LOG(ERROR) << "Could not decode datum ";
  167. }
  168. return cv_img;
  169. }
  170. // If Datum is encoded will decoded using DecodeDatumToCVMat and CVMatToDatum
  171. // If Datum is not encoded will do nothing
  172. bool DecodeDatumNative(Datum* datum) {
  173. if (datum->encoded()) {
  174. cv::Mat cv_img = DecodeDatumToCVMatNative((*datum));
  175. CVMatToDatum(cv_img, datum);
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. }
  181. bool DecodeDatum(Datum* datum, bool is_color) {
  182. if (datum->encoded()) {
  183. cv::Mat cv_img = DecodeDatumToCVMat((*datum), is_color);
  184. CVMatToDatum(cv_img, datum);
  185. return true;
  186. } else {
  187. return false;
  188. }
  189. }
  190. void CVMatToDatum(const cv::Mat& cv_img, Datum* datum) {
  191. CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";
  192. datum->set_channels(cv_img.channels());
  193. datum->set_height(cv_img.rows);
  194. datum->set_width(cv_img.cols);
  195. datum->clear_data();
  196. datum->clear_float_data();
  197. datum->set_encoded(false);
  198. int datum_channels = datum->channels();
  199. int datum_height = datum->height();
  200. int datum_width = datum->width();
  201. int datum_size = datum_channels * datum_height * datum_width;
  202. std::string buffer(datum_size, ‘ ‘);
  203. for (int h = 0; h < datum_height; ++h) {
  204. const uchar* ptr = cv_img.ptr<uchar>(h);
  205. int img_index = 0;
  206. for (int w = 0; w < datum_width; ++w) {
  207. for (int c = 0; c < datum_channels; ++c) {
  208. int datum_index = (c * datum_height + h) * datum_width + w;
  209. buffer[datum_index] = static_cast<char>(ptr[img_index++]);
  210. }
  211. }
  212. }
  213. datum->set_data(buffer);
  214. }
  215. 。。。。。

五,以上代码注释为个人理解,如有遗漏,错误还望大家多多交流,指正,以便共同学习,进步!!

时间: 2024-10-03 22:40:35

Caffe3——ImageNet数据集创建lmdb类型的数据的相关文章

Caffe2——cifar10数据集创建lmdb或leveldb类型的数据

Caffe2——cifar10数据集创建lmdb或leveldb类型的数据 cifar10数据集和mnist数据集存储方式不同,cifar10数据集把标签和图像数据以bin文件的方式存放在同一个文件内,这种存放方式使得每个子cifar数据bin文件的结构相同,所以cifar转换数据代码比mnist的代码更加的模块化,分为源数据读取模块(image_read函数),把lmdb(leveldb)数据转换的变量声明,句柄(函数)调用都放到定义的caffe::db子空间中,这样简化了代码,而且使得代码更

Caffe1——Mnist数据集创建lmdb或leveldb类型的数据

Caffe1——Mnist数据集创建lmdb或leveldb类型的数据 Leveldb和lmdb简单介绍 Caffe生成的数据分为2种格式:Lmdb和Leveldb.它们都是键/值对(Key/Value Pair)嵌入式数据库管理系统编程库.虽然lmdb的内存消耗是leveldb的1.1倍,但是lmdb的速度比leveldb快10%至15%,更重要的是lmdb允许多种训练模型同时读取同一组数据集. 因此lmdb取代了leveldb成为Caffe默认的数据集生成格式(http://blog.csd

在不清楚数据表字段数据分布的情况下,应该创建什么类型的索引?

在讨论之前,先看看关系型数据库常见的索引类型: 1.位图索引,适用于该字段重复数据很多的情况: 2.B+树索引,适用于该字段重复数据不多的情况. 在不清楚数据表字段数据分布的情况下,应该创建什么类型的索引?个人觉得以上两种都不太适用,可以尝试使用第3类的索引: 3.倒排索引,在搜索引擎使用较多,适用于大多数的情况. 使用普通的文本文件格式存储倒排索引,格式为: value:rowid(行标识码),字段值对应value,rowid对应该行的标识码. 要注意的是,在创建倒排索引时,倒排索引的key需

javaScript-数据类型和数据类型转换

特别声明,以下为达内科技web前端讲师张东张老师的原创笔记,未经允许,不可转于其他商用,仅供学习. 1. 什么是JavaScript:专门编写网页交互的语言 2. 什么变量:内存中存储*一个*数据的存储空间,再起一个名字 声明: 创建一个变量, var 变量名; 赋值: 将等号右边的数据保存到等号左边的变量中.变量名=值; 取值: 在任何位置使用变量名等效于直接使用变量中的值 特殊:1. 简写:var 变量名=值;——建议 ***2. 声明提前:在正式执行程序前,都会预读所有var声明的变量,集

数据库表的创建、管理和数据操作(实验一),数据库创建

数据库表的创建.管理和数据操作(实验一),数据库创建 今天我们就以实验的形式对表的创建.管理和数据操作进行学习,上课吧. [实验目的]:了解SQL语言的使用,进一步理解关系运算,巩固数据库的基础知识.[实验要求]:利用SQL语言进行数据库表的各种操作:1.数据库表的创建.修改和删除操作.2.向表中进行数据的插入.删除和修改操作.[实验内容]1. 利用数据定义语句在实验一创建的stu_DB库中建立学生管理系统的三个表:Student.Course.SC.2.利用INSERT.UPDATE和DELE

NetworkComms V3 使用TCP通信传递IList&lt;T&gt;类型的数据

客户端从服务器获取一组IList<T>类型的数据非常常见(通常从数据库中获取) 我们用NeworkComms V3来演示一下(NetworkcommsV2.x版本也同样支持) [ 使用protobuf.net序列化器] 第一步创建相关的工程文件: MessageContract中的 User类为契约类,使用protobuf.net进行序列化 写法如下: using System; using System.Collections.Generic; using System.Text; usin

svn报错“请求的名称有效 但是找不到请求的类型的数据”的解决方案之一

昨天收到同事的易信,说是svn不能用了. 由于只有他自己反映无法使用,而且,我使用svn也没有问题.因此,这事基本应该和服务没什么关系.看了一下进程,也没什么问题. [[email protected] svndata]# ps -ef|grep svn root      2298     1  0 Feb13 ?        00:00:00 svnserve -d -r /data/svndata/yunqiandai/ root      8556  8489  0 13:44 pts

用jdbc访问二进制类型的数据

1 package it.cast.jdbc; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileOutputStream; 8 import java.io.InputStream; 9 import java.io.OutputSt

hive原生和复合类型的数据加载和使用

原生类型 原生类型包括TINYINT,SMALLINT,INT,BIGINT,BOOLEAN,FLOAT,DOUBLE,STRING,BINARY (Hive 0.8.0以上才可用),TIMESTAMP (Hive 0.8.0以上才可用),这些数据加载很容易,只要设置好列分隔符,按照列分隔符输出到文件就可以了. 假设有这么一张用户登陆表 CREATE TABLE login ( uid BIGINT, ip STRING ) ROW FORMAT DELIMITED FIELDS TERMINA