21

I'm currently using FileStorage class for storing matrices XML/YAML using OpenCV C++ API.

However, I have to write a Python Script that reads those XML/YAML files.

I'm looking for existing OpenCV Python API that can read the XML/YAML files generated by OpenCV C++ API

1

6 Answers 6

23

You can use PyYAML to parse the YAML file.

Since PyYAML doesn't understand OpenCV data types, you need to specify a constructor for each OpenCV data type that you are trying to load. For example:

import yaml
def opencv_matrix(loader, node):
    mapping = loader.construct_mapping(node, deep=True)
    mat = np.array(mapping["data"])
    mat.resize(mapping["rows"], mapping["cols"])
    return mat
yaml.add_constructor(u"tag:yaml.org,2002:opencv-matrix", opencv_matrix)

Once you've done that, loading the yaml file is simple:

with open(file_name) as fin:
    result = yaml.load(fin.read())

Result will be a dict, where the keys are the names of whatever you saved in the YAML.

Sign up to request clarification or add additional context in comments.

5 Comments

@misha do you know why it gives this warning? comparison to 'None' will result in an elementwise object comparison in the future. if data in [None, ()]:
Add @vishal's advice about stripping the first line, and this answer would be perfect. Otherwise, python won't be able to read the file.
Also, set the data-type. Fortunately, the numpy data-type codes are the same as OpenCV's, at least for simple types like float and double.
This didn't work for me. I write .yml with c++ and read them as above. The resulting numpy array isn't the same as the original (multidimensional) cv::Mat
Please view @TimSC's answer for the correct answer - the question specifically asks for the Python OpenCV API, this answer gives a convoluted PyYAML way of doing it.
9

Using the FileStorage functions available in OpenCV 3.2, I've used this with success:

import cv2
fs = cv2.FileStorage("calibration.xml", cv2.FILE_STORAGE_READ)
fn = fs.getNode("Camera_Matrix")
print (fn.mat())

1 Comment

Do you know how to get list of all available nodes?
8

In addition to @misha's response, OpenCV YAML's are somewhat incompatible with Python.

Few reasons for incompatibility are:

  1. Yaml created by OpenCV doesn't have a space after ":". Whereas Python requires it. [Ex: It should be a: 2, and not a:2 for Python]
  2. First line of YAML file created by OpenCV is wrong. Either convert "%YAML:1.0" to "%YAML 1.0". Or skip the first line while reading.

The following function takes care of providing that:

import yaml
import re
def readYAMLFile(fileName):
    ret = {}
    skip_lines=1    # Skip the first line which says "%YAML:1.0". Or replace it with "%YAML 1.0"
    with open(scoreFileName) as fin:
        for i in range(skip_lines):
            fin.readline()
        yamlFileOut = fin.read()
        myRe = re.compile(r":([^ ])")   # Add space after ":", if it doesn't exist. Python yaml requirement
        yamlFileOut = myRe.sub(r': \1', yamlFileOut)
        ret = yaml.load(yamlFileOut)
    return ret

outDict = readYAMLFile("file.yaml")

NOTE: Above response is applicable only for yaml's. XML's have their own share of problems, something I haven't explored completely.

4 Comments

Helpful tip. I tried change the line to "YAML 1.0" though, and it doesn't work. So you do have to strip the first line.
@orodbhen: Had you put the % before YAML?
Yes. Sorry, that was just a typo in my comment. With %YAML 1.0 I get a ParseError. This is with PyYAML 3.12 for Python 3.6.
Please view @TimSC's answer for the correct answer - the question specifically asks for the Python OpenCV API, which works just fine with the C++ generated YAMLs.
4

I wrote a small snippet to read and write FileStorage-compatible YAMLs in Python:

# A yaml constructor is for loading from a yaml node.
# This is taken from @misha 's answer: http://stackoverflow.com/a/15942429
def opencv_matrix_constructor(loader, node):
    mapping = loader.construct_mapping(node, deep=True)
    mat = np.array(mapping["data"])
    mat.resize(mapping["rows"], mapping["cols"])
    return mat
yaml.add_constructor(u"tag:yaml.org,2002:opencv-matrix", opencv_matrix_constructor)

# A yaml representer is for dumping structs into a yaml node.
# So for an opencv_matrix type (to be compatible with c++'s FileStorage) we save the rows, cols, type and flattened-data
def opencv_matrix_representer(dumper, mat):
    mapping = {'rows': mat.shape[0], 'cols': mat.shape[1], 'dt': 'd', 'data': mat.reshape(-1).tolist()}
    return dumper.represent_mapping(u"tag:yaml.org,2002:opencv-matrix", mapping)
yaml.add_representer(np.ndarray, opencv_matrix_representer)

#examples 

with open('output.yaml', 'w') as f:
    yaml.dump({"a matrix": np.zeros((10,10)), "another_one": np.zeros((2,4))}, f)

with open('output.yaml', 'r') as f:
    print yaml.load(f)

1 Comment

This does not work anymore : File "/usr/lib/python2.7/dist-packages/yaml/representer.py", line 142, in ignore_aliases if data in [None, ()]: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
1

To improve on the previous answer by @Roy_Shilkrot I added support for numpy vectors as well as matrices:

# A yaml constructor is for loading from a yaml node.
# This is taken from @misha 's answer: http://stackoverflow.com/a/15942429
def opencv_matrix_constructor(loader, node):
    mapping = loader.construct_mapping(node, deep=True)
    mat = np.array(mapping["data"])
    if mapping["cols"] > 1:
        mat.resize(mapping["rows"], mapping["cols"])
    else:
        mat.resize(mapping["rows"], )
    return mat
yaml.add_constructor(u"tag:yaml.org,2002:opencv-matrix", opencv_matrix_constructor)


# A yaml representer is for dumping structs into a yaml node.
# So for an opencv_matrix type (to be compatible with c++'s FileStorage) we save the rows, cols, type and flattened-data
def opencv_matrix_representer(dumper, mat):
    if mat.ndim > 1:
        mapping = {'rows': mat.shape[0], 'cols': mat.shape[1], 'dt': 'd', 'data': mat.reshape(-1).tolist()}
    else:
        mapping = {'rows': mat.shape[0], 'cols': 1, 'dt': 'd', 'data': mat.tolist()}
    return dumper.represent_mapping(u"tag:yaml.org,2002:opencv-matrix", mapping)
yaml.add_representer(np.ndarray, opencv_matrix_representer)

Example:

with open('output.yaml', 'w') as f:
    yaml.dump({"a matrix": np.zeros((10,10)), "another_one": np.zeros((5,))}, f)

with open('output.yaml', 'r') as f:
    print yaml.load(f)

Output:

a matrix: !!opencv-matrix
  cols: 10
  data: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0]
  dt: d
  rows: 10
another_one: !!opencv-matrix
  cols: 1
  data: [0.0, 0.0, 0.0, 0.0, 0.0]
  dt: d
  rows: 5

Though I could not control the order of rows, cols, dt, data.

Comments

0

pip install opencv-contrib-python for video support to install specific version use pip install opencv-contrib-python

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.