DavRay DavRay - 8 days ago 4
C++ Question

How to convert vector<Mat> to vector<float>?

Suppose i have a

vector<Mat>
called
regionFeaMat
and
regionFeaMat.size() == 81
.In other words, regionFeaMat have 81 equal size matrix, and
regionFeaMat[0].rows==256
and
regionFeaMat[0].cols==1
. I want to convert
reginFeaMat
to
vector<float> reginFeaVec
. I tried with following code but i got wrong result:

vector<float> regionFeaVec;
regionFeaVec.assign((float*)regionFeaMat[0].datastart, (float*)regionFeaMat[80].dataend);

Answer

You seem to have made a few wrong assumptions.

std::vector does store its elements contiguously in memory, but cv::Mat is a header containing a pointer to its internal buffer, so only pointers in vector<Mat> are stored contiguously, not the Mat data itself. Because of that, the memory that lies in between (float*)regionFeaMat[0].dataend and (float*)regionFeaMat[80].datastart is some random garbage - if it does contain other Mat's data partially, it's pure luck.

Because of the above, you can't have a one-liner assigning vector to any other vector and you have to insert each mat separately instead. Try something like this:

// prevent vector reallocation after each Mat insertion:
regionFeaVec.reserve(regionFeaMat.size()*regionFeaMat[0].cols*regionFeaMat[0].rows);
for (int i = 0; i < regionFeaMat.size(); ++i)
{
    if (regionFeaMat[i].isContinuous())
        regionFeaVec.insert(
                           regionFeaVec.end(), 
                           (float*)regionFeaMat[i].datastart, 
                           (float*)regionFeaMat[i].dataend
                           );
    else
    {
        for (int j = 0; j < regionFeaMat[i].rows; ++j)
        {
                const float* row = regionFeaMat[i].ptr<float>(j);
                regionFeaVec.insert(regionFeaVec.end(), row, row + regionFeaMat[i].cols);
        }
    }
}

Note that I'm checking if a particular Mat object is continuous, because as per OpenCV docs, each row may contain gaps at the end in some cases, and in that case we have to read the matrix row by row.

This code can be simplified, because if matrix is continuous, we may treat it as a 1D vector as per the docs referenced above:

// prevent vector reallocation after each Mat insertion:
regionFeaVec.reserve(regionFeaMat.size()*regionFeaMat[0].cols*regionFeaMat[0].rows);
for (int i = 0; i < regionFeaMat.size(); ++i)
{
    cv::Size size = regionFeaMat[i].size();
    if (regionFeaMat[i].isContinuous())
    {
        size.width *= size.height;
        size.height = 1;
    }
    for (int j = 0; j < size.height; ++j)
    {
        const float* row = regionFeaMat[i].ptr<float>(j);
        regionFeaVec.insert(regionFeaVec.end(), row, row + size.width);
    }
}

If you want to prevent vector reallocation in more general cases, you also have to change the method of calculating the number of elements passed to reserve(). The method I use assumes all the Mat objects have only two dimensions that are equal for all the objects since this is how you described your problem.

Also, if you want to assign it to vector<float>, be sure that the element type of regionFeaMat is CV_32F.