腌制cv2.KeyPoint引起腌制E

2024-04-16 18:27:41 发布

您现在位置:Python中文网/ 问答频道 /正文

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

#!/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和opencv2.3.1

非常感谢


Tags: pathinimportselfbaseosfilenamecv2
3条回答

类似于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++构造函数,因此允许这种修复/处理。

部分问题是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而不是copyreg,使用point.pt[0], point.pt[1]而不是*point.pt
  • 由于某种原因,您不能直接访问cv2.KeyPoint类,因此您需要创建一个临时对象并使用它。
  • 当取消锁定时,copyreg修补将使用我在_pickle_keypoints输出中指定的其他有问题的cv2.KeyPoint函数,因此我们不需要实现取消锁定例程。
  • 并且令人恶心地完成,^ {CD15}}是C++中的重载函数,但在Python,这不是一回事。而在C++中,有一个函数使用第一个参数的点,在Python中,它会尝试将它解释为^ {CD16}}。*将点展开为两个参数,xy以匹配唯一的int参数构造函数。

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

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

我想有一个更整洁的方法来做这件事,但上面的工作很好(和快速)为我。由于我的功能存储在特定于格式的列表中,因此您可能需要稍微修改一下数据格式。我试图用我的想法在它的一般基础上提出上述观点。我希望这对你有帮助。

相关问题 更多 >