梳理caffe代码io(十三)
io包含了创建临时文件临时目录操作,以及从txt文件以及bin文件读取proto数据或者写入proto的数据到txt或者bin文件。io其实就是提供如何读取如何写入的一些读取图像或者文件,以及它们之间的一些转化的函数。
hpp文件:
- #ifndef CAFFE_UTIL_IO_H_
- #define CAFFE_UTIL_IO_H_
- #include <unistd.h>
- #include <string>
- #include "google/protobuf/message.h"
- #include "caffe/blob.hpp"
- #include "caffe/common.hpp"
- #include "caffe/proto/caffe.pb.h"
- namespace caffe {
- using ::google::protobuf::Message;
- // 内联函数,创建临时文件
- inline void MakeTempFilename(string* temp_filename) {
- temp_filename->clear();
- *temp_filename = "/tmp/caffe_test.XXXXXX";
- char* temp_filename_cstr = new char[temp_filename->size() + 1];
- // NOLINT_NEXT_LINE(runtime/printf)
- strcpy(temp_filename_cstr, temp_filename->c_str());
- int fd = mkstemp(temp_filename_cstr);
- CHECK_GE(fd, 0) << "Failed to open a temporary file at: " << *temp_filename;
- close(fd);
- *temp_filename = temp_filename_cstr;
- delete[] temp_filename_cstr;
- }
- // 内联函数,创建临时目录
- inline void MakeTempDir(string* temp_dirname) {
- temp_dirname->clear();
- *temp_dirname = "/tmp/caffe_test.XXXXXX";
- char* temp_dirname_cstr = new char[temp_dirname->size() + 1];
- // NOLINT_NEXT_LINE(runtime/printf)
- strcpy(temp_dirname_cstr, temp_dirname->c_str());
- char* mkdtemp_result = mkdtemp(temp_dirname_cstr);
- CHECK(mkdtemp_result != NULL)
- << "Failed to create a temporary directory at: " << *temp_dirname;
- *temp_dirname = temp_dirname_cstr;
- delete[] temp_dirname_cstr;
- }
- // 从txt读取proto的定义
- bool ReadProtoFromTextFile(const char* filename, Message* proto);
- // 从text读取proto的定义
- inline bool ReadProtoFromTextFile(const string& filename, Message* proto) {
- return ReadProtoFromTextFile(filename.c_str(), proto);
- }
- // 从text读取proto的定义,只是增加了检查而已
- inline void ReadProtoFromTextFileOrDie(const char* filename, Message* proto) {
- CHECK(ReadProtoFromTextFile(filename, proto));
- }
- // 从text读取proto的定义,只是增加了检查而已
- inline void ReadProtoFromTextFileOrDie(const string& filename, Message* proto) {
- ReadProtoFromTextFileOrDie(filename.c_str(), proto);
- }
- // 将proto写入到txt文件
- void WriteProtoToTextFile(const Message& proto, const char* filename);
- inline void WriteProtoToTextFile(const Message& proto, const string& filename) {
- WriteProtoToTextFile(proto, filename.c_str());
- }
- // 从bin读取proto的定义
- bool ReadProtoFromBinaryFile(const char* filename, Message* proto);
- // 从bin读取proto的定义
- inline bool ReadProtoFromBinaryFile(const string& filename, Message* proto) {
- return ReadProtoFromBinaryFile(filename.c_str(), proto);
- }
- // 从bin读取proto的定义,只是增加了检查而已
- inline void ReadProtoFromBinaryFileOrDie(const char* filename, Message* proto) {
- CHECK(ReadProtoFromBinaryFile(filename, proto));
- }
- // 从bin读取proto的定义,只是增加了检查而已
- inline void ReadProtoFromBinaryFileOrDie(const string& filename,
- Message* proto) {
- ReadProtoFromBinaryFileOrDie(filename.c_str(), proto);
- }
- // 将proto写入到bin文件
- void WriteProtoToBinaryFile(const Message& proto, const char* filename);
- // 内联函数,将proto写入到bin文件
- inline void WriteProtoToBinaryFile(
- const Message& proto, const string& filename) {
- WriteProtoToBinaryFile(proto, filename.c_str());
- }
- // 从文件读取数据到Datum
- bool ReadFileToDatum(const string& filename, const int label, Datum* datum);
- // 内联函数,从文件读取数据到Datum
- inline bool ReadFileToDatum(const string& filename, Datum* datum) {
- return ReadFileToDatum(filename, -1, datum);
- }
- // 从图像文件读取数据到Datum
- bool ReadImageToDatum(const string& filename, const int label,
- const int height, const int width, const bool is_color,
- const std::string & encoding, Datum* datum);
- // 内联函数,从图像文件(彩色还是黑白?)读取数据到Datum,指定图像大小
- inline bool ReadImageToDatum(const string& filename, const int label,
- const int height, const int width, const bool is_color, Datum* datum) {
- return ReadImageToDatum(filename, label, height, width, is_color,
- "", datum);
- }
- // 内联函数,从彩色图像文件读取数据到Datum,指定图像大小
- inline bool ReadImageToDatum(const string& filename, const int label,
- const int height, const int width, Datum* datum) {
- return ReadImageToDatum(filename, label, height, width, true, datum);
- }
- // 内联函数,从图像文件(彩色还是黑白?)读取数据到Datum,自动获取图像大小
- inline bool ReadImageToDatum(const string& filename, const int label,
- const bool is_color, Datum* datum) {
- return ReadImageToDatum(filename, label, 0, 0, is_color, datum);
- }
- // 内联函数,从彩色图像文件读取数据到Datum,自动获取图像大小
- inline bool ReadImageToDatum(const string& filename, const int label,
- Datum* datum) {
- return ReadImageToDatum(filename, label, 0, 0, true, datum);
- }
- // 内联函数,从彩色图像文件读取数据到Datum,自动获取图像大小,指定编码格式
- inline bool ReadImageToDatum(const string& filename, const int label,
- const std::string & encoding, Datum* datum) {
- return ReadImageToDatum(filename, label, 0, 0, true, encoding, datum);
- }
- // 对Datum进行解码
- bool DecodeDatumNative(Datum* datum);
- // 对彩色图像的Datum进行解码
- bool DecodeDatum(Datum* datum, bool is_color);
- #ifdef USE_OPENCV
- // 将图像读取到CVMat,指定图像大小,是否彩色
- cv::Mat ReadImageToCVMat(const string& filename,
- const int height, const int width, const bool is_color);
- // 将图像读取到CVMat,指定图像大小
- cv::Mat ReadImageToCVMat(const string& filename,
- const int height, const int width);
- // 将图像读取到CVMat,指定是否彩色
- cv::Mat ReadImageToCVMat(const string& filename,
- const bool is_color);
- // 将图像读取到CVMat
- cv::Mat ReadImageToCVMat(const string& filename);
- // 将Datum解码为为CVMat
- cv::Mat DecodeDatumToCVMatNative(const Datum& datum);
- // 将彩色图像的Datum解码为为CVMat
- cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color);
- // 将CVMat转换为Datum
- void CVMatToDatum(const cv::Mat& cv_img, Datum* datum);
- #endif // USE_OPENCV
- } // namespace caffe
- #endif // CAFFE_UTIL_IO_H_
实现:
- #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>
- #ifdef USE_OPENCV
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/highgui/highgui_c.h>
- #include <opencv2/imgproc/imgproc.hpp>
- #endif // USE_OPENCV
- #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; // Max size of 2 GB minus 1 byte.
- 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;
- // 从文件读取Proto的txt文件
- 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);//新建一个FileInputStream对象 input
- // 注意如何使用protobuf去读取,解析input文件中的Message, 即使文件中参数定义顺序与Message中的参数定义顺序不一致,也可以解析。
- bool success = google::protobuf::TextFormat::Parse(input, proto);
- delete input;
- close(fd);
- return success;
- }
- // 将proto写入到txt文件
- 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);// 和ReadProtoFromTextFile功能相反
- }
- // 从bin读取proto的定义
- 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);
- // 解码流com.google.protobuf.CodedInputStream
- CodedInputStream* coded_input = new CodedInputStream(raw_input);
- // 建立CodedInputStream类的对象coded_input
- coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);
- //折设置最大字节限制
- bool success = proto->ParseFromCodedStream(coded_input);
- delete coded_input;
- delete raw_input;
- close(fd);
- return success;
- }
- // 将proto写入到bin文件
- void WriteProtoToBinaryFile(const Message& proto, const char* filename) {
- fstream output(filename, ios::out | ios::trunc | ios::binary);
- CHECK(proto.SerializeToOstream(&output));
- }
- #ifdef USE_OPENCV
- // 将图像读取到CVMat,指定图像大小,是否彩色
- cv::Mat ReadImageToCVMat(const string& filename,
- const int height, const int width, const bool is_color) {
- //height,width都不为0则把图像resize 到height*width
- 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,//is_color 为1读入彩色图像,0灰度图
- 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);
- }//重载函数,读入图像但不resize
- cv::Mat ReadImageToCVMat(const string& filename) {
- return ReadImageToCVMat(filename, 0, 0, true);
- }//重载函数,读入彩色图像且不resize
- // Do the file extension and encoding match?
- // 看看是不是jpg还是jpeg的图像
- static bool matchExt(const std::string & fn,
- std::string en) {
- size_t p = fn.rfind('.');//p 为文件名中“.”所在位置的索引
- std::string ext = p != fn.npos ? fn.substr(p) : fn;//ext为文件后缀名".xxx"
- std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
- //把ext中的大写字母转化小写字母
- std::transform(en.begin(), en.end(), en.begin(), ::tolower);
- if ( ext == en )
- return true;
- if ( en == "jpg" && ext == "jpeg" )
- return true;
- return false;
- }
- // 从图像文件读取数据到Datum
- 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);//cvmat转为Datum格式
- datum->set_label(label);
- return true;
- } else {
- return false;
- }
- }
- #endif // USE_OPENCV
- // 从文件读取数据到Datum
- bool ReadFileToDatum(const string& filename, const int label,
- Datum* datum) {
- std::streampos size;//获取文件指针位置 size
- fstream file(filename.c_str(), ios::in|ios::binary|ios::ate);
- if (file.is_open()) {
- size = file.tellg();//代表当前get 流指针的位置
- std::string buffer(size, ' ');
- file.seekg(0, ios::beg);//设置0输入文件流的起始位置
- file.read(&buffer[0], size);
- file.close();
- datum->set_data(buffer);//data_->assign(buffer)
- datum->set_label(label);//label_ = label
- datum->set_encoded(true);
- return true;
- } else {
- return false;
- }
- }
- #ifdef USE_OPENCV
- // 直接编码数据的Datum到CVMat
- 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);//flag=-1
- if (!cv_img.data) {
- LOG(ERROR) << "Could not decode datum ";
- }
- return cv_img;
- }
- // 直接编码彩色或者非彩色Datum到CVMat
- 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);// flag为用户指定的,从内存都入图片
- if (!cv_img.data) {
- LOG(ERROR) << "Could not decode datum ";
- }
- return cv_img;//将encode 的Datum转化为cvMat
- }
- // If Datum is encoded will decoded using DecodeDatumToCVMat and CVMatToDatum
- // If Datum is not encoded will do nothing
- bool DecodeDatumNative(Datum* datum) {
- if (datum->encoded()) {
- cv::Mat cv_img = DecodeDatumToCVMatNative((*datum));
- CVMatToDatum(cv_img, datum);
- return true;
- } else {
- return false;
- }
- }
- // 将Datum进行解码
- 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;
- }
- }//将encodedDatum转化为没有encode的Datum
- // 将CVMat转换到Datum
- 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);//分别设置channel, height,width
- 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, ' ');//将buffer初始化为字符''的datum_size个副本
- for (int h = 0; h < datum_height; ++h) {
- const uchar* ptr = cv_img.ptr<uchar>(h);//指向图像第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);
- }
- #endif // USE_OPENCV
- } // namespace caffe