pythonxmlopencvpersistenceopencv3.0

How can I read/write a matrix from a persistent XML/YAML file in OpenCV 3 with Python?


I've been trying to read and write matrices to persistent file storage (for example, 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.


Solution

  • How this is done in the most recent update of opencv is not stated at all in the documentation.

    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 is 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 it’s almost exactly the same. Imagine cv2 is the namespace for cv.
    # In C++, the 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, and 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, otherwise 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>