opencv Pickling cv2.KeyPoint导致PicklingError

eeq64g8w  于 2023-10-24  发布在  其他
关注(0)|答案(4)|浏览(119)

我想在给定目录下的所有图像中搜索冲浪,并保存它们的关键点和描述符以备将来使用。我决定使用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
多谢了

xuo3flqw

xuo3flqw1#

问题是你不能将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,然后你可以使用这些点来构造一个特征列表。
我怀疑有一种更简洁的方法来做到这一点,但上面的工作对我来说很好(而且很快)。你可能需要稍微摆弄一下你的数据格式,因为我的功能存储在特定格式的列表中。我试图在其通用基础上使用我的想法来呈现上面的内容。我希望这可以帮助你。

qybjjes1

qybjjes12#

问题的一部分是cv2.KeyPoint是python中的一个函数,它返回一个cv2.KeyPoint对象。Pickle感到困惑,因为从字面上看,“<type 'cv2.KeyPoint'> [是] not the same object as cv2.KeyPoint“。也就是说,cv2.KeyPoint是一个函数对象,而类型是cv2.KeyPoint。为什么OpenCV是这样的,我只能猜测,除非我去挖掘。我有一种感觉,这与它是一个C/C++库的 Package 器有关。
Python确实给了你给予自己修复这个问题的能力。
我实际上使用了这段代码,在帖子中对原始代码进行了高度修改

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而不是copyreg,使用point.pt[0], point.pt[1]而不是*point.pt
  • 由于某种原因,您不能直接访问cv2.KeyPoint类,因此您创建一个临时对象并使用它。
  • copyreg补丁程序将使用我在_pickle_keypoints的输出中指定的有问题的cv2.KeyPoint函数,所以我们不需要实现一个unpickle例程。
  • 简单来说,cv2::KeyPoint::KeyPoint在C中是一个重载函数,但在Python中,这并不完全是一个东西。而在C中,有一个函数将第一个参数作为点,在Python中,它会尝试将其解释为int*将点展开为两个参数,xy匹配唯一的int参数构造函数。

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

2jcobegt

2jcobegt3#

一个类似于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”元组有问题。但是,C++构造函数存在X和Y分隔坐标,因此,允许此修复/解决方案。

kqqjbcuj

kqqjbcuj4#

在我的实现中,我将Keypoints存储在我的一个类中,这会阻止整个类正确地pickle。扩展@hjweide answer,像这样解构和重构关键点对我来说很有效:

class FeatureMeasurement:
    def __init__(self, points, keypoints, descriptors, image=None, Q=None):
       
       ...

       # Destructure the keypoints object and store the data in a list so we can pickle it
       self._keypoints = [[p.pt, p.size, p.angle, p.response, p.octave, p.class_id] for p in keypoints]

   @property
   def keypoints(self):
       return [cv2.KeyPoint(x[0][0], x[0][1], x[1], x[2], x[3], x[4], x[5]) for x in self._keypoints]

这让我可以很好地腌制,同时保持我的界面不变。

相关问题