How to write a Float Mat to a file in OpenCV
Asked Answered
S

6

37

I have a matrix,

Mat B(480,640,CV_32FC1);

containing floating-point values. I want to write this matrix to a file which could be opened in Notepad or Microsoft Word or Excel to see the values inside and for storage, but the imwrite function can save 8-bit or 16-bit images only...

Could this be done? If yes, how?

Scissile answered 1/5, 2013 at 4:52 Comment(2)
you can [write the matrix in a xml or yaml file][1]. [1]: #15115546Guncotton
see best answer IMHOKlinges
N
63

Using pure OpenCV API calls:

// Declare what you need
cv::FileStorage file("some_name.ext", cv::FileStorage::WRITE);
cv::Mat someMatrixOfAnyType;

// Write to file!
file << "matName" << someMatrixOfAnyType;

// Close the file and release all the memory buffers
file.release();

The file extension can be xml or yml. In both cases you get a small header that you can easily remove/parse, then you have access to the data in a floating point format. I used this approach successfully (with .yml files) to get data into MATLAB and Matplotlib.

To get the data:

  1. open the file with any editor
  2. then suppress all the text and numbers, except the content of the data tag (i.e., the pixel values).
  3. When done, save your file with a .txt or .csv extension and open it with MATLAB (drag-and-drop works).

Voilà. You may have to reshape the resulting matrix on the MATLAB command line if it didn't guess the image size correctly.

Nealson answered 1/5, 2013 at 6:55 Comment(11)
Thank you... I can open the file using gedit ..Can you tell me how to load this xml file in Matlab or Excel???..Scissile
I think you need to give the matrix a name, like file << "matName" << someMatrixOfAnyType; otherwise I get this error: OpenCV Error: Unspecified error (No element name has been given) in operator<<, file /usr/local/include/opencv2/core/operations.hpp, line 2910Dias
Yes you're right. Without a name the yml file is bad formed (missing markup before the data).Nealson
This should be followed by file.release(). As a general rule of thumb, It's good to close and release resources for any file IO.Upanishad
@dev_nut: FileStorage destructor calls release(), no need to call it explicitly.Merciful
Following saving to file, how do you load a saved matrix?Karylkarylin
Found this: #19201344Karylkarylin
opencv also support jsonEngels
Hi, what's the python version to do it? could you give some clue?Enlighten
Need to call file.release() explicitly. Otherwise, it won't save the actual dataClifford
Can you respond to the call for adding file.release(), please?Antipyrine
W
18

You can write cv::Mat to text file using simple C++ file handling.

Here is how you can do it:

#include <iostream>
#include <fstream>

using namespace std;

void writeMatToFile(cv::Mat& m, const char* filename)
{
    ofstream fout(filename);

    if(!fout)
    {
        cout<<"File Not Opened"<<endl;  return;
    }

    for(int i=0; i<m.rows; i++)
    {
        for(int j=0; j<m.cols; j++)
        {
            fout<<m.at<float>(i,j)<<"\t";
        }
        fout<<endl;
    }

    fout.close();
}

int main()
{
    cv::Mat m = cv::Mat::eye(5,5,CV_32FC1);

    const char* filename = "output.txt";

    writeMatToFile(m,filename);

}
Woke answered 1/5, 2013 at 6:57 Comment(6)
Want to accept this answer too... But only 1 can be given... anyway this simple approach also works great !1Scissile
@smttsp I don't think so. OpenCV implementation may be optimized.Woke
Thanks, I suspected of if compiler optimizations could make both approaches have the same speed.Renfroe
I have checked both answer and yours is 50-60% more slower than the other answer for an 1024x1024 double matrice to be written to xml file.Renfroe
@smttsp... Yes of course, my solution is just a basic approach. While OpenCV's implementation is far more optimized.Woke
@sgarizvi, your solution is more helpful for batch processing as compared to the accepted answer. If we need to write 100s of cv::mat objects to files, it would be hard to open and modify each xml/yml file as suggested by accepted answer unless we again write some code to automatically modify.Customable
D
5

OpenCV can serialize (save) its objects in JSON, XML or YAML formats. You can use any editor, which understand these formats, in order to read these files, or use OpenCV to download data (de-serialize) from these files. A detailed explanation of how this is done can be found here. In short, to store the data into an XML file, you have to call

cv::FileStorage fs("/path/to/file.xml", cv::FileStorage::WRITE); // Create FileStorage object
cv::Mat cameraM; // Matrix, which you need to save, do not forget to fill it with some data
fs << "cameraMatrix" << cameraM; // Command to save the data
fs.release(); // Releasing the file.

If you want to use JSON or YAML, just change the extension to .json or .yaml/.yml - OpenCV will automatically understand your intentions.

The important thing is the command

fs << "cameraMatrix" << cameraM;

the string "cameraMatrix" is the tag name, under which this matrix will be stored and using which this matrix can be found later in the file.

Note that xml format will not allow you to use tag names with spaces and some special characters, since only alphanumeric values, dots, dashes and underscores are allowed (see XML specification for details), while in YAML and JSON you can have something like

fs << "Camera Matrix" << cameraM;
Decrement answered 7/11, 2018 at 16:15 Comment(0)
R
3

I wrote this code to serialize the matrix, including sample code below.

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

/*
Will save in the file:
cols\n
rows\n
elemSize\n
type\n
DATA
*/
void serializeMatbin(cv::Mat& mat, std::string filename) {
    if (!mat.isContinuous()) {
        std::cout << "Not implemented yet" << std::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, /* how many elements */ 4, /* size of each element */ sizeof(int), /* file */ FP);
    fwrite(mat.data, mat.cols * mat.rows, elemSizeInBytes, FP);
    fclose(FP);
}

cv::Mat deserializeMatbin(std::string filename) {
    FILE* fp = fopen(filename.c_str(), "rb");
    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];

    //std::cout << "rows=" << rows << " cols=" << cols << " elemSizeInBytes=" << elemSizeInBytes << std::endl;

    cv::Mat outputMat = cv::Mat::ones(rows, cols, elemType);

    size_t result = fread(outputMat.data, elemSizeInBytes, (size_t)(cols * rows), fp);

    if (result != (size_t)(cols * rows)) {
        fputs ("Reading error", stderr);
    }

    std::cout << ((float*)outputMat.data)[200] << std::endl;
    fclose(fp);
    return outputMat;
}

void testSerializeMatbin() {
    cv::Mat a = cv::Mat::ones(/*cols*/ 10, /* rows */ 5, CV_32F) * -2;
    std::string filename = "test.matbin";
    serializeMatbin(a, filename);
    cv::Mat b = deserializeMatbin(filename);
    std::cout << "Rows: " << b.rows << " Cols: " << b.cols << " type: " << b.type() << std::endl;
}
Rove answered 9/8, 2018 at 10:3 Comment(1)
An explanation would be in order. E.g., what is the idea/gist? What is some example output? Why does it include deserialisation when only output is called for? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).Antipyrine
E
2

Use write binary:

FILE* FP = fopen("D.bin", "wb");
int sizeImg[2] = { D.cols, D.rows };
fwrite(sizeImg, 2, sizeof(int), FP);
fwrite(D.data, D.cols * D.rows, sizeof(float), FP);
fclose(FP);

Then you can read it in MATLAB.

Read the size and then reshape (type=single):

fp = fopen(fname);
data = fread(fp, 2, 'int');
width = data(1); height = data(2);
B = fread(fp, Inf, type);

imageOut = reshape(B, [width, height])';

fclose(fp);
Edy answered 11/5, 2017 at 7:44 Comment(0)
A
0

Starting from OpenCV 3.4.4, imwrite can serialize CV_32F images in TIFF format. While slower than 'raw' codes, it ensures that the file is portable across all architectures.

Alfonse answered 7/1, 2022 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.