ffr*_*end 6 opencv numpy image-processing convolution
我正在尝试使用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)
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,它在过滤期间给了我一个断言错误:
OpenCV错误:转置中的断言失败(src.dims <= 2 && esz <=(size_t)32),文件/build/buildd/opencv-2.4.2+dfsg/modules/core/src/matrix.cpp,第1877行在抛出'cv :: Exception'的实例后调用终止what():/ build/buildd/opencv-2.4.2 + dfsg/modules/core/src/matrix.cpp:1877:error:( - 1515)src. dims <= 2 && esz <=(size_t)32在函数转置中
但是,如果我将翻转方法更改为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)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
所以我们有2个看起来相同但行为不同的数组.
我很好奇,用Numpy翻转的阵列和用OpenCV翻转的阵列有什么区别?另外,我应该期待与其他功能有类似的问题吗?
我认为以下解释是这种不当行为的原因。
简短说明:
当您使用 numpy 函数进行翻转时,仅更改 ndarray 的步幅,而不是整个数组,即它只是创建一个具有不同步幅的视图。但是当你用 OpenCV 函数翻转时,整个数组就会被重塑。因此,当您应用filter2D()函数时,它会在内部调用transpose()。最初,OpenCV 的 Python 包装器无法按照预期方式将具有负步长的数组转换为 Mat 结构。
因此,可能的解决方案是使用方法手动复制数组copy()。
后来这个解决方案解决了,所以可以使用更高版本的opencv。(我使用OpenCV 3.x,从OpenCV master分支编译,它工作正常)
详细说明:
Numpy数组通过修改其步幅来实现翻转、转置等多种操作,它有一个很大的优点,不需要复制数组,从而提高了性能。所以这些函数不会创建副本,而只是创建一个view. 这些创建的视图可能不是连续数组,但复制总是创建连续数组。
但 OpenCV 总是创建数组的副本。因此,在这些情况下,OpenCV 函数可能比 Numpy 函数慢,因为 numpy 只是编辑步幅值,而不是数组。您可以使用以下转置函数进行如下检查:
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
Run Code Online (Sandbox Code Playgroud)
现在尝试使用 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
Run Code Online (Sandbox Code Playgroud)
现在尝试使用 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
Run Code Online (Sandbox Code Playgroud)
最后尝试使用 numpy transpose 手动复制
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
Run Code Online (Sandbox Code Playgroud)
以及性能对比
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
Run Code Online (Sandbox Code Playgroud)
类似地,用 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)
Run Code Online (Sandbox Code Playgroud)
在 OpenCV 的早期版本中,Python 包装器无法将负跨步数组转换为相应的 Mat 结构。所以可能的解决方案是使数组与copy()方法连续。
但在 OpenCV 的更高版本中,他们添加了这种支持。它将检查步幅,如果为负数,则 python 包装器将创建数组的副本。所以在 OpenCV 的更高版本中这不是问题。
我正在使用 OpenCV 3,它是从 OpenCV 的 master 分支编译的。让我们检查一下:
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 ]]
Run Code Online (Sandbox Code Playgroud)