Pickling cv2.KeyPoint causes PicklingError
The problem is that you cannot dump cv2.KeyPoint to a pickle file. I had the same issue, and managed to work around it by essentially serializing and deserializing the keypoints myself before dumping them with Pickle.
So represent every keypoint and its descriptor with a tuple:
temp = (point.pt, point.size, point.angle, point.response, point.octave,
point.class_id, desc)
Append all these points to some list that you then dump with Pickle.
Then when you want to retrieve the data again, load all the data with Pickle:
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]
Create a cv2.KeyPoint from this data using the above code, and you can then use these points to construct a list of features.
I suspect there is a neater way to do this, but the above works fine (and fast) for me. You might have to play around with your data format a bit, as my features are stored in format-specific lists. I tried to present the above using my idea at its generic base. I hope that this may help you.
Part of the issue is cv2.KeyPoint
is a function in python that returns a cv2.KeyPoint
object. Pickle is getting confused because, literally, "<type 'cv2.KeyPoint'>
[is] not the same object as cv2.KeyPoint
". That is, cv2.KeyPoint
is a function object, while the type was cv2.KeyPoint
. Why OpenCV is like that, I can only make guesses at unless I go digging. I have a feeling it has something to do with it being a wrapper around a C/C++ library.
Python does give you the ability to fix this yourself. I found the inspiration on this post about pickling methods of classes.
I actually use this clip of code, highly modified from the original in the post
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)
Key points of note:
- In Python 2, you need to use
copy_reg
instead ofcopyreg
andpoint.pt[0], point.pt[1]
instead of*point.pt
. - You can't directly access the
cv2.KeyPoint
class for some reason, so you make a temporary object and use that. - The
copyreg
patching will use the otherwise problematiccv2.KeyPoint
function as I have specified in the output of_pickle_keypoints
when unpickling, so we don't need to implement an unpickling routine. - And to be nauseatingly complete,
cv2::KeyPoint::KeyPoint
is an overloaded function in C++, but in Python, this isn't exactly a thing. Whereas in the C++, there's a function that takes the point for the first argument, in Python, it would try to interpret that as anint
instead. The*
unrolls the point into two arguments,x
andy
to match the onlyint
argument constructor.
I had been using casper's excellent solution until I realized this was possible.