Сериализация OpenCV Mat_

Я работаю над исследовательским проектом по робототехнике, где мне нужно сериализовать 2D-матрицы трехмерных точек: в основном каждый пиксель представляет собой 3-векторный поплавок. Эти пиксели сохраняются в матрице OpenCV, и их необходимо отправлять по межпроцессной связи и сохранять в файлах, которые будут обрабатываться на нескольких компьютерах. Я бы хотел как можно быстрее сериализовать их в независимом от endian / architecture-space, экономичном пространстве . cv::imencode здесь был бы идеальным, за исключением того, что он работает только на 8-битных и 16-битных элементах, и мы не хотим терять точность. Файлы не должны быть удобочитаемыми (хотя мы делаем это сейчас, чтобы обеспечить переносимость данных, и это невероятно медленно). Есть ли лучшие методы для этого, или изящные способы сделать это?

Благодаря!

Редактировать: Christoph Heindl прокомментировал это сообщение со ссылкой на свой блог, где он улучшил этот код сериализации. Настоятельно рекомендуется!

http://cheind.wordpress.com/2011/12/06/serialization-of-cvmat-objects-using-boost/

Для тех, кому это может пригодиться: некоторый код для сериализации Mat & with boost :: serialization
Я не тестировал многоканальные данные, но все должно работать нормально.

 #include  #include  #include  #include  #include  #include  BOOST_SERIALIZATION_SPLIT_FREE(Mat) namespace boost { namespace serialization { /*** Mat ***/ template void save(Archive & ar, const Mat& m, const unsigned int version) { size_t elemSize = m.elemSize(), elemType = m.type(); ar & m.cols; ar & m.rows; ar & elemSize; ar & elemType; // element type. size_t dataSize = m.cols * m.rows * m.elemSize(); //cout << "Writing matrix data rows, cols, elemSize, type, datasize: (" << m.rows << "," << m.cols << "," << m.elemSize() << "," << m.type() << "," << dataSize << ")" << endl; for (size_t dc = 0; dc < dataSize; ++dc) { ar & m.data[dc]; } } template void load(Archive & ar, Mat& m, const unsigned int version) { int cols, rows; size_t elemSize, elemType; ar & cols; ar & rows; ar & elemSize; ar & elemType; m.create(rows, cols, elemType); size_t dataSize = m.cols * m.rows * elemSize; //cout << "reading matrix data rows, cols, elemSize, type, datasize: (" << m.rows << "," << m.cols << "," << m.elemSize() << "," << m.type() << "," << dataSize << ")" << endl; for (size_t dc = 0; dc < dataSize; ++dc) { ar & m.data[dc]; } } } } 

Теперь мат можно сериализовать и десериализовать следующим образом:

  void saveMat(Mat& m, string filename) { ofstream ofs(filename.c_str()); boost::archive::binary_oarchive oa(ofs); //boost::archive::text_oarchive oa(ofs); oa << m; } void loadMat(Mat& m, string filename) { std::ifstream ifs(filename.c_str()); boost::archive::binary_iarchive ia(ifs); //boost::archive::text_iarchive ia(ifs); ia >> m; } 

Я использовал здесь binary_oarchive и binary_iarchive, чтобы сохранить использование памяти. Бинарный формат не обеспечивает переносимость между платформами, но при желании можно использовать text_oarchive / iarchive.

Более ранние ответы хороши, но они не будут работать для не-непрерывных матриц, которые возникают, когда вы хотите сериализовать интересующие регионы (между прочим). Кроме того, нет необходимости в сериализации elemSize() потому что это происходит из значения type .

Вот код, который будет работать независимо от непрерывности (включая include / namespace)

 #pragma once #include  #include  #include  #include  namespace boost { namespace serialization { template void serialize(Archive &ar, cv::Mat& mat, const unsigned int) { int cols, rows, type; bool continuous; if (Archive::is_saving::value) { cols = mat.cols; rows = mat.rows; type = mat.type(); continuous = mat.isContinuous(); } ar & cols & rows & type & continuous; if (Archive::is_loading::value) mat.create(rows, cols, type); if (continuous) { const unsigned int data_size = rows * cols * mat.elemSize(); ar & boost::serialization::make_array(mat.ptr(), data_size); } else { const unsigned int row_size = cols*mat.elemSize(); for (int i = 0; i < rows; i++) { ar & boost::serialization::make_array(mat.ptr(i), row_size); } } } } // namespace serialization } // namespace boost 

Для этого вы можете использовать boost::serialization . Он сильно оптимизирован и довольно легко интегрируется.

Возможные ускорения для вашего случая include сериализацию каждого объекта в виде необработанного двоичного блока (см. boost::serialization::make_binary ) и отключение отслеживания версий ( BOOST_SERIALIZATION_DISABLE_TRACKING ).

Кроме того, вы можете поэкспериментировать с добавлением сжатия в свои процедуры сериализации для экономии места (и времени в случае легко сжимаемых данных ). Это может быть реализовано с помощью boost::iostreams , например.

Недавно я задал себе аналогичный вопрос, хотя я специально пытался сериализовать объекты MatND и MatND . Использование boost::serialize неплохо, но требует нескольких трюков. Поскольку вы не хотите модифицировать внутренности самого OpenCV для сериализации этих объектов, вы вынуждены использовать так называемую «свободную» функцию. Поскольку сложно сериализовать объекты OpenCV, я обнаружил, что мне пришлось разделить операцию сериализации на сохранение и загрузку, каждая из которых имеет немного другую реализацию. Для этой задачи вам необходимо использовать boost/serialization/split_free.hpp . Boost предоставляет хорошую документацию для этого здесь: http://www.boost.org/doc/libs/1_45_0/libs/serialization/doc/index.html .

Удачи!

Как насчет того, чтобы просто преобразовать свой Mat в вектор и использовать fwrite?

Преобразование в векторный процесс может повлиять на производительность, но это безопасно. Я подозреваю, что все приведенные выше ответы, либо обход данных изображения, как в принятом ответе, либо использование make_array, как и в столбце Christoph, предполагают, что ваши данные Mat являются смежными, что не обязательно так. Когда ваши данные Mat не будут смежными, результат этих ответов будет неправильным.

Вы можете использовать msgpack также Создать адаптер https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor Вот пример кода. Это может быть полезно:

 namespace clmdep_msgpack { MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) { namespace adaptor { //I am sending as bin (int)(int)(int)(char*) //Mat values: rows,cols,type,data template<> struct convert { clmdep_msgpack::object const &operator()(clmdep_msgpack::object const &o, cv::Mat &v) const { if(o.type != clmdep_msgpack::type::BIN) throw clmdep_msgpack::type_error(); char *buffer = (char *) o.via.bin.ptr; int buffer_size = o.via.bin.size; int rows, cols, type; rows = *reinterpret_cast(buffer); cols = *reinterpret_cast(buffer + 1 * sizeof(int)); type = *reinterpret_cast(buffer + 2 * sizeof(int)); cv::Mat(rows, cols, type, (void *) (buffer + 3 * sizeof(int))).copyTo(v); return o; } }; template<> struct pack { template clmdep_msgpack::packer &operator()(clmdep_msgpack::packer &o, cv::Mat const &v) const { // packing member variables as bin. size_t mat_size; if(v.isContinuous()) { mat_size = v.total() * v.elemSize(); } else { mat_size = v.step[v.dims - 1]; for(int t = 0; t < v.dims; t++) { // calculate total size of multi dimensional matrix by multiplying dimensions mat_size *= v.size[t]; } } int extra_ints = 3; int buffer_size = extra_ints * sizeof(int) + mat_size; // Allocate destination image buffer char *imagebuffer = new char[buffer_size]; int type = v.type(); std::memcpy(imagebuffer, &(v.rows), sizeof(int)); std::memcpy(imagebuffer + 1 * sizeof(int), &(v.cols), sizeof(int)); std::memcpy(imagebuffer + 2 * sizeof(int), &type, sizeof(int)); if(v.isContinuous()) { std::memcpy((imagebuffer + 3 * sizeof(int)), (char *) v.data, mat_size); } else { const size_t rowsize = v.step[v.dims - 1] * v.size[v.dims - 1]; size_t coordinates[v.dims - 1] = {0}; size_t srcptr = 0, dptr = extra_ints * sizeof(int); while(dptr < buffer_size) { // we copy entire rows at once, so lowest iterator is always [dims-2] // this is legal since OpenCV does not use 1 dimensional matrices internally (a 1D matrix is a 2d matrix with only 1 row) std::memcpy(&imagebuffer[dptr], &(((char *) v.data)[srcptr]), rowsize); // destination matrix has no gaps so rows follow each other directly dptr += rowsize; // src matrix can have gaps so we need to calculate the address of the start of the next row the hard way // see *brief* text in opencv2/core/mat.hpp for address calculation coordinates[v.dims - 2]++; srcptr = 0; for(int t = v.dims - 2; t >= 0; t--) { if(coordinates[t] >= v.size[t]) { if(t == 0) break; coordinates[t] = 0; coordinates[t - 1]++; } srcptr += v.step[t] * coordinates[t]; } } } o.pack_bin(buffer_size); o.pack_bin_body(imagebuffer, buffer_size); return o; } }; template<> struct object_with_zone { void operator()(clmdep_msgpack::object::with_zone &o, cv::Mat const &v) const { size_t mat_size; if(v.isContinuous()) { mat_size = v.total() * v.elemSize(); } else { mat_size = v.step[v.dims - 1]; for(int t = 0; t < v.dims; t++) { // calculate total size of multi dimensional matrix by multiplying dimensions mat_size *= v.size[t]; } } int extra_ints = 3; int buffer_size = extra_ints * sizeof(int) + mat_size; // Allocate destination image buffer char *imagebuffer = new char[buffer_size]; int type = v.type(); std::memcpy(imagebuffer, &(v.rows), sizeof(int)); std::memcpy(imagebuffer + 1 * sizeof(int), &(v.cols), sizeof(int)); std::memcpy(imagebuffer + 2 * sizeof(int), &type, sizeof(int)); if(v.isContinuous()) { std::memcpy((imagebuffer + 3 * sizeof(int)), (char *) v.data, mat_size); } else { const size_t rowsize = v.step[v.dims - 1] * v.size[v.dims - 1]; size_t coordinates[v.dims - 1] = {0}; size_t srcptr = 0, dptr = extra_ints * sizeof(int); while(dptr < buffer_size) { // we copy entire rows at once, so lowest iterator is always [dims-2] // this is legal since OpenCV does not use 1 dimensional matrices internally (a 1D matrix is a 2d matrix with only 1 row) std::memcpy(&imagebuffer[dptr], &(((char *) v.data)[srcptr]), rowsize); // destination matrix has no gaps so rows follow each other directly dptr += rowsize; // src matrix can have gaps so we need to calculate the address of the start of the next row the hard way // see *brief* text in opencv2/core/mat.hpp for address calculation coordinates[v.dims - 2]++; srcptr = 0; for(int t = v.dims - 2; t >= 0; t--) { if(coordinates[t] >= v.size[t]) { if(t == 0) break; coordinates[t] = 0; coordinates[t - 1]++; } srcptr += v.step[t] * coordinates[t]; } } } o.type = type::BIN; o.via.bin.size = buffer_size; o.via.bin.ptr = imagebuffer; } }; } // namespace adaptor } // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) } // names 

Я написал этот код:

 /* Will save in the file: cols\n rows\n elemSize\n type\n DATA */ void serializeMatbin(Mat& mat, std::string filename){ if (!mat.isContinuous()) { cout << "Not implemented yet" << endl; exit(1); } int elemSizeInBytes = (int)mat.elemSize(); int elemType = (int)mat.type(); int dataSize = (int)(mat.cols * mat.rows * mat.elemSize()); FILE* FP = fopen(filename.c_str(), "wb"); int sizeImg[4] = {mat.cols, mat.rows, elemSizeInBytes, elemType }; fwrite(/*buffer*/ sizeImg, /*howmanyelements*/ 4, /* size of each element */ sizeof(int), /*file*/ FP); fwrite(mat.data, mat.cols * mat.rows, elemSizeInBytes, FP); fclose(FP); } Mat deserializeMatbin(std::string filename){ FILE* fp = fopen(filename.c_str(), "r"); int header[4]; fread(header, sizeof(int), 4, fp); int cols = header[0]; int rows = header[1]; int elemSizeInBytes = header[2]; int elemType = header[3]; Mat outputMat = Mat(rows, cols, elemType); fread(outputMat.data, elemSizeInBytes, cols * rows, fp); fclose(fp); return outputMat; } void testSerializeMatbin(){ Mat a = Mat::ones(/*cols*/ 10, /* rows */ 5, CV_8U) * 2; std::string filename = "test.matbin"; serializeMatbin(a, filename); Mat b = deserializeMatbin(filename); cout << "Rows: " << b.rows << " Cols: " << b.cols << " type: " << b.type()<< endl; } 
  • Использование JsonConvert.DeserializeObject для десериализации Json в class C # POCO
  • Обнаружено, что
  • Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
  • Java: объект для байта и байт для конвертера объектов (для Tokyo Cabinet)
  • Информация о сериализации типов кеша Json.NET?
  • Автоматическая десериализация свойств C #
  • Преобразование любого объекта в массив байтов в java
  • Сериализация с Qt
  • Json Сериализация Java, которая работает с GWT
  • Django rest framework, используйте разные сериализаторы в том же ModelViewSet
  • Json.NET: десериализация вложенных словарей
  • Interesting Posts
    Давайте будем гением компьютера.