酸洗cv2.KeyPoint会导致PicklingError

时间:2012-04-06 15:07:11

标签: python opencv

我想在给定目录中的所有图像中搜索冲浪并保存其关键点和描述符以供将来使用。我决定使用如下所示的泡菜:

#!/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()

当我尝试执行时,出现以下错误:

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

有谁知道,它是什么意思以及如何解决它?我使用的是Python 2.6和Opencv 2.3.1

非常感谢

3 个答案:

答案 0 :(得分:18)

问题是您无法将cv2.KeyPoint转储到pickle文件。我遇到了同样的问题,并且在用Pickle转储它们之前,通过自己对关键点进行基本序列化和反序列化来设法解决它。

因此用元组表示每个关键点及其描述符:

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

将所有这些点附加到您随后使用Pickle转储的列表中。

然后,当您想再次检索数据时,使用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]

使用上面的代码从这些数据创建一个cv2.KeyPoint,然后您可以使用这些点来构建一个功能列表。

我怀疑有一种更简洁的方法可以做到这一点,但上面的工作对我来说很好(和快)。您可能需要稍微调整一下数据格式,因为我的功能存储在特定于格式的列表中。我尝试在其通用基础上使用我的想法来呈现上述内容。我希望这对你有帮助。

答案 1 :(得分:8)

部分问题是cv2.KeyPoint是python中的一个返回cv2.KeyPoint对象的函数。 Pickle感到困惑,因为字面意思是“<type 'cv2.KeyPoint'> [是] not the same object as cv2.KeyPoint”。也就是说,cv2.KeyPoint是一个函数对象,而类型是cv2.KeyPoint。为什么OpenCV就是这样,除非我去挖掘,否则我只能猜测。我觉得它与C / C ++库的包装有关。

Python确实让你能够自己解决这个问题。 I found the inspiration on this post about pickling methods of classes

我实际上使用了这段代码,在帖子

中经过了大量修改
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)

要点:

  • 在Python 2中,您需要使用copy_reg代替copyregpoint.pt[0], point.pt[1]而不是*point.pt
  • 由于某种原因,您无法直接访问cv2.KeyPoint类,因此您创建了一个临时对象并使用它。
  • copyreg修补将使用我在cv2.KeyPoint输出中指定的有问题的_pickle_keypoints函数,因此我们不需要实现unpickling例程。< / LI>
  • 令人作呕的是,cv2::KeyPoint::KeyPoint是C ++中的一个重载函数,但在Python中,这并不完全是一个问题。而在C ++中,有一个函数可以解释第一个参数,在Python中,它会尝试将其解释为int*将该点展开为两个参数xy,以匹配唯一的int参数构造函数。

我一直在使用casper's excellent solution,直到我意识到这是可能的。

答案 2 :(得分:0)

与Poik提供的解决方案类似的解决方案。腌制前只需拨打一次即可。

def patch_Keypoint_pickiling(self):
    # Create the bundling between class and arguements to save for Keypoint class
    # See : https://stackoverflow.com/questions/50337569/pickle-exception-for-cv2-boost-when-using-multiprocessing/50394788#50394788
    def _pickle_keypoint(keypoint): #  : cv2.KeyPoint
        return cv2.KeyPoint, (
            keypoint.pt[0],
            keypoint.pt[1],
            keypoint.size,
            keypoint.angle,
            keypoint.response,
            keypoint.octave,
            keypoint.class_id,
        )
    # C++ Constructor, notice order of arguments : 
    # KeyPoint (float x, float y, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1)

    # Apply the bundling to pickle
    copyreg.pickle(cv2.KeyPoint().__class__, _pickle_keypoint)

除了提供代码外,还提供了令人难以置信的清晰解释:https://stackoverflow.com/a/50394788/11094914

请注意,如果您想将此思想扩展到其他openCV的“不可挑选的”类,则只需要构建与“ _pickle_keypoint”类似的功能即可。确保以与构造函数相同的顺序存储属性。您可以考虑像我一样复制C ++构造函数,即使是在Python中也是如此。大多数情况下,C ++和Python构造函数似乎并没有太大差异。

“ pt”元组有问题。但是,对于X和Y分隔的坐标,存在C ++构造函数,因此,可以使用此修复程序/解决方法。