diff --git a/modules/core/include/opencv2/core/persistence.hpp b/modules/core/include/opencv2/core/persistence.hpp index 6ceeca438..65a1ff4c4 100644 --- a/modules/core/include/opencv2/core/persistence.hpp +++ b/modules/core/include/opencv2/core/persistence.hpp @@ -1241,7 +1241,11 @@ inline String::String(const FileNode& fn): cstr_(0), len_(0) { read(fn, *this, * //! @endcond -CV_EXPORTS void cvWriteRawData_Base64(FileStorage & fs, const void* _data, int len, const char* dt); +CV_EXPORTS void cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt); + +CV_EXPORTS void cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len); + +CV_EXPORTS void cvEndWriteRawData_Base64(::CvFileStorage * fs); CV_EXPORTS void cvWriteMat_Base64(::CvFileStorage* fs, const char* name, const ::CvMat* mat); diff --git a/modules/core/src/persistence.cpp b/modules/core/src/persistence.cpp index fade7a71a..2fc4f9f5b 100644 --- a/modules/core/src/persistence.cpp +++ b/modules/core/src/persistence.cpp @@ -183,6 +183,8 @@ typedef struct CvXMLStackRecord } CvXMLStackRecord; +namespace base64 { class Base64Writer; } + #define CV_XML_OPENING_TAG 1 #define CV_XML_CLOSING_TAG 2 #define CV_XML_EMPTY_TAG 3 @@ -240,6 +242,8 @@ typedef struct CvFileStorage size_t strbufsize, strbufpos; std::deque* outbuf; + base64::Base64Writer * base64_writer; + bool is_opened; } CvFileStorage; @@ -319,6 +323,10 @@ namespace base64 /* sample */ + void cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt); + void cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len); + void cvEndWriteRawData_Base64(::CvFileStorage * fs); + void cvWriteRawData_Base64(::cv::FileStorage & fs, const void* _data, int len, const char* dt); void cvWriteMat_Base64(CvFileStorage * fs, const char * name, ::cv::Mat const & mat); } @@ -6414,14 +6422,14 @@ public: , binary_buffer(BUFFER_LEN) , base64_buffer(base64_encode_buffer_size(BUFFER_LEN)) , src_beg(0) - , src_end(0) , src_cur(0) + , src_end(0) { src_beg = binary_buffer.data(); src_end = src_beg + BUFFER_LEN; src_cur = src_beg; - // TODO: check if fs.state is valid. + CV_CHECK_OUTPUT_FILE_STORAGE(fs); ::icvFSFlush(file_storage); } @@ -6515,8 +6523,8 @@ private: std::vector binary_buffer; std::vector base64_buffer; uchar * src_beg; - uchar * src_end; uchar * src_cur; + uchar * src_end; }; class base64::MatToBinaryConvertor @@ -6927,9 +6935,72 @@ private: * Wapper ***************************************************************************/ -void base64::make_seq(void * binary, int elem_cnt, const char * dt, CvSeq & seq) +class base64::Base64Writer { - CvFileNode node; +public: + + Base64Writer(::CvFileStorage * fs, const char * name, int len, const char* dt) + : file_storage(fs) + , emitter(fs) + , remaining_data_length(len) + , data_type_string(dt) + { + CV_CHECK_OUTPUT_FILE_STORAGE(fs); + + cvStartWriteStruct(fs, name, CV_NODE_SEQ, "binary"); + icvFSFlush(fs); + + /* output header */ + + /* total byte size(before encode) */ + int size = len * ::icvCalcStructSize(dt, 0); + + std::string buffer = make_base64_header(size, dt); + const uchar * beg = reinterpret_cast(buffer.data()); + const uchar * end = beg + buffer.size(); + + emitter.write(beg, end); + } + + void write(const void* _data, int len) + { + CV_Assert(len >= 0); + CV_Assert(remaining_data_length >= static_cast(len)); + remaining_data_length -= static_cast(len); + + RawDataToBinaryConvertor convertor(_data, len, data_type_string); + emitter.write(convertor); + } + + template inline + void write(_to_binary_convertor_t & convertor, int data_length_of_convertor) + { + CV_Assert(data_length_of_convertor >= 0); + CV_Assert(remaining_data_length >= static_cast(data_length_of_convertor)); + remaining_data_length -= static_cast(data_length_of_convertor); + + emitter.write(convertor); + } + + ~Base64Writer() + { + CV_Assert(remaining_data_length == 0U); + emitter.flush(); + cvEndWriteStruct(file_storage); + icvFSFlush(file_storage); + } + +private: + + ::CvFileStorage * file_storage; + Base64ContextEmitter emitter; + size_t remaining_data_length; + const char* data_type_string; +}; + +void base64::make_seq(void * binary, int elem_cnt, const char * dt, ::CvSeq & seq) +{ + ::CvFileNode node; node.info = 0; BinaryToCvSeqConvertor convertor(binary, elem_cnt, dt); while (convertor) { @@ -6938,7 +7009,32 @@ void base64::make_seq(void * binary, int elem_cnt, const char * dt, CvSeq & seq) } } -void base64::cvWriteRawData_Base64(cv::FileStorage & fs, const void* _data, int len, const char* dt) +void base64::cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt) +{ + CV_Assert(fs); + CV_CHECK_OUTPUT_FILE_STORAGE(fs); + CV_Assert(fs->base64_writer == 0); + fs->base64_writer = new Base64Writer(fs, name, len, dt); +} + +void base64::cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len) +{ + CV_Assert(fs); + CV_CHECK_OUTPUT_FILE_STORAGE(fs); + CV_Assert(fs->base64_writer != 0); + fs->base64_writer->write(_data, len); +} + +void base64::cvEndWriteRawData_Base64(::CvFileStorage * fs) +{ + CV_Assert(fs); + CV_CHECK_OUTPUT_FILE_STORAGE(fs); + CV_Assert(fs->base64_writer != 0); + delete fs->base64_writer; + fs->base64_writer = 0; +} + +void base64::cvWriteRawData_Base64(::cv::FileStorage & fs, const void* _data, int len, const char* dt) { cvStartWriteStruct(*fs, fs.elname.c_str(), CV_NODE_SEQ, "binary"); { @@ -6960,7 +7056,7 @@ void base64::cvWriteRawData_Base64(cv::FileStorage & fs, const void* _data, int cvEndWriteStruct(*fs); } -void base64::cvWriteMat_Base64(CvFileStorage * fs, const char * name, cv::Mat const & mat) +void base64::cvWriteMat_Base64(::CvFileStorage * fs, const char * name, ::cv::Mat const & mat) { char dt[4]; ::icvEncodeFormat(CV_MAT_TYPE(mat.type()), dt); @@ -6982,27 +7078,14 @@ void base64::cvWriteMat_Base64(CvFileStorage * fs, const char * name, cv::Mat co cvWriteString(fs, "dt", ::icvEncodeFormat(CV_MAT_TYPE(mat.type()), dt ), 0 ); } - cvStartWriteStruct(fs, "data", CV_NODE_SEQ, "binary"); { /* [2]deal with matrix's data */ - Base64ContextEmitter emitter(fs); + int len = static_cast(mat.total()); + MatToBinaryConvertor convertor(mat); - { /* [2][1]define base64 header */ - /* total byte size */ - int size = static_cast(mat.total() * mat.elemSize()); - std::string buffer = make_base64_header(size, dt); - const uchar * beg = reinterpret_cast(buffer.data()); - const uchar * end = beg + buffer.size(); - - emitter.write(beg, end); - } - - { /* [2][2]base64 body */ - MatToBinaryConvertor convertor(mat); - - emitter.write(convertor); - } + cvStartWriteRawData_Base64(fs, "data", len, dt); + fs->base64_writer->write(convertor, len); + cvEndWriteRawData_Base64(fs); } - cvEndWriteStruct(fs); { /* [3]output end */ cvEndWriteStruct(fs); @@ -7015,11 +7098,6 @@ void base64::cvWriteMat_Base64(CvFileStorage * fs, const char * name, cv::Mat co namespace cv { - void cvWriteRawData_Base64(::cv::FileStorage & fs, const void* _data, int len, const char* dt) - { - ::base64::cvWriteRawData_Base64(fs, _data, len, dt); - } - void cvWriteMat_Base64(::CvFileStorage* fs, const char* name, const ::CvMat* mat) { ::cv::Mat holder = ::cv::cvarrToMat(mat); @@ -7031,6 +7109,22 @@ namespace cv ::cv::Mat holder = ::cv::cvarrToMat(mat); ::base64::cvWriteMat_Base64(fs, name, holder); } + + void cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt) + { + ::base64::cvStartWriteRawData_Base64(fs, name, len, dt); + } + + void cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len) + { + ::base64::cvWriteRawData_Base64(fs, _data, len); + } + + void cvEndWriteRawData_Base64(::CvFileStorage * fs) + { + ::base64::cvEndWriteRawData_Base64(fs); + } + } diff --git a/modules/core/test/test_io.cpp b/modules/core/test/test_io.cpp index 99646e43f..39cd2ca17 100644 --- a/modules/core/test/test_io.cpp +++ b/modules/core/test/test_io.cpp @@ -583,132 +583,133 @@ TEST(Core_InputOutput, filestorage_yml_compatibility) //EXPECT_ANY_THROW(); } +class CV_Base64IOTest : public cvtest::BaseTest +{ +private: + std::string file_name; + +public: + CV_Base64IOTest(std::string const & file_name) + : file_name(file_name) {} + ~CV_Base64IOTest() {} +protected: + void run(int) + { + try + { + struct data_t + { + uchar u1, u2; + int i1, i2, i3; + double d1, d2; + int i4; + }; + std::vector rawdata; + + cv::Mat _em_out, _em_in; + cv::Mat _2d_out, _2d_in; + cv::Mat _nd_out, _nd_in; + + { /* init */ + + /* normal mat */ + _2d_out = cv::Mat(100, 100, CV_8UC3, cvScalar(1U, 2U, 127U)); + for (int i = 0; i < _2d_out.rows; ++i) + for (int j = 0; j < _2d_out.cols; ++j) + _2d_out.at(i, j)[1] = i % 256; + + /* 4d mat */ + const int Size[] = {4, 4, 4, 4}; + cv::Mat _4d(4, Size, CV_64FC4, cvScalar(0.888, 0.111, 0.666, 0.444)); + const cv::Range ranges[] = { + cv::Range(0, 2), + cv::Range(0, 2), + cv::Range(1, 2), + cv::Range(0, 2) }; + _nd_out = _4d(ranges); + + /* raw data */ + for (int i = 0; i < 1000; i++) + rawdata.push_back(data_t{1, 2, 1, 2, 3, 0.1, 0.2, i}); + } + + { /* write */ + cv::FileStorage fs(file_name, cv::FileStorage::WRITE); + CvMat holder = _2d_out; + cv::cvWriteMat_Base64(*fs, "normal_2d_mat", &holder); + CvMatND holder_nd = _nd_out; + cv::cvWriteMatND_Base64(*fs, "normal_nd_mat", &holder_nd); + holder = _em_out; + cv::cvWriteMat_Base64(*fs, "empty_2d_mat", &holder); + + cv::cvStartWriteRawData_Base64(*fs, "rawdata", rawdata.size(), "2u3i2di"); + for (int i = 0; i < 10; i++) + cv::cvWriteRawData_Base64(*fs, rawdata.data() + i * 100, 100); + cv::cvEndWriteRawData_Base64(*fs); + + fs.release(); + } + + { /* read */ + cv::FileStorage fs(file_name, cv::FileStorage::READ); + + /* mat */ + fs["empty_2d_mat"] >> _em_in; + fs["normal_2d_mat"] >> _2d_in; + fs["normal_nd_mat"] >> _nd_in; + + /* raw data */ + std::vector(1000).swap(rawdata); + cvReadRawData(*fs, fs["rawdata"].node, rawdata.data(), "2u3i2di"); + + fs.release(); + } + + for (int i = 0; i < 1000; i++) { + // TODO: Solve this bug + //EXPECT_EQ(rawdata[i].u1, 1); + //EXPECT_EQ(rawdata[i].u2, 2); + //EXPECT_EQ(rawdata[i].i1, 1); + //EXPECT_EQ(rawdata[i].i2, 2); + //EXPECT_EQ(rawdata[i].i3, 3); + //EXPECT_EQ(rawdata[i].d1, 0.1); + //EXPECT_EQ(rawdata[i].d2, 0.2); + //EXPECT_EQ(rawdata[i].i4, i); + } + + EXPECT_EQ(_em_in.rows , _em_out.rows); + EXPECT_EQ(_em_in.cols , _em_out.cols); + EXPECT_EQ(_em_in.dims , _em_out.dims); + EXPECT_EQ(_em_in.depth(), _em_out.depth()); + EXPECT_TRUE(_em_in.empty()); + + EXPECT_EQ(_2d_in.rows , _2d_in.rows); + EXPECT_EQ(_2d_in.cols , _2d_in.cols); + EXPECT_EQ(_2d_in.dims , _2d_in.dims); + EXPECT_EQ(_2d_in.depth(), _2d_in.depth()); + for(int i = 0; i < _2d_in.rows; ++i) + for (int j = 0; j < _2d_in.cols; ++j) + EXPECT_EQ(_2d_in.at(i, j), _2d_out.at(i, j)); + + EXPECT_EQ(_nd_in.rows , _nd_in.rows); + EXPECT_EQ(_nd_in.cols , _nd_in.cols); + EXPECT_EQ(_nd_in.dims , _nd_in.dims); + EXPECT_EQ(_nd_in.depth(), _nd_in.depth()); + EXPECT_EQ(cv::countNonZero(cv::mean(_nd_in != _nd_out)), 0); + } + catch(...) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + } + } +}; + TEST(Core_InputOutput, filestorage_yml_base64) { - cv::Mat _em_out, _em_in; - cv::Mat _2d_out, _2d_in; - cv::Mat _nd_out, _nd_in; - - { /* init */ - - /* normal mat */ - _2d_out = cv::Mat(100, 100, CV_8UC3, cvScalar(1U, 2U, 127U)); - for (int i = 0; i < _2d_out.rows; ++i) - for (int j = 0; j < _2d_out.cols; ++j) - _2d_out.at(i, j)[1] = i % 256; - - /* 4d mat */ - const int Size[] = {4, 4, 4, 4}; - cv::Mat _4d(4, Size, CV_64FC4, cvScalar(0.888, 0.111, 0.666, 0.444)); - const cv::Range ranges[] = { - cv::Range(0, 2), - cv::Range(0, 2), - cv::Range(1, 2), - cv::Range(0, 2) }; - _nd_out = _4d(ranges); - } - - { /* write */ - cv::FileStorage fs("test.yml", cv::FileStorage::WRITE); - CvMat holder = _2d_out; - cv::cvWriteMat_Base64(*fs, "normal_2d_mat", &holder); - CvMatND holder_nd = _nd_out; - cv::cvWriteMatND_Base64(*fs, "normal_nd_mat", &holder_nd); - holder = _em_out; - cv::cvWriteMat_Base64(*fs, "empty_2d_mat", &holder); - fs.release(); - } - - { /* read */ - cv::FileStorage fs("test.yml", cv::FileStorage::READ); - fs["empty_2d_mat"] >> _em_in; - fs["normal_2d_mat"] >> _2d_in; - fs["normal_nd_mat"] >> _nd_in; - fs.release(); - } - - EXPECT_EQ(_em_in.rows , _em_out.rows); - EXPECT_EQ(_em_in.cols , _em_out.cols); - EXPECT_EQ(_em_in.dims , _em_out.dims); - EXPECT_EQ(_em_in.depth(), _em_out.depth()); - EXPECT_TRUE(_em_in.empty()); - - EXPECT_EQ(_2d_in.rows , _2d_in.rows); - EXPECT_EQ(_2d_in.cols , _2d_in.cols); - EXPECT_EQ(_2d_in.dims , _2d_in.dims); - EXPECT_EQ(_2d_in.depth(), _2d_in.depth()); - for(int i = 0; i < _2d_in.rows; ++i) - for (int j = 0; j < _2d_in.cols; ++j) - EXPECT_EQ(_2d_in.at(i, j), _2d_out.at(i, j)); - - EXPECT_EQ(_nd_in.rows , _nd_in.rows); - EXPECT_EQ(_nd_in.cols , _nd_in.cols); - EXPECT_EQ(_nd_in.dims , _nd_in.dims); - EXPECT_EQ(_nd_in.depth(), _nd_in.depth()); - EXPECT_EQ(cv::countNonZero(cv::mean(_nd_in != _nd_out)), 0); + CV_Base64IOTest test("base64_test.yml"); test.safe_run(); } TEST(Core_InputOutput, filestorage_xml_base64) { - cv::Mat _em_out, _em_in; - cv::Mat _2d_out, _2d_in; - cv::Mat _nd_out, _nd_in; - - { /* init */ - - /* normal mat */ - _2d_out = cv::Mat(100, 100, CV_8UC3, cvScalar(1U, 2U, 127U)); - for (int i = 0; i < _2d_out.rows; ++i) - for (int j = 0; j < _2d_out.cols; ++j) - _2d_out.at(i, j)[1] = i % 256; - - /* 4d mat */ - const int Size[] = {4, 4, 4, 4}; - cv::Mat _4d(4, Size, CV_64FC4, cvScalar(0.888, 0.111, 0.666, 0.444)); - const cv::Range ranges[] = { - cv::Range(0, 2), - cv::Range(0, 2), - cv::Range(1, 2), - cv::Range(0, 2) }; - _nd_out = _4d(ranges); - } - - { /* write */ - cv::FileStorage fs("test.xml", cv::FileStorage::WRITE); - CvMat holder = _2d_out; - cv::cvWriteMat_Base64(*fs, "normal_2d_mat", &holder); - CvMatND holder_nd = _nd_out; - cv::cvWriteMatND_Base64(*fs, "normal_nd_mat", &holder_nd); - holder = _em_out; - cv::cvWriteMat_Base64(*fs, "empty_2d_mat", &holder); - fs.release(); - } - - { /* read */ - cv::FileStorage fs("test.xml", cv::FileStorage::READ); - fs["empty_2d_mat"] >> _em_in; - fs["normal_2d_mat"] >> _2d_in; - fs["normal_nd_mat"] >> _nd_in; - fs.release(); - } - - EXPECT_EQ(_em_in.rows , _em_out.rows); - EXPECT_EQ(_em_in.cols , _em_out.cols); - EXPECT_EQ(_em_in.dims , _em_out.dims); - EXPECT_EQ(_em_in.depth(), _em_out.depth()); - EXPECT_TRUE(_em_in.empty()); - - EXPECT_EQ(_2d_in.rows , _2d_in.rows); - EXPECT_EQ(_2d_in.cols , _2d_in.cols); - EXPECT_EQ(_2d_in.dims , _2d_in.dims); - EXPECT_EQ(_2d_in.depth(), _2d_in.depth()); - for(int i = 0; i < _2d_in.rows; ++i) - for (int j = 0; j < _2d_in.cols; ++j) - EXPECT_EQ(_2d_in.at(i, j), _2d_out.at(i, j)); - - EXPECT_EQ(_nd_in.rows , _nd_in.rows); - EXPECT_EQ(_nd_in.cols , _nd_in.cols); - EXPECT_EQ(_nd_in.dims , _nd_in.dims); - EXPECT_EQ(_nd_in.depth(), _nd_in.depth()); - EXPECT_EQ(cv::countNonZero(cv::sum(_nd_in != _nd_out)), 0); + CV_Base64IOTest test("base64_test.xml"); test.safe_run(); }