Python 具有多维(或非标量)输出的Scipy滤波器

Python 具有多维(或非标量)输出的Scipy滤波器,python,image-processing,numpy,scipy,ndimage,Python,Image Processing,Numpy,Scipy,Ndimage,是否有类似于ndimage的过滤器支持向量输出?我没有设法使scipy.ndimage.filters.generic\u filter返回的不仅仅是一个标量。取消对下面代码中的行的注释以获取错误:TypeError:只有length-1数组可以转换为Python标量 我正在寻找一个通用的过滤器,它可以处理2D或3D数组,并在每个点返回一个向量。因此,输出将增加一个维度。对于下面的示例,我希望类似这样: m.shape # (10,10) res.shape # (10,10,2) 示

是否有类似于
ndimage
的过滤器支持向量输出?我没有设法使
scipy.ndimage.filters.generic\u filter
返回的不仅仅是一个标量。取消对下面代码中的行的注释以获取错误:
TypeError:只有length-1数组可以转换为Python标量

我正在寻找一个通用的过滤器,它可以处理2D或3D数组,并在每个点返回一个向量。因此,输出将增加一个维度。对于下面的示例,我希望类似这样:

m.shape    # (10,10)
res.shape  # (10,10,2)
示例代码

import numpy as np
from scipy import ndimage

a = np.ones((10, 10)) * np.arange(10)

footprint = np.array([[1,1,1],
                    [1,0,1],
                    [1,1,1]])

def myfunc(x):
    r = sum(x)
    #r = np.array([1,1])  # uncomment this
    return r

res = ndimage.generic_filter(a, myfunc, footprint=footprint)

我想我明白了你的要求,但我不完全确定
ndimage.generic_filter
是如何工作的(源代码有多深奥!)

这里只是一个简单的包装函数。此函数将接收数组中的所有参数
ndimage.generic_filter
所需。函数返回一个数组,其中前一个数组的每个元素现在由一个带形状(2,)的数组表示,函数的结果存储为该数组的第二个元素

def generic_expand_filter(inarr, func, **kwargs):
    shape = inarr.shape
    res = np.empty((  shape+(2,) ))
    temp = ndimage.generic_filter(inarr, func, **kwargs)
    for row in range(shape[0]):
        for val in range(shape[1]):
            res[row][val][0] = inarr[row][val]
            res[row][val][1] = temp[row][val]
    return res
输出,其中,
res
仅表示
generic_filter
,res2表示
generic_expand_filter
,此函数的输出为:

>>> a.shape #same as res.shape
(10, 10)
>>> res2.shape
(10, 10, 2)

>>> a[0]
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> res[0]
array([  3.,   8.,  16.,  24.,  32.,  40.,  48.,  56.,  64.,  69.])
>>> print(*res2[0], sep=", ") #this is just to avoid the vertical default output
[ 0.  3.], [ 1.  8.], [  2.  16.], [  3.  24.], [  4.  32.], [  5.  40.], [  6.  48.], [  7.  56.], [  8.  64.], [  9.  69.]

>>> a[0][0]
0.0
>>> res[0][0]
3.0
>>> res2[0][0]
array([ 0.,  3.])
当然,您可能不想保存旧数组,而是将这两个字段都作为新结果保存。除了我不知道你到底在想什么,如果你想要存储的两个值是不相关的,只需添加一个
temp2
func2
,然后调用另一个
generic_filter
,使用相同的
**kwargs
并将其存储为第一个值


但是,如果您想要使用多个
inarr
元素计算的实际矢量量,这意味着两个新创建的字段不是独立的,那么您只需要编写一个函数,一个接受数组的函数,
idx
idy
索引并返回一个tuple\list\array值,然后您可以将其解包并分配给结果。
generic\u过滤器要求
myfunc
返回一个标量,而不是向量。 但是,没有任何东西阻止
myfunc
也添加信息 例如,作为额外参数传递给
myfunc
的列表

我们不必使用
泛型_filter
返回的数组,而是可以通过重塑此列表来生成向量值数组


比如说,

import numpy as np
from scipy import ndimage

a = np.ones((10, 10)) * np.arange(10)

footprint = np.array([[1,1,1],
                      [1,0,1],
                      [1,1,1]])

ndim = 2
def myfunc(x, out):
    r = np.arange(ndim, dtype='float64')
    out.extend(r)
    return 0

result = []
ndimage.generic_filter(
    a, myfunc, footprint=footprint, extra_arguments=(result,))
result = np.array(result).reshape(a.shape+(ndim,))

使用“可变默认值”确实是一个聪明的技巧,我还没见过。我认为你的解决方案优雅简洁,回答了我的问题。你能评论一下演出吗。除了操作
结果
变量外,似乎没有其他开销。另外,
r[1:][:-1]
是否等同于
r[:0:-1]
generic_filter
的性能从来都不是很好,因为它需要为图像的每个像素调用一次Python函数。我会尝试将其作为最后手段使用——在寻找一种方法来表达计算后,将更快的内置NumPy/SciPy函数应用于整个数组,这些数组将大部分工作卸载到用C/C++或Fortran编写的底层函数。你是对的
r[:0:-1]
相当于
r[1:[:-1]
。由于我很容易被搞糊涂,所以我选择了(在我看来)更简单的
r[1:][::-1]
r[:0:-1]
的速度稍快,但要注意预优化。
myfunc
中真正的瓶颈可能在于
r
的计算,而不是相反。如果您追求速度并愿意牺牲准确性,您可以使用另一种技巧:
myfunc
必须返回标量,例如
np.float128
。但是NumPy也支持
np.float16
,因此您可以将8个
np.float16
s打包成一个
np.float128
。因此,如果您想返回向量np.arange(8,dtype=np.float16)
,您可以返回单个标量np.arange(8,dtype=np.float16).view(np.float128)
,然后使用
res=res.view(np.float16)
来恢复8
np.float16
值。还有一个
np.complex256
dtype,它可以让您打包16
np.float16
s。我找到了一种更简单的方法来实现所需的结果。只需将所有向量附加到一个列表中。在
generic_filter
完成后,将列表转换为数组,然后对其进行重塑。这也比我的第一个答案稍微快一点。