【问题标题】:Pickling cv2.KeyPoint causes PicklingErrorPickling cv2.KeyPoint 导致 PicklingError
【发布时间】:2012-04-20 04:22:39
【问题描述】:

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

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

非常感谢

【问题讨论】:

标签: python opencv


【解决方案1】:

与 Poik 提供的解决方案类似。只需在酸洗前调用一次。

def patch_Keypoint_pickiling(self):
    # Create the bundling between class and arguments 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++ 构造函数,因此,允许此修复/解决方法。

【讨论】:

    【解决方案2】:

    部分问题是cv2.KeyPoint 是python 中的一个函数,它返回一个cv2.KeyPoint 对象。 Pickle 感到困惑,因为从字面上看,“&lt;type 'cv2.KeyPoint'&gt; [is] 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 时,我们不需要实现 unpickling 例程。
    • 说得令人作呕,cv2::KeyPoint::KeyPoint 在 C++ 中是一个重载函数,但在 Python 中,这并不完全是一回事。而在 C++ 中,有一个函数将点作为第一个参数,而在 Python 中,它会尝试将其解释为 int* 将该点展开为两个参数,xy 以匹配唯一的 int 参数构造函数。

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

    【讨论】:

    • 不应该*point.ptpoint.pt 吗?另外,我在泡菜的load_reduce 函数中得到了TypeError: a float is required。有没有办法解决这个问题?
    • @sha1 应该是*point.pt 把它分成x和y。 cv2.Keypoint 的构造函数需要这样做,因此您会遇到 TypeError。或者,使用point.pt[0], point.pt[1] 以每点速度稍慢为代价,并且能够在仍然需要 python 2 的遗留代码中使用它。
    • 这是最好的答案。从字面上看!
    【解决方案3】:

    问题是您不能将 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,然后你可以使用这些点来构造一个特征列表。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-23
      • 2021-01-04
      • 1970-01-01
      • 2017-01-15
      • 2016-12-12
      • 2021-07-19
      相关资源
      最近更新 更多