Merge several boost serialized OpenCV Mats

2019-06-12 14:12发布

问题:

Follow up the question here of Serializing OpenCV Mat_

My task is I have multiple OpenCV Mats that are serialized. Now I want to merge all those Mats. I can do this by deserialize these binaries into Mats and use push_back method to merge them. However, for my own reason, I have to merge them in binary format first before deserialize.

How can I merge these binaries so that in the end, I can call my same deserialization to get the whole big Mat?

Thanks

回答1:

You can do this without using boost. Following the serialization approach described here, you can append the data of your matrix at the end of the file, taking care of increasing the number of rows of the final matrix accordingly.

Here's a working example, where matappend does the job. I'll put also the matread and matwrite functions for completeness:

#include <opencv2\opencv.hpp>
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;


void matwrite(const string& filename, const Mat& mat)
{
    ofstream fs(filename, fstream::binary);

    // Header
    int type = mat.type();
    int channels = mat.channels();
    fs.write((char*)&mat.rows, sizeof(int));    // rows
    fs.write((char*)&mat.cols, sizeof(int));    // cols
    fs.write((char*)&type, sizeof(int));        // type
    fs.write((char*)&channels, sizeof(int));    // channels

    // Data
    if (mat.isContinuous())
    {
        fs.write(mat.ptr<char>(0), (mat.dataend - mat.datastart));
    }
    else
    {
        int rowsz = CV_ELEM_SIZE(type) * mat.cols;
        for (int r = 0; r < mat.rows; ++r)
        {
            fs.write(mat.ptr<char>(r), rowsz);
        }
    }
}

Mat matread(const string& filename)
{
    ifstream fs(filename, fstream::binary);

    // Header
    int rows, cols, type, channels;
    fs.read((char*)&rows, sizeof(int));         // rows
    fs.read((char*)&cols, sizeof(int));         // cols
    fs.read((char*)&type, sizeof(int));         // type
    fs.read((char*)&channels, sizeof(int));     // channels

    // Data
    Mat mat(rows, cols, type);
    fs.read((char*)mat.data, CV_ELEM_SIZE(type) * rows * cols);

    return mat;
}

void matappend(const string& filename, const Mat& mat)
{
    fstream fs(filename, fstream::binary | fstream::in);

    // https://stackoverflow.com/a/2390938/5008845
    if (fs.peek() == fstream::traits_type::eof())
    {
        // The file is empty, write (same as matwrite)

        fs.close();
        fs.open(filename, fstream::binary | fstream::out);

        // Header
        int type = mat.type();
        int channels = mat.channels();
        fs.write((char*)&mat.rows, sizeof(int));    // rows
        fs.write((char*)&mat.cols, sizeof(int));    // cols
        fs.write((char*)&type, sizeof(int));        // type
        fs.write((char*)&channels, sizeof(int));    // channels
    }
    else
    {
        // The file is not empty, append

        fs.close();
        fs.open(filename, fstream::binary | fstream::out | fstream::in);

        // Read Header
        int rows, cols, type, channels;
        fs.read((char*)&rows, sizeof(int));         // rows
        fs.read((char*)&cols, sizeof(int));         // cols
        fs.read((char*)&type, sizeof(int));         // type
        fs.read((char*)&channels, sizeof(int));     // channels

        // Consistency check
        CV_Assert((cols == mat.cols) && (type == mat.type()) && (channels == mat.channels()));

        // Go to beginning of file
        fs.seekp(fstream::beg);

        // Overwrite the number of rows
        rows += mat.rows;
        fs.write((char*)&rows, sizeof(int));    // rows

        // Go to end of file
        fs.seekp(0, fstream::end);
    }

    // Write data
    if (mat.isContinuous())
    {
        fs.write(mat.ptr<char>(0), (mat.dataend - mat.datastart));
    }
    else
    {
        int rowsz = CV_ELEM_SIZE(mat.type()) * mat.cols;
        for (int r = 0; r < mat.rows; ++r)
        {
            fs.write(mat.ptr<char>(r), rowsz);
        }
    }
    fs.close();
}



int main()
{
    // Save the random generated data

    Mat1b m1 = (Mat1b(2, 2) << 1, 2, 3, 4);
    Mat1b m2 = (Mat1b(3, 2) << 5, 6, 7, 8, 9, 10);

    matappend("raw.bin", m1);
    matappend("raw.bin", m2);

    Mat m3 = matread("raw.bin");

    // m3: 
    // 1 2
    // 3 4
    // 5 6
    // 7 8
    // 9 10


    return 0;
}