2012-06-21 19 views
18

Actualmente estoy usando FileStorage clase para almacenar matrices XML/YAML usando OpenCV C++ API.FileStorage para OpenCV Python API

Sin embargo, tengo que escribir un script de Python que lea esos archivos XML/YAML.

Busco OpenCV Python API que puede leer los XML/YAML archivos generados por OpenCV C++ API

+2

en la última versión (3.1.0 después) de Python para FileStorage/FileNode se han añadido: https://github.com/Itseez/opencv/pull/6482 –

Respuesta

22

Puede utilizar PyYAML para analizar el archivo YAML existente.

Dado que PyYAML no comprende los tipos de datos OpenCV, debe especificar un constructor para cada tipo de datos OpenCV que está intentando cargar. Por ejemplo:

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) 

Una vez que hayas hecho esto, se carga el archivo YAML es simple:

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

resultado será un diccionario, donde las claves son los nombres de todo lo que ha guardado en el YAML .

+0

+1 - eso es genial. –

+0

@misha ¿sabes por qué te da esta advertencia? 'comparación con 'Ninguno' dará como resultado una comparación de objeto elemento en el futuro. si los datos en [Ninguno,()]: ' – Yamaneko

+0

Agregue el consejo de @vishal sobre quitar la primera línea, y esta respuesta sería perfecta. De lo contrario, Python no podrá leer el archivo. – orodbhen

8

Además de la respuesta de @ misha, los YAML de OpenCV son algo incompatibles con Python.

algunas de las razones de incompatibilidad son:

  1. Yaml creado por OpenCV no tiene un espacio después de ":". Mientras que Python lo requiere. [Ejemplo: debe ser a: 2, y no a:2 para Python]
  2. La primera línea del archivo YAML creado por OpenCV es incorrecta. O convierta "% YAML: 1.0" a "% YAML 1.0". O omita la primera línea mientras lee.

La siguiente función se encarga de proporcionar lo siguiente:

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") 

NOTA: En la respuesta es aplicable sólo para los de YAML. Los XML tienen sus propios problemas, algo que no he explorado por completo.

+0

Consejo útil. Intenté cambiar la línea a "YAML 1.0" sin embargo, y no funciona. Entonces tienes que quitar la primera línea. – orodbhen

4

escribí un pequeño fragmento de leer y escribir YAMLs compatibles con FileStorage en 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

mejorar en la respuesta anterior por @Roy_Shilkrot he añadido soporte para vectores numpy así como 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) 

Ejemplo:

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) 

de salida:

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 

Aunque no pude controlar el orden de las filas, cols, dt, data.

3

Uso de las funciones disponibles en FileStorage OpenCV 3.2, he utilizado esto con éxito:

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

¿Sabes cómo obtener la lista de todos los nodos disponibles? – mkuse