How to read/write a matrix from a persistent XML/YAML file in OpenCV 3 with python?

whn picture whn · May 18, 2017 · Viewed 8.7k times · Source

I've been trying to read and write matrices to persistent file storage (eg. XML) with anaconda's current cv2 (which I believe is actually OpenCV 3.x). I looked at the solutions online for this, and people reference something done like this:

object = cv2.cv.Load(file)
object = cv2.cv.Save(file)

source. This does not work on the current anaconda python cv2. People propose solutions like this yaml example, but I'm confused why so much boiler plate code is needed for this simple functionality, I don't find this an acceptable solution. I want something that is as simple as the old solution.

Answer

whn picture whn · May 18, 2017

I knew how to solve this before I asked this, but the only reason I knew how to solve this is because I was also learning how to do this simultaneosly in C++. How this is done in the most recent update of opencv is not stated at all in the documentation. I could not find anywhere online with a solution to this, so hopefully those of you who don't use C++ can understand how to do this in python with out much effort.

This minimal example should be enough to show you how the process works. Effectively the current python wrapper for opencv looks much more like the c++ version, and you now use cv2.FileStorage directly instead of cv2.cv.Save and cv2.cv.Load.

The python cv2.FileStorage now is its own file handler like it inside C++. In c++ if you wanted to write to a file with FileStorage you would do the following:

cv::FileStorage opencv_file("test.xml", cv::FileStorage::WRITE);
cv::Mat file_matrix;
file_matrix = (cv::Mat_<int>(3, 3) << 1, 2, 3,
                                      3, 4, 6,
                                      7, 8, 9); 
opencv_file << "my_matrix" << file_matrix
opencv_file.release();

And to read you would do the following:

cv::FileStorage opencv_file("test.xml", cv::FileStorage::READ);
cv::Mat file_matrix;
opencv_file["my_matrix"] >> file_matrix;
opencv_file.release();

In python if you want to write you have to do the following

#notice how its almost exactly the same, imagine cv2 is the namespace for cv 
#in C++, only difference is FILE_STORGE_WRITE is exposed directly in cv2
cv_file = cv2.FileStorage("test.xml", cv2.FILE_STORAGE_WRITE)
#creating a random matrix
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("write matrix\n", matrix)
# this corresponds to a key value pair, internally opencv takes your numpy 
# object and transforms it into a matrix just like you would do with << 
# in c++
cv_file.write("my_matrix", matrix)
# note you *release* you don't close() a FileStorage object
cv_file.release()

If you want to then read the matrix it is a bit more contrived.

# just like before we specify an enum flag, but this time it is 
# FILE_STORAGE_READ
cv_file = cv2.FileStorage("test.xml", cv2.FILE_STORAGE_READ)
# for some reason __getattr__ doesn't work for FileStorage object in python
# however in the C++ documentation, getNode, which is also available, 
# does the same thing
#note we also have to specify the type to retrieve other wise we only get a 
# FileNode object back instead of a matrix
matrix = cv_file.getNode("my_matrix").mat()
print("read matrix\n", matrix)
cv_file.release()

The output of the read and write python examples should be:

write matrix
 [[1 2 3]
 [4 5 6]
 [7 8 9]]

read matrix
 [[1 2 3]
 [4 5 6]
 [7 8 9]]

And the XML looks like this:

<?xml version="1.0"?>
<opencv_storage>
<my_matrix type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>i</dt>
  <data>
    1 2 3 4 5 6 7 8 9</data></my_matrix>
</opencv_storage>