Травление cv2.KeyPoint вызывает PicklingError
Я хочу искать серфики во всех изображениях в данном каталоге и сохранять их ключевые точки и дескрипторы для будущего использования. Я решил использовать рассол, как показано ниже:
#!/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 not the same object as cv2.KeyPoint
Кто-нибудь знает, что это значит и как его исправить? Я использую Python 2.6 и Opencv 2.3.1
Большое спасибо
Ответы
Ответ 1
Проблема заключается в том, что вы не можете сбрасывать 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 из этих данных, используя приведенный выше код, и затем вы можете использовать эти точки для создания списка функций.
Я подозреваю, что есть более аккуратный способ сделать это, но это работает отлично (и быстро) для меня. Возможно, вам придется немного поиграть со своим форматом данных, так как мои функции хранятся в списках формата. Я попытался представить выше, используя мою идею на своей общей основе. Я надеюсь, что это может вам помочь.
Ответ 2
Частью проблемы является cv2.KeyPoint
- это функция в Python, которая возвращает объект cv2.KeyPoint
. Pickle запутывается, потому что буквально " <type 'cv2.KeyPoint'>
[is] not the same object as cv2.KeyPoint
". То есть cv2.KeyPoint
является функциональным объектом, тогда как тип был cv2.KeyPoint
. Почему OpenCV такой, я могу только догадываться, если я не буду копать. У меня есть ощущение, что это как-то связано с тем, что он является оберткой вокруг библиотеки C/C++.
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
какой-то причине вы не можете напрямую получить доступ к классу cv2.KeyPoint
, поэтому вы создаете временный объект и используете его. - В
copyreg
будет использоваться проблемная функция cv2.KeyPoint
как я указал в выводе _pickle_keypoints
при снятии засечек, поэтому нам не нужно реализовывать подпрограмму расщепления. - И чтобы быть тошнотворно завершенным,
cv2::KeyPoint::KeyPoint
- перегруженная функция в C++, но в Python это не совсем то, что нужно. В то время как в C++ есть функция, которая берет точку для первого аргумента, в Python она будет пытаться вместо этого интерпретировать это как int
. *
Разворачивает точку в два аргумента, x
и y
чтобы соответствовать единственному конструктору аргумента int
.
Я использовал отличное решение Каспера, пока не понял, что это возможно.
Ответ 3
Решение, аналогичное предоставленному Poik. Просто позвоните один раз, прежде чем засолить.
def patch_Keypoint_pickiling(self):
# Create the bundling between class and arguements to save for Keypoint class
# See : https://stackoverflow.com/info/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)
Больше, чем для кода, это для невероятно четкого объяснения, доступного там: fooobar.com/info/15684049/...
Обратите внимание, что если вы хотите распространить эту идею на другой "непикабельный" класс openCV, вам нужно всего лишь создать функцию, аналогичную "_pickle_keypoint". Убедитесь, что вы храните атрибуты в том же порядке, что и конструктор. Вы можете скопировать конструктор C++, даже в Python, как я сделал. Большинство конструкторов C++ и Python, похоже, не сильно отличаются.
У меня есть проблема с кортежем "pt". Тем не менее, конструктор C++ существует для координат, разделенных X и Y, и, следовательно, допускает это исправление/обходной путь.