Search code examples
c++opencvboostc++17

Deserializing array of string with boost and are bigger than expected (after serializing from cv::cuda::GpuMat)


I'm trying to serialize and deserialize an array of cv::cuda::GpuMat (pitched arrays of byte / uchar). In the deserialize part, I don't need to recover a GpuMat, but a byte array.

I use those two classes:

#pragma once
#include <fstream>
#include <string>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <opencv2/core/cuda.hpp>

using namespace std;
using namespace cv;
using namespace cuda;

#ifndef uchar
#define uchar unsigned char
#endif

class SerieFlow
{
    friend class boost::serialization::access;
    
    template <class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & flowX;
        ar & flowY;
    }

    GpuMat* FlowPlanes{}; // GpuMat[2]
public:
    string flowX{};
    string flowY{};

    void PrepData()
    {
        Mat matX = Mat(FlowPlanes[0]);
        Mat matY = Mat(FlowPlanes[1]);
        flowX = *new string((const char*)matX.col(0).data);
        flowY = *new string((const char*)matY.col(0).data);
        matX.release();
        matY.release();
    }
    
    SerieFlow() = default;
    SerieFlow(GpuMat* flowPlanes) : FlowPlanes(flowPlanes)
    {
        PrepData();
    }
};

class SerieFlowFile
{
public:
    void Save(SerieFlow content, string filename)
    {
        ofstream stream(filename);
        {
            boost::archive::text_oarchive archive(stream);
            archive << content;
        }
    }
    
    SerieFlow Open(string filename)
    {
        SerieFlow content;
        {
            ifstream stream(filename);
            boost::archive::text_iarchive archive(stream);
            archive >> content;
        }
        return content;
    }
};

And I unit test the results with:

TEST_METHOD(ReadDeserializeTest)
{
    string filename{ "WriteRead.sflow" };
    if (filesystem::exists(filename))
        std::remove(filename.c_str());
    unsigned char data[8] = { 7, 4, 2, 6, 7, 18, 29, 111 };
    string dataAsString((const char*)data, 8);
    vector<unsigned char> expectedData{ dataAsString.begin(), dataAsString.end() };
    Mat mat{ 8, 1, CV_8UC1, data };
    GpuMat gpuMat1(mat);
    GpuMat gpuMat2(mat);
    GpuMat gpuMatArray[2] = { gpuMat1 , gpuMat2 };
    SerieFlow sflow(gpuMatArray);
    SerieFlowFile sut{};
    sut.Save(sflow, filename);

    SerieFlowFile sut2{};
    auto sflow2 = sut2.Open(filename);
    vector<unsigned char> resultFlowX{ sflow2.flowX.begin(),sflow2.flowX.end() };
    vector<unsigned char> resultFlowY{ sflow2.flowY.begin(),sflow2.flowY.end() };

    stringstream ss{};
    ss << "flowX size: " << resultFlowX.size();
    ss << " flowY size: " << resultFlowY.size() << endl;
    Logger::WriteMessage(ss.str().c_str());

    stringstream resultFlowXStream{};
    stringstream resultFlowYStream{};
    copy(resultFlowX.begin(), resultFlowX.end(), std::ostream_iterator<int>(resultFlowXStream, " "));
    copy(resultFlowY.begin(), resultFlowY.end(), std::ostream_iterator<int>(resultFlowYStream, " "));
    Logger::WriteMessage(resultFlowXStream.str().c_str());
    Logger::WriteMessage(resultFlowYStream.str().c_str());

    Assert::IsTrue(resultFlowX == resultFlowY, L"flowX and flowY are not the same.");
    Assert::IsTrue(expectedData == resultFlowX, L"resultFlowX is not correct.");
    Assert::IsTrue(expectedData == resultFlowY, L"resultFlowY is not correct.");
}

However I get:

flowX size: 52 flowY size: 36

7 4 2 6 7 18 29 111 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 253 253 253 253

and

7 4 2 6 7 18 29 111 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 253 253 253 253

I'm expecting both to be of size 8 and to give:

7 4 2 6 7 18 29 111

Is it normal to get this much padding?

I'm using string as a convenient way to manipulate a byte array, compare, etc.

I suspect I have a problem with string because my raw array from the file is not null terminated; how should I serialize and deserialize those arrays ? How come this is a problem if I null terminated the string at serialization (string dataAsString((const char*)data, 8);) ?

I'm using boost 1.72 as nuget package and opencv 4.51, and test with VS2019 toolset 1.42 in x64.


Solution

  • In PrepData, string are initialized without a given length, leading to arbitrary long string (until null '\0' is reached).

    The correct code is :

    string strX((const char*)matX.col(0).data, matX.rows);
    string strY((const char*)matY.col(0).data, matY.rows);