在 Python 中将 OpenCV SURF 特征转换为 float32 数组

Converting OpenCV SURF features to float32 arrays in Python

我使用 compute() 函数提取特征并将它们添加到列表中。然后,我尝试使用 NumPy 将所有特征转换为 float32,以便它们可以与 OpenCV 一起用于分类。我得到的错误是:

ValueError: setting an array element with a sequence. 

不太确定我能做些什么。我正在看一本书并执行相同的步骤,除了他们使用 HOS 来提取功能。我正在提取特征并取回大小不一致的矩阵,但不确定如何使它们都相等。相关代码(可能有轻微的语法错误,因为我从原始代码中截断了它):

    def get_SURF_feature_vector(area_of_interest, surf):
            # Detect the key points in the image
            key_points = surf.detect(area_of_interest);
            # Create array of zeros with the same shape and type as a given array
            image_key_points = np.zeros_like(area_of_interest);
            # Draw key points on the image
            image_key_points = cv2.drawKeypoints(area_of_interest, key_points, image_key_points, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
            # Create feature discriptors
            key_points, feature_descriptors = surf.compute(area_of_interest, key_points);
            # Plot Image and descriptors
            # plt.imshow(image_key_points);
            # Return computed feature description matrix
            return feature_descriptors;

    for x in range(0, len(data)):
            feature_list.append(get_SURF_feature_vector(area_of_interest[x], surf));
list_of_features = np.array(list_of_features, dtype = np.float32);

错误根本不是 OpenCV 特有的,只是 numpy。

您的列表 feature_list 包含不同长度的数组。您不能用不同大小的数组制作二维数组。

例如您可以非常简单地重现错误:

>>> np.array([[1], [2, 3]], dtype=np.float32)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: setting an array element with a sequence.

我假设您对操作的期望是输入 [1], [1, 2] 并返回 np.array([1, 2, 3]),即连接(实际上这不是 OP 想要的,请参阅下面的评论这个post)。您可以使用 np.hstack()np.vstack() 进行这些操作,具体取决于您输入的形状。您也可以将 np.concatenate()axis 参数一起使用,但堆叠操作对于 2D/3D 数组更为明确。

>>> a = np.array([1], dtype=np.float32)
>>> b = np.array([2, 3, 4], dtype=np.float32)
>>> np.hstack([a, b])
array([1., 2., 3., 4.], dtype=float32)

描述符是垂直排列的,所以它们应该垂直堆叠,而不是像上面那样水平排列。因此你可以简单地做:

list_of_features = np.vstack(list_of_features)

您不需要指定 dtype=np.float32,因为默认情况下描述符是 np.float32(此外,vstack 没有 dtype 参数,因此您d必须在堆叠操作后转换它)。


如果您想要一个 3D 阵列,那么您需要在所有图像中使用相同数量的特征,以便它是一个均匀填充的 3D 阵列。您可以只用占位符值填充特征向量,例如 0s 或 np.nan,这样它们的长度都相同,然后您可以像最初那样将它们组合在一起。

>>> des1 = np.random.rand(500, 64).astype(np.float32)
>>> des2 = np.random.rand(200, 64).astype(np.float32)
>>> des3 = np.random.rand(400, 64).astype(np.float32)
>>> feature_descriptors = [des1, des2, des3]

所以这里每个图像的特征描述符都有不同数量的特征。你可以找到最大的一个:

>>> max_des_length = max([len(d) for d in feature_descriptors])
>>> max_des_length
500

您可以使用 np.pad() 用更多的值填充每个特征数组,但它需要与最大大小描述符集的大小相同。

现在在一行中完成所有操作有点不必要,但无论如何。

>>> feature_descriptors = [np.pad(d, ((0, (max_des_length - len(d))), (0, 0)), 'constant', constant_values=np.nan) for d in feature_descriptors]

这里烦人的论点((0, (max_des_length - len(d))), (0, 0))只是说在顶部填充0个元素,在底部填充max_des_length - len(des)个元素,在左侧填充0个元素,在右侧填充0个元素。

正如您在此处看到的,我正在向数组添加 np.nan 值。如果您省略了 constant_values 参数,它默认为 0。最后,您所要做的就是转换为一个 numpy 数组:

>>> feature_descriptors = np.array(feature_descriptors)
>>> feature_descriptors.shape
(3, 500, 64)