标签:
Caffe3——ImageNet数据集创建lmdb类型的数据
ImageNet数据集和cifar,mnist数据集最大的不同,就是数据量特别大;单张图片尺寸大,训练样本个数多;面对如此大的数据集,在转换成lmdb文件时;使用了很多新的类型对象。
1,动态扩容的数组“vector”,动态地添加新元素
2,pair类型数据对,用于存储成对的对象,例如存储文件名和对应标签
3,利用opencv中的图像处理函数,来读取和处理大尺寸图像
 
一:程序开始
 
由于要向imageNet数据集中设置resize和是否乱序等参数,所以本文使用gflags命令行解析工具;在Create.sh文件中,调用convert_imageset.bin语句为:
 
- <pre name="code" class="cpp">GLOG_logtostderr=1$TOOLS/convert_imageset \  
-     --resize_height=$RESIZE_HEIGHT \  
-     --resize_width=$RESIZE_WIDTH \  
-     --shuffle \  
-     $TRAIN_DATA_ROOT \  图像数据集存放的根目录  
- $DATA/train.txt \       图像的ID和对应的分类标签数字  
- $EXAMPLE/ilsvrc12_train_lmdb  lmdb文件保存的路径  
 
 
由于train.txt文件太大,电脑打不开,故打开val.txt一窥之;val.txt中的某个数据为:
 
65ILSVRC2012_val_00000002.JPEG ,65应该是对应的标签,后面的是图像的编号id。
二:数据转换流程图

 
三:convert_imageset.cpp函数分析
 
1引入必要的头文件和命名空间
 
- #include<algorithm>//输出数组的内容、对数组进行升幂排序、反转数组内容、复制数组内容等操作,  
- #include <fstream>  // NOLINT(readability/streams)  
- #include <string>  
- #include<utility>//utility头文件定义了一个pair类型,pair类型用于存储一对数据  
- #include<vector>//会自动扩展容量的数组  
- #include "boost/scoped_ptr.hpp"//智能指针头文件  
- #include "gflags/gflags.h"  
- #include "glog/logging.h"  
- #include"caffe/proto/caffe.pb.h"  
- #include "caffe/util/db.hpp" //引入包装好的lmdb操作函数  
- #include "caffe/util/io.hpp" //引入opencv中的图像操作函数  
- #include "caffe/util/rng.hpp"  
 
头文件和convert_cifar_data.cpp的区别:
 
1,引入gflags命令行解析工具;
2,引入utility头文件,里面提供了数组洗牌等操作
 
- using namespace caffe;  
- using std::pair;  
- using boost::scoped_ptr;  
 
命名空间区别:
 
1,引入全部caffe命名空间
2,引入pair对命名空间
2 gflags宏定义参数
//通过gflags宏定义一些程序的参数变量
 
- DEFINE_bool(gray, false,"When thisoption is on, treat images as grayscale ones");
- DEFINE_bool(shuffle, false,"Randomlyshuffle the order of images and their labels");
- DEFINE_string(backend, "lmdb","The backend {lmdb, leveldb} for storing the result");
- DEFINE_int32(resize_width, 0, "Width images areresized to");
- DEFINE_int32(resize_height, 0, "Height imagesare resized to");  
- DEFINE_bool(check_size, false,"When this optionis on, check that all the datum have the samesize");  
- DEFINE_bool(encoded, false,"When this option ison, the encoded image will be save in datum");
- 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宏定义接收命令行中传入的参数
 
- const boolis_color = !FLAGS_gray;  
- const boolcheck_size = FLAGS_check_size; 
- const boolencoded = FLAGS_encoded;
- 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中存放图片的名字和对应的标签,不存储真正的图片数据
 
- while (infile>> filename >> label) {  
- nes.push_back(std::make_pair(filename, label));  
 
//make_pair是pair模板中定义的给pair对象赋值的函数,push_back()函数是vector对象的一个成员函数,用来在末端添加新元素}
 
3.3判断是否进行洗牌操作
 
- if(FLAGS_shuffle) {  
-   
-   LOG(INFO)<< "Shuffling data";  
 
- <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">
 
 
 shuffle(lines.begin(), lines.end());//vector.begin() - 回传一个Iterator迭代器,它指向 vector 第一个元素。}
3.4以智能指针的方式创建db::DB类型的对象 db
 
- scoped_ptr<db::DB>db(db::GetDB(FLAGS_backend));  
- db->Open(argv[3], db::NEW);
- scoped_ptr<db::Transaction>txn(db->NewTransaction());
 
 
3.5 源数据中提取图像数据
3.5.1 通过ReadImageToDatum函数把图像数据读取到datum中
//到源数据位置读取每张图片的数据。(../imagenet/xxx.jpeg,65,256,256,true,jpeg,&datum)
 
- status= ReadImageToDatum(root_folder + lines[line_id].first,lines[line_id].second, resize_height,resize_width, is_color,enc, &datum); 
 
3.5.2  ReadImageToDatum函数说明
 
ReadImageToDatum函数为io.cpp文件中定义的函数;io.cpp主要实现了3部分功能:
1,从text文件或者二进制文件中读写proto文件;
2,利用opencv的Mat矩阵,把图像数据读到Mat矩阵中;
3,把Mat矩阵中的值放入到datum中
3.5.3 检查数据尺寸
 
- if (check_size) {
-       if (!data_size_initialized) {
-         data_size = datum.channels() *datum.height() * datum.width();  
-         data_size_initialized = true;  
-       } else {  
-         const std::string& data =datum.data();  
-         CHECK_EQ(data.size(), data_size)<< "Incorrect data field size "<< data.size();  
-       }  
 
 
3.6   序列化键和值并放入临时数据库
 
- intlength = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id,lines[line_id].first.c_str());
-     
-  string out;  
-     CHECK(datum.SerializeToString(&out));
-     txn->Put(string(key_cstr, length), out);
 
3.7 批量提交到lmdb文件
 
 
- if (++count % 1000 == 0) {  
-       
-       txn->Commit();
-       txn.reset(db->NewTransaction());
-       LOG(ERROR) << "Processed" << count << " files.";  
-     }  
 
 
四,相关文件
4.1 Convert_imageset.cpp文件
 
 
-   
- #include <algorithm>//输出数组的内容、对数组进行升幂排序、反转数组内容、复制数组内容等操作,  
- #include <fstream>  // NOLINT(readability/streams)  
- #include <string>  
- #include <utility>//utility头文件定义了一个pair类型  
- #include <vector>//会自动扩展容量的数组  
-   
- #include "boost/scoped_ptr.hpp"  
- #include "gflags/gflags.h"  
- #include "glog/logging.h"  
-   
- #include "caffe/proto/caffe.pb.h"  
- #include "caffe/util/db.hpp"  
- #include "caffe/util/io.hpp"  
- #include "caffe/util/rng.hpp"  
-   
- using namespace caffe;  
- using std::pair;  
- using boost::scoped_ptr;  
-   
-   
- DEFINE_bool(gray, false,  
-     "When this option is on, treat images as grayscale ones");  
- DEFINE_bool(shuffle, false,  
-     "Randomly shuffle the order of images and their labels");
- DEFINE_string(backend, "lmdb",  
-         "The backend {lmdb, leveldb} for storing the result");  
- DEFINE_int32(resize_width, 0, "Width images are resized to");  
- DEFINE_int32(resize_height, 0, "Height images are resized to");  
- DEFINE_bool(check_size, false,  
-     "When this option is on, check that all the datum have the same size");  
- DEFINE_bool(encoded, false,  
-     "When this option is on, the encoded image will be save in datum");
- DEFINE_string(encode_type, "",  
-     "Optional: What type should we encode the image as (‘png‘,‘jpg‘,...).");
-   
- int main(int argc, char** argv) {  
-   ::google::InitGoogleLogging(argv[0]);  
-   
- #ifndef GFLAGS_GFLAGS_H_  
-   namespace gflags = google;  
- #endif  
-   
-   gflags::SetUsageMessage("Convert a set of images to the leveldb/lmdb\n"  
-         "format used as input for Caffe.\n"  
-         "Usage:\n"  
-         "    convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME\n"  
-         "The ImageNet dataset for the training demo is at\n"  
-         "    http://www.image-net.org/download-images\n");  
-   gflags::ParseCommandLineFlags(&argc, &argv, true);  
-   
-   if (argc < 4) {  
-     gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/convert_imageset");  
-     return 1;  
-   }  
-   
-   const bool is_color = !FLAGS_gray;  
-   const bool check_size = FLAGS_check_size; 
-   const bool encoded = FLAGS_encoded;
-   const string encode_type = FLAGS_encode_type;
-   
-   std::ifstream infile(argv[2]);
-   std::vector<std::pair<std::string, int> > lines;
-   std::string filename;  
-   int label;  
-   
-   while (infile >> filename >> label) {  
-     lines.push_back(std::make_pair(filename, label));
-   }  
-   if (FLAGS_shuffle) {  
-     
-     LOG(INFO) << "Shuffling data";  
-     
-     shuffle(lines.begin(), lines.end());
-   }  
-   LOG(INFO) << "A total of " << lines.size() << " images.";  
-   
-   if (encode_type.size() && !encoded)  
-     LOG(INFO) << "encode_type specified, assuming encoded=true.";  
-   
-   int resize_height = std::max<int>(0, FLAGS_resize_height);  
-   int resize_width = std::max<int>(0, FLAGS_resize_width);  
-   
-   
-   scoped_ptr<db::DB> db(db::GetDB(FLAGS_backend));  
-   db->Open(argv[3], db::NEW);
-   scoped_ptr<db::Transaction> txn(db->NewTransaction());
-   
-   
-   std::string root_folder(argv[1]);
-   Datum datum;
-   int count = 0;  
-   const int kMaxKeyLength = 256;  
-   char key_cstr[kMaxKeyLength];  
-   int data_size = 0;  
-   bool data_size_initialized = false;  
-   
-   for (int line_id = 0; line_id < lines.size(); ++line_id) {  
-     bool status;  
-     std::string enc = encode_type; 
-     if (encoded && !enc.size()) {  
-       
-       string fn = lines[line_id].first;
-       size_t p = fn.rfind(‘.‘);
-       if ( p == fn.npos )  
-         LOG(WARNING) << "Failed to guess the encoding of ‘" << fn << "‘";  
-       enc = fn.substr(p);
-       std::transform(enc.begin(), enc.end(), enc.begin(), ::tolower);
-     }  
-     
-     status = ReadImageToDatum(root_folder + lines[line_id].first,  
-         lines[line_id].second, resize_height, resize_width, is_color,enc, &datum);  
-     if (status == false) continue;
-     if (check_size) {
-       if (!data_size_initialized) {
-         data_size = datum.channels() * datum.height() * datum.width();  
-         data_size_initialized = true;  
-       } else {  
-         const std::string& data = datum.data();  
-         CHECK_EQ(data.size(), data_size) << "Incorrect data field size "  
-             << data.size();  
-       }  
-     }  
-     
-     int length = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id,   
-         lines[line_id].first.c_str());
-   
-     
-     string out;  
-     CHECK(datum.SerializeToString(&out));
-     txn->Put(string(key_cstr, length), out);
-   
-     if (++count % 1000 == 0) {  
-       
-       txn->Commit();
-       txn.reset(db->NewTransaction());
-       LOG(ERROR) << "Processed " << count << " files.";  
-     }  
-   }  
-   
-   if (count % 1000 != 0) {  
-     txn->Commit();  
-     LOG(ERROR) << "Processed " << count << " files.";  
-   }  
-   return 0;  
- }  
 
4.2 io.cpp文件
 
 
- #include <fcntl.h>  
- #include <google/protobuf/io/coded_stream.h>  
- #include <google/protobuf/io/zero_copy_stream_impl.h>  
- #include <google/protobuf/text_format.h>  
- #include <opencv2/core/core.hpp>  
- #include <opencv2/highgui/highgui.hpp>  
- #include <opencv2/highgui/highgui_c.h>  
- #include <opencv2/imgproc/imgproc.hpp>  
- #include <stdint.h>  
-   
- #include <algorithm>  
- #include <fstream>  // NOLINT(readability/streams)  
- #include <string>  
- #include <vector>  
-   
- #include "caffe/common.hpp"  
- #include "caffe/proto/caffe.pb.h"  
- #include "caffe/util/io.hpp"  
-   
- const int kProtoReadBytesLimit = INT_MAX;  
-   
- namespace caffe {  
-   
- using google::protobuf::io::FileInputStream;
- using google::protobuf::io::FileOutputStream;
- using google::protobuf::io::ZeroCopyInputStream;
- using google::protobuf::io::CodedInputStream;  
- using google::protobuf::io::ZeroCopyOutputStream;  
- using google::protobuf::io::CodedOutputStream;  
- using google::protobuf::Message;  
-   
- bool ReadProtoFromTextFile(const char* filename, Message* proto) {
-   int fd = open(filename, O_RDONLY);  
-   CHECK_NE(fd, -1) << "File not found: " << filename;  
-   FileInputStream* input = new FileInputStream(fd);  
-   bool success = google::protobuf::TextFormat::Parse(input, proto);  
-   delete input;  
-   close(fd);  
-   return success;  
- }  
-   
- void WriteProtoToTextFile(const Message& proto, const char* filename) {
-   int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);  
-   FileOutputStream* output = new FileOutputStream(fd);  
-   CHECK(google::protobuf::TextFormat::Print(proto, output));  
-   delete output;  
-   close(fd);  
- }  
-   
- bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {
-   int fd = open(filename, O_RDONLY);  
-   CHECK_NE(fd, -1) << "File not found: " << filename;  
-   ZeroCopyInputStream* raw_input = new FileInputStream(fd);  
-   CodedInputStream* coded_input = new CodedInputStream(raw_input);  
-   coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);  
-   
-   bool success = proto->ParseFromCodedStream(coded_input);  
-   
-   delete coded_input;  
-   delete raw_input;  
-   close(fd);  
-   return success;  
- }  
-   
- void WriteProtoToBinaryFile(const Message& proto, const char* filename) {
-   fstream output(filename, ios::out | ios::trunc | ios::binary);  
-   CHECK(proto.SerializeToOstream(&output));  
- }  
-   
-   
- cv::Mat ReadImageToCVMat(const string& filename,  
-     const int height, const int width, const bool is_color) {
-   cv::Mat cv_img;  
-   int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :  
-     CV_LOAD_IMAGE_GRAYSCALE);  
-   cv::Mat cv_img_origin = cv::imread(filename, cv_read_flag);
-   if (!cv_img_origin.data) {  
-     LOG(ERROR) << "Could not open or find file " << filename;  
-     return cv_img_origin;  
-   }  
-   if (height > 0 && width > 0) {  
-     cv::resize(cv_img_origin, cv_img, cv::Size(width, height));  
-   } else {  
-     cv_img = cv_img_origin;  
-   }  
-   return cv_img;  
- }  
-   
- cv::Mat ReadImageToCVMat(const string& filename,
-     const int height, const int width) {  
-   return ReadImageToCVMat(filename, height, width, true);  
- }  
-   
- cv::Mat ReadImageToCVMat(const string& filename,
-     const bool is_color) {  
-   return ReadImageToCVMat(filename, 0, 0, is_color);  
- }  
-   
- cv::Mat ReadImageToCVMat(const string& filename) {
-   return ReadImageToCVMat(filename, 0, 0, true);  
- }  
- static bool matchExt(const std::string & fn, 
-                      std::string en) {  
-   size_t p = fn.rfind(‘.‘);
-   std::string ext = p != fn.npos ? fn.substr(p) : fn;
-   std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
-   std::transform(en.begin(), en.end(), en.begin(), ::tolower);  
-   if ( ext == en )  
-     return true;  
-   if ( en == "jpg" && ext == "jpeg" )  
-     return true;  
-   return false;  
- }  
-   
- bool ReadImageToDatum(const string& filename, const int label,
-     const int height, const int width, const bool is_color,  
-     const std::string & encoding, Datum* datum) {  
-   cv::Mat cv_img = ReadImageToCVMat(filename, height, width, is_color);
-   if (cv_img.data) {
-     if (encoding.size()) {
-       if ( (cv_img.channels() == 3) == is_color && !height && !width &&  
-           matchExt(filename, encoding) )  
-         return ReadFileToDatum(filename, label, datum);  
-   
-       std::vector<uchar> buf;  
-       cv::imencode("."+encoding, cv_img, buf);
-       datum->set_data(std::string(reinterpret_cast<char*>(&buf[0]),  
-                       buf.size()));  
-       datum->set_label(label);  
-       datum->set_encoded(true);
-       return true;  
-     }  
-     CVMatToDatum(cv_img, datum);  
-     datum->set_label(label);  
-     return true;  
-   } else {  
-     return false;  
-   }  
- }  
-   
- bool ReadFileToDatum(const string& filename, const int label,  
-     Datum* datum) {  
-   std::streampos size;  
-   
-   fstream file(filename.c_str(), ios::in|ios::binary|ios::ate);  
-   if (file.is_open()) {  
-     size = file.tellg();  
-     std::string buffer(size, ‘ ‘);  
-     file.seekg(0, ios::beg);  
-     file.read(&buffer[0], size);  
-     file.close();  
-     datum->set_data(buffer);  
-     datum->set_label(label);  
-     datum->set_encoded(true);  
-     return true;  
-   } else {  
-     return false;  
-   }  
- }  
-   
- cv::Mat DecodeDatumToCVMatNative(const Datum& datum) {  
-   cv::Mat cv_img;  
-   CHECK(datum.encoded()) << "Datum not encoded";  
-   const string& data = datum.data();  
-   std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());  
-   cv_img = cv::imdecode(vec_data, -1);  
-   if (!cv_img.data) {  
-     LOG(ERROR) << "Could not decode datum ";  
-   }  
-   return cv_img;  
- }  
- cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color) {  
-   cv::Mat cv_img;  
-   CHECK(datum.encoded()) << "Datum not encoded";  
-   const string& data = datum.data();  
-   std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());  
-   int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR :  
-     CV_LOAD_IMAGE_GRAYSCALE);  
-   cv_img = cv::imdecode(vec_data, cv_read_flag);  
-   if (!cv_img.data) {  
-     LOG(ERROR) << "Could not decode datum ";  
-   }  
-   return cv_img;  
- }  
-   
- bool DecodeDatumNative(Datum* datum) {  
-   if (datum->encoded()) {  
-     cv::Mat cv_img = DecodeDatumToCVMatNative((*datum));  
-     CVMatToDatum(cv_img, datum);  
-     return true;  
-   } else {  
-     return false;  
-   }  
- }  
- bool DecodeDatum(Datum* datum, bool is_color) {  
-   if (datum->encoded()) {  
-     cv::Mat cv_img = DecodeDatumToCVMat((*datum), is_color);  
-     CVMatToDatum(cv_img, datum);  
-     return true;  
-   } else {  
-     return false;  
-   }  
- }  
-   
- void CVMatToDatum(const cv::Mat& cv_img, Datum* datum) {  
-   CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";  
-   datum->set_channels(cv_img.channels());  
-   datum->set_height(cv_img.rows);  
-   datum->set_width(cv_img.cols);  
-   datum->clear_data();  
-   datum->clear_float_data();  
-   datum->set_encoded(false);  
-   int datum_channels = datum->channels();  
-   int datum_height = datum->height();  
-   int datum_width = datum->width();  
-   int datum_size = datum_channels * datum_height * datum_width;  
-   std::string buffer(datum_size, ‘ ‘);  
-   for (int h = 0; h < datum_height; ++h) {  
-     const uchar* ptr = cv_img.ptr<uchar>(h);  
-     int img_index = 0;  
-     for (int w = 0; w < datum_width; ++w) {  
-       for (int c = 0; c < datum_channels; ++c) {  
-         int datum_index = (c * datum_height + h) * datum_width + w;  
-         buffer[datum_index] = static_cast<char>(ptr[img_index++]);  
-       }  
-     }  
-   }  
-   datum->set_data(buffer);  
- }  
- 。。。。。  
 
五,以上代码注释为个人理解,如有遗漏,错误还望大家多多交流,指正,以便共同学习,进步!!
Caffe3——ImageNet数据集创建lmdb类型的数据
标签:
原文地址:http://www.cnblogs.com/yymn/p/4479220.html