Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Hello

Here is the code I made in order to read one or more matrices written in a text file. It is all but the most efficient way to do it and it is limitted to the numpy formating.

utils.h

#include <opencv2/core.hpp>
#include <fstream>

namespace utils
{
class StdFileStreamFormater
{
public:

    inline StdFileStreamFormater(std::fstream& _stream):
        stream(_stream)
    {}

    StdFileStreamFormater(const StdFileStreamFormater&) = delete;
    StdFileStreamFormater(StdFileStreamFormater&&) = delete;

    ~StdFileStreamFormater() = default;

    StdFileStreamFormater& operator=(const StdFileStreamFormater&) = delete;
    StdFileStreamFormater& operator=(StdFileStreamFormater&&) = delete;


    void writeMatrixWithNumpyFmt(cv::InputArray _tmp);

    void readFileWithNumpyFmt(cv::OutputArray _out);



    inline std::fstream& getStream()const { return this->stream; }

    inline operator std::fstream&()const { return this->stream; }

private:

    std::fstream& stream;
};
} //utils

utils.cpp

#include <fstream>
#include <sstream>
#include <iterator>
#include <numeric>

namespace utils
{

void StdFileStreamFormater::writeMatrixWithNumpyFmt(cv::InputArray _tmp)
{

    CV_Assert(_tmp.isMat() || _tmp.isUMat() || _tmp.isGpuMat() || _tmp.isMatVector() || _tmp.isUMatVector() || _tmp.isGpuMatVector() || (_tmp.kind() == cv::_InputArray::EXPR));

    if(!_tmp.isVector())
    {
        if(!_tmp.isGpuMat())
            this->stream<<cv::format(_tmp.getMat(), cv::Formatter::FMT_NUMPY)<<std::endl;
        else
        {
           cv::Mat tmp(_tmp.getGpuMat());
           this->stream<<cv::format(tmp, cv::Formatter::FMT_NUMPY)<<std::endl;
        }
    }
    else
    {
        std::vector<cv::Mat> tmp;

        if(!_tmp.isGpuMatVector())
            _tmp.getMatVector(tmp);
        else
        {
            std::vector<cv::cuda::GpuMat> tmp2;

            _tmp.getGpuMatVector(tmp2);

            tmp.resize(tmp2.size());

            std::transform(tmp2.begin(), tmp2.end(), tmp.begin(),[](const cv::cuda::GpuMat& src)->cv::Mat{ return cv::Mat(src); });
        }

        for(const cv::Mat& image : tmp)
            this->stream<<cv::format(image, cv::Formatter::FMT_NUMPY)<<std::endl;
    }
}

void StdFileStreamFormater::readFileWithNumpyFmt(cv::OutputArray _out)
{

    static std::unordered_map<cv::String, int> Strings2Types =
    {
        std::make_pair("uint8",CV_8U),
        std::make_pair("int8",CV_8S),
        std::make_pair("uint16",CV_16U),
        std::make_pair("int16",CV_16S),
        std::make_pair("int32",CV_32S),
        std::make_pair("float32",CV_32F),
        std::make_pair("foat64",CV_64F),
    };

    // Step 0-a): Copy the content of the file in memory.
    std::ostringstream oss;

    oss<<this->stream.rdbuf();

    cv::String buffer = oss.str();

    // Step 0-b): Count the number of matrices in the file.
    int nb_images = 0;

    size_t init = buffer.find("array");

    while(init != cv::String::npos)
    {
        nb_images++;

        init = buffer.find("array",init + 5);
    }

    std::vector<cv::Mat> images;

    images.resize(nb_images);


    init = 0;

    // For each matrix... (there is prior that each matrix represent an image. This prior may be wrong.)
    for(int i=0;i<nb_images;i++)
    {

        // Step 0-c): Copy the data of the current matrix to a local buffer.

        init = buffer.find("array", init);


       // Step 1): From the buffer get the depth of the current matrix.
        cv::Range matrix_position;

        matrix_position.start = static_cast<int>(init)+7;
        matrix_position.end = buffer.find("dtype", static_cast<size_t>(matrix_position.start));

        cv::String strType;

        size_t j = static_cast<size_t>(matrix_position.end + 7);

        if(buffer[j] == '\'')
            j++;

        while(buffer[j]!='\'')
            strType.push_back(buffer[j++]);

        int depth = Strings2Types[strType];

        cv::String data = buffer.substr(static_cast<size_t>(matrix_position.start), static_cast<size_t>(matrix_position.size()) );

       // Step 2): From the duplication of the data for the current matrix count the number of channels.
        int cns = 1;

        init = data.find("[[[");

        if(init != cv::String::npos)
        {
            size_t end = data.find("]",init+3);

            init = data.find(",",init+3);

            while(init != cv::String::npos)
            {
                cns++;

                init = data.find(",",init+1);

                if(init>end)
                    break;
            }
        }

       // Step 3): From the duplication of the data for the current matrix count the number of rows.
        int rows = 0;

        cv::String row_delim = cns == 1 ? "]," : "]],";

        init = data.find(row_delim);

        while(init != cv::String::npos)
        {
            rows++;
            init = data.find(row_delim, init+row_delim.size());
        }

       // Step 4): From the duplication of the data for the current matrix count the number of cols.

        int cols = 0;

        cv::String row_delim_left = cns == 1 ? "[" : "[[";

        init = 0;

        size_t begin = data.find(row_delim_left);
        size_t end = data.find(row_delim, begin+row_delim.size());

        cv::String row_buffer = data.substr(begin, end);

        cv::String col_delim = cns == 1 ? "," : "], [";

        init = row_buffer.find(col_delim);

        while (init != cv::String::npos)
        {
            cols++;
            init = row_buffer.find(col_delim,init + col_delim.size());
        }

        if(cns>1)
            cols++;

       // Step 5): Organize the values un order to be a string of numbers only separate a space character.

        if(cns == 1)
        {
            data.erase(0,2);
            data.erase(data.size() - 3, 2);


        }
        else
        {
            data.erase(0,3);
            data.erase(data.size() - 4, 3);
        }


        init = data.find("[");

        while(init != cv::String::npos)
        {
            data.erase(init,1);
            init = data.find("[");
        }

        init = data.find("]");

        while(init != cv::String::npos)
        {
            data.erase(init,1);
            init = data.find("]");
        }

        init = data.find("\n");

        while(init != cv::String::npos)
        {
            data.erase(init,1);
            init = data.find("\n");
        }

        init = data.find("  ");

        while(init != cv::String::npos)
        {
            data.erase(init,1);
            init = data.find("  ");
        }

        init = data.find(",");

        while(init != cv::String::npos)
        {
            data.erase(init,1);
            init = data.find(",");
        }

       // Step 6): Extract the numbers from the string.

        std::stringstream fromStr2Double;

        fromStr2Double<<data;

        cv::Mat1d numeric_buffer;

        numeric_buffer.reserve(static_cast<size_t>(rows * cols * cns));

        std::copy(std::istream_iterator<double>(fromStr2Double),std::istream_iterator<double>(), std::back_insert_iterator<cv::Mat1d >(numeric_buffer));

        numeric_buffer.reshape(cns, rows).convertTo(images[i],depth);
    }

    // Step 0-d): Set properly the outputs.

    if(_out.isGpuMatVector())
        _out.assign( _out.isVector() ? images : images.front() );
    else
    {
        _out.create(images.size(), 1, CV_8U, -1, true);

        std::vector<cv::cuda::GpuMat>& tmp = _out.getGpuMatVecRef();

        if(tmp.size() != images.size())
            tmp.clear();

        tmp.resize(images.size());

        std::transform(images.begin(), images.end(), tmp.begin(),[](const cv::Mat& src)->cv::cuda::GpuMat{ cv::cuda::GpuMat dst; dst.upload(src); return dst;});
    }
}




} //utils

Hope it helps.