2012-04-06 9 views
16

Quiero buscar navegaciones en todas las imágenes en un directorio determinado y guardar sus puntos clave y descripciones para uso futuro. Decidí usar la salmuera como se muestra a continuación:Pickling cv2.KeyPoint causa PicklingError

#!/usr/bin/env python 
import os 
import pickle 
import cv2 

class Frame: 
    def __init__(self, filename): 
    surf = cv2.SURF(500, 4, 2, True) 
    self.filename = filename 
    self.keypoints, self.descriptors = surf.detect(cv2.imread(filename, cv2.CV_LOAD_IMAGE_GRAYSCALE), None, False) 

if __name__ == '__main__': 

    Fdb = open('db.dat', 'wb') 
    base_path = "img/" 
    frame_base = [] 

    for filename in os.listdir(base_path): 
    frame_base.append(Frame(base_path+filename)) 
    print filename 

    pickle.dump(frame_base,Fdb,-1) 

    Fdb.close() 

Cuando intento ejecutar, consigo un error de seguimiento:

File "src/pickle_test.py", line 23, in <module> 
    pickle.dump(frame_base,Fdb,-1) 
... 
pickle.PicklingError: Can't pickle <type 'cv2.KeyPoint'>: it's not the same object as cv2.KeyPoint 

¿Alguien sabe, ¿qué significa y cómo solucionarlo? Estoy usando Python 2.6 y 2.3.1 OPENCV

Gracias mucho

Respuesta

14

El problema es que no se puede volcar cv2.KeyPoint a un archivo de salmuera. Tuve el mismo problema y me las ingenié para serializar y deserializar los puntos clave por mi cuenta antes de deshacerme de ellos con Pickle.

Así que representan cada punto clave y su descriptor con una tupla:

temp = (point.pt, point.size, point.angle, point.response, point.octave, 
     point.class_id, desc)  

Anexar todos estos puntos a alguna lista que a continuación volcar con salmuera.

Luego, cuando se desea recuperar los datos de nuevo, cargar todos los datos con la salmuera:

temp_feature = cv2.KeyPoint(x=point[0][0],y=point[0][1],_size=point[1], _angle=point[2], 
          _response=point[3], _octave=point[4], _class_id=point[5]) 
temp_descriptor = point[6] 

Crear una cv2.KeyPoint partir de estos datos utilizando el código de arriba, y luego se puede utilizar estos puntos para construir una lista de características

Sospecho que hay una manera más ordenada de hacer esto, pero lo anterior funciona bien (y rápido) para mí. Es posible que tenga que jugar un poco con su formato de datos, ya que mis funciones se almacenan en listas de formatos específicos. Intenté presentar lo anterior utilizando mi idea en su base genérica. Espero que esto te pueda ayudar.

0

Parte del problema es cv2.KeyPoint es una función en python que devuelve un objeto cv2.KeyPoint. Pickle se confunde porque, literalmente, "<type 'cv2.KeyPoint'> [es] not the same object as cv2.KeyPoint". Es decir, cv2.KeyPoint es un objeto de función, mientras que el tipo era cv2.KeyPoint. Por qué OpenCV es así, solo puedo hacer conjeturas a menos que vaya a cavar. Tengo la sensación de que tiene algo que ver con que es una envoltura alrededor de una biblioteca C/C++.

Python le ofrece la posibilidad de solucionarlo usted mismo. I found the inspiration on this post about pickling methods of classes.

realidad utilizo este clip de código, altamente modificada de la original en el puesto

import copyreg 
import cv2 

def _pickle_keypoints(point): 
    return cv2.KeyPoint, (*point.pt, point.size, point.angle, 
          point.response, point.octave, point.class_id) 

copyreg.pickle(cv2.KeyPoint().__class__, _pickle_keypoints) 

puntos clave de la nota:

  • En Python 2, es necesario utilizar copy_reg en lugar de copyreg .
  • No puede acceder directamente a la clase cv2.KeyPoint por algún motivo, por lo que crea un objeto temporal y lo usa.
  • El parche copyreg utilizará la función cv2.KeyPoint de otro modo problemática que he especificado en la salida de _pickle_keypoints al desatornillar, por lo que no es necesario implementar una rutina de desatornillamiento.
  • Y para estar completamente nauseabundo, cv2::KeyPoint::KeyPoint es una función sobrecargada en C++, pero en Python, esto no es exactamente una cosa. Mientras que en C++, hay una función que toma el punto para el primer argumento, en Python intentaría interpretar eso como int. El * desenrolla el punto en dos argumentos, x y y para que coincida con el único int constructor de argumentos.

He estado usando casper's excellent solution hasta que me di cuenta de que esto era posible.