【问题标题】:Numpy-flipped image + cv2.filter2D = assertion failed?Numpy翻转图像+ cv2.filter2D =断言失败?
【发布时间】:2013-12-09 02:57:42
【问题描述】:

我正在尝试使用 OpenCV 的 filter2D() 进行卷积。在我的算法中,我需要在将内核传递给函数之前翻转内核。我的第一次尝试是使用 Numpy 的 fliplr()flipud() 方法:

def test_np():
    im = np.random.uniform(size=(32, 32))
    k = np.ones((3, 3), dtype=np.float32) / 9.
    k = np.fliplr(np.flipud(k))                # Numpy-flipped kernel
    return cv2.filter2D(im, -1, k)

令人惊讶的是,它在过滤期间给了我一个断言错误

OpenCV 错误:转置中的断言失败 (src.dims

但是,如果我将翻转方法更改为 OpenCV 的flip()

def test_cv2():
    im = np.random.uniform(size=(32, 32))
    k = np.ones((3, 3), dtype=np.float32) / 9.
    k = cv2.flip(k, -1)                        # OpenCV-flipped kernel
    return cv2.filter2D(im, -1, k)

filter2D() 没有任何问题

我检查了np.fliplr(np.flipud(...))cv2.flip(...)的结果,它们是一样的:

k_np = np.fliplr(np.flipud(k))
k_cv2 = cv2.flip(k, -1)
(k_np == k_cv2).all()    # gives True

所以我们有 2 个看起来相同但行为不同的数组

我很好奇,用 Numpy 翻转的数组和用 OpenCV 翻转的数组有什么区别?另外,我是否应该预料到其他功能会出现类似问题

【问题讨论】:

  • 试试这个并检查它是否有效:k = np.fliplr(np.flipud(k)).copy()
  • 你使用哪个版本的 OpenCV?
  • @AbidRahmanK:是的,复制作品的伎俩。所以我想翻转版本的内核只是不拥有它的数据。但是为什么这对filter2D 很重要?我使用 OpenCV 2.4.2。

标签: opencv image-processing numpy convolution


【解决方案1】:

我认为下面的解释是这种不当行为的原因。

简短说明:

当您使用 numpy 函数翻转时,只会更改 ndarray 的步幅,而不是整个数组,即它只是创建具有不同步幅的视图。但是当你用 OpenCV 函数翻转时,整个数组会被重塑。因此,当您应用 filter2D() 函数时,它会在内部调用 transpose() 。最初,OpenCV 的 Python 包装器无法按照预期的方式将具有负跨度的数组转换为 Mat 结构。

因此可能的解决方案是使用copy() 方法手动复制数组。

后来这个方案解决了,所以可以使用更高版本的opencv。 (我使用的是 OpenCV 3.x,从 OpenCV master 分支编译而来,效果很好)

详细说明:

先了解Numpy ndarray的结构

Numpy 数组通过修改 strides 来实现翻转、转置等许多操作。它有一个很大的优势,不需要复制数组,从而提高了性能。所以这些函数不会创建副本,而只是一个view。这些创建的视图可能不是连续的数组,但复制总是会创建连续的数组。

但 OpenCV 总是创建数组的副本。所以在这些情况下,OpenCV 函数可能比 Numpy 函数慢,因为 numpy 只是编辑 strides 值,而不是数组。您可以使用以下转置功能进行如下检查:

In [39]: x = np.ones((3,3),dtype=np.float32)

In [40]: x.strides
Out[40]: (12, 4)

In [43]: x.flags
Out[43]: 
  C_CONTIGUOUS : True    # Original array is continuous
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

现在试试 Numpy 转置

In [41]: y = np.transpose(x)

In [42]: y.strides
Out[42]: (4, 12)         # transpose just change the strides

In [44]: y.flags
Out[44]: 
  C_CONTIGUOUS : False   # tranposed array is not continuous in numpy
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

现在试试 OpenCV 转置

In [45]: z = cv2.transpose(x)

In [46]: np.all(z==y)   # result of numpy and OpenCV are same
Out[46]: True

In [47]: z.strides      # Check the strides
Out[47]: (12, 4)

In [48]: z.flags
Out[48]: 
  C_CONTIGUOUS : True   # OpenCV result is continuous.
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

最后尝试使用手动复制的 numpy 转置

In [53]: q = np.transpose(x).copy()

In [55]: np.all(z==q)
Out[55]: True

In [56]: q.strides     # Strides is same as OpenCV function
Out[56]: (12, 4)

In [57]: q.flags       
Out[57]: 
  C_CONTIGUOUS : True  # result is continuous also
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

和性能对比

In [49]: %timeit y = np.transpose(x)
1000000 loops, best of 3: 701 ns per loop

In [50]: %timeit y = np.transpose(x).copy()
1000000 loops, best of 3: 1.48 us per loop

In [51]: %timeit y = cv2.transpose(x)
1000000 loops, best of 3: 1.04 us per loop

类似地使用 numpy 函数翻转会产生负步幅。但 OpenCV 函数不会创建。

In [58]: a = np.fliplr(x)

In [59]: a.strides
Out[59]: (12, -4)

In [60]: b = cv2.flip(x,-1)

In [61]: b.strides
Out[61]: (12, 4)

在早期版本的 OpenCV 中,python 包装器无法将负跨步数组转换为相应的 Mat 结构。所以可能的解决方案是使用copy() 方法使数组连续。

但在后来的 OpenCV 版本中,他们添加了这种支持。它将检查步幅,如果它是负数,则 python 包装器将制作数组的副本。所以这在更高版本的 OpenCV 中不是问题。

我使用的是从 OpenCV 的主分支编译的 OpenCV 3。让我们检查一下:

In [62]: cv2.__version__
Out[62]: '3.0.0-dev'

In [63]: im = np.random.uniform(size=(32,32))

In [64]: k = np.ones((3,3), dtype=np.float32)/9.

In [65]: k = np.fliplr(np.flipud(k))

In [66]: z = cv2.filter2D(im, -1, k)

In [70]: print z[:5,:5]
[[ 0.65543429  0.53362787  0.45040413  0.52151458  0.61432061]
 [ 0.53666124  0.49690944  0.40779054  0.50330829  0.60923295]
 [ 0.39288601  0.42130001  0.41378173  0.5080897   0.58349994]
 [ 0.32685086  0.4340541   0.46039198  0.48272091  0.45093509]
 [ 0.25456175  0.40217766  0.4459138   0.49665956  0.4198618 ]]

【讨论】:

  • 哇,我什至不知道 OpenCV 的第三个分支。感谢您提供信息和详细解释!
猜你喜欢
  • 2021-06-27
  • 2021-02-13
  • 1970-01-01
  • 2020-02-03
  • 2018-05-20
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 1970-01-01
相关资源
最近更新 更多