I\'m working on a robotics research project where I need to serialize 2D matrices of 3D points: basically each pixel is a 3-vector of floats. These pixels are saved in an Op
How about just convert your Mat to a vector and use fwrite?
The converting to vector process might affect performance, but it's safe. I suspect that all the answers above, either looping through the image data as in the accepted answer, or using make_array as in Christoph post, assume that your Mat data is contiguous, which is not necessarily the case. When your Mat data is not contiguous, the output from these answers will not be correct.
The earlier answers are good, but they won't work for non-continuous matrices which arise when you want to serialize regions of interest (among other things). Also, it is unnecessary to serialize elemSize()
because this is derived from the type
value.
Here's some code that will work regardless of continuity (with includes/namespace)
#pragma once
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <opencv2/opencv.hpp>
namespace boost {
namespace serialization {
template<class Archive>
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
You could use boost::serialization
for that. It's heavily optimized and is pretty easy to integrate.
Possible speed-ups for your case include serializing each object as a raw binary block (see boost::serialization::make_binary
) and disabling version tracking (BOOST_SERIALIZATION_DISABLE_TRACKING
).
Also, you can experiment with adding compression into your serialization routines to save space (and time in case of data that is easily compressable). This can be implemented with boost::iostreams
, for example.
I was recently asking myself a similar question, though specifically I was trying to serialize opencv's Mat
and MatND
objects. Using boost::serialize
is nice, but requires a couple tricks. As you don't want to go about modifying the internals of OpenCV itself to serialize these objects, you are forced to use what's called a "free" function. Since it is complicated to serialize the OpenCV objects, I found I was forced to split the serialize operation into save and load, each with a slightly different implementation. You need to use boost/serialization/split_free.hpp
for this task. Boost provides good documentation for this here: http://www.boost.org/doc/libs/1_45_0/libs/serialization/doc/index.html.
Good luck!
Edit: Christoph Heindl has commented on this post with a link to his blog where he has improved on this serialisation code. Highly recommended!
http://cheind.wordpress.com/2011/12/06/serialization-of-cvmat-objects-using-boost/
--
For whoever it may benefit: Some code to serialize Mat& with boost::serialization
I haven't tested with multi-channel data, but everything should work fine.
#include <iostream>
#include <fstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/vector.hpp>
BOOST_SERIALIZATION_SPLIT_FREE(Mat)
namespace boost {
namespace serialization {
/*** Mat ***/
template<class Archive>
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<class Archive>
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];
}
}
}
}
Now, mat can be serialized and deserialized as following:
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;
}
I've used the binary_oarchive and binary_iarchive here to keep the memory usage down. The binary format doesn't provide portability between platforms, but if desired the text_oarchive/iarchive can be used.
I wrote this code:
/*
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;
}