如何将圆形面罩应用于NumPy数组?

39

我有一个像这样的数组:

>>> np.ones((8,8))
array([[ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.]])

我正在创建一个半径为3的圆盘形遮罩,如下:

y,x = np.ogrid[-3: 3+1, -3: 3+1]
mask = x**2+y**2 <= 3**2
这将产生以下结果:
>> mask
array([[False, False, False,  True, False, False, False],
       [False,  True,  True,  True,  True,  True, False],
       [False,  True,  True,  True,  True,  True, False],
       [ True,  True,  True,  True,  True,  True,  True],
       [False,  True,  True,  True,  True,  True, False],
       [False,  True,  True,  True,  True,  True, False],
       [False, False, False,  True, False, False, False]], dtype=bool)

现在,我想能够将这个掩码应用到我的数组中,以任何元素作为中心点。 例如,以(1,1)为中心点,我想获得一个类似下面的数组:

>>> new_arr
array([[ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ True,  True,  True,  True,  True,  1.,  1.,  1.],
       [ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ 1.,    True,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.]])

有没有一种简单的方法来应用这个掩码?

编辑:我不应该混淆布尔值和浮点数 - 这是具有误导性的。

>>> new_arr
array([[ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,  255.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 1.,    255.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.]])

这更符合我所需的结果。

array[mask] = 255 

会使用中心点 (0+radius,0+radius) 来遮盖数组。

然而,我想能够将任何大小的遮罩置于任何点 (y,x),并自动裁剪以适应。

7个回答

69

我会这样做,其中(a, b)是您掩码的中心:

import numpy as np

a, b = 1, 1
n = 7
r = 3

y,x = np.ogrid[-a:n-a, -b:n-b]
mask = x*x + y*y <= r*r

array = np.ones((n, n))
array[mask] = 255

7

您可以使用scipy的卷积函数,该函数允许您一次在数组中的任意给定坐标上放置任何特定的掩模(也称为内核):

import numpy as np
from scipy.ndimage.filters import convolve

首先创建一个坐标数组,其中要将掩码(内核)居中的坐标标记为2。

background = np.ones((10,10))
background[5,5] = 2
print(background)

[[ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  2.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]]

创建您的掩码:

y,x = np.ogrid[-3: 3+1, -3: 3+1]
mask = x**2+y**2 <= 3**2
mask = 254*mask.astype(float)
print(mask)

[[   0.    0.    0.  254.    0.    0.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [ 254.  254.  254.  254.  254.  254.  254.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.    0.    0.  254.    0.    0.    0.]]

对两个图像进行卷积运算:

b = convolve(background, mask)-sum(sum(mask))+1
print(b)

[[   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.  255.    1.    1.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.  255.  255.  255.  255.  255.  255.  255.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.    1.    1.  255.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]]

请注意,convolve函数的输入顺序不能交换,即convolve(a,b) != convolve(b,a)
同时请注意,如果您的点靠近边缘,算法将不会在该坐标处复制核。为了解决这个问题,您可以通过核的最大轴来填充背景,应用卷积,然后去除填充。
现在,您可以将任何核映射到数组中的任意数量的点,但请注意,如果两个核重叠,它们会在重叠处相加。如果需要,您可以进行阈值处理。

7
我想与大家分享一下我所面临的稍微高级一点的应用技巧。这个问题是应用圆形核计算二维矩阵中每个点周围所有值的平均值。生成的核可以通过以下方式传递给Scipy的通用过滤器函数:
import numpy as np
from scipy.ndimage.filters import generic_filter as gf

kernel = np.zeros((2*radius+1, 2*radius+1))
y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask = x**2 + y**2 <= radius**2
kernel[mask] = 1
circular_mean = gf(data, np.mean, footprint=kernel)

希望这能帮助你!

5
将其转化为一个方便的函数:
def cmask(index,radius,array):
  a,b = index
  nx,ny = array.shape
  y,x = np.ogrid[-a:nx-a,-b:ny-b]
  mask = x*x + y*y <= radius*radius

  return(sum(array[mask]))

返回半径内的像素总和,或者对任何需要返回(array [mask] = 2)。

2

您试过使用掩码、0和1,然后使用逐元素数组乘法吗? 这是一种规范的方法,多少有点。

另外,您确定要在 numpy 数组中混合数字和布尔值吗? 如其名称所示,NumPy最擅长处理数字。


对于数字/布尔值混合的混淆,我很抱歉。希望现在问题不再具有误导性。您可以解释一下您的第一句话吗? - user816555

0

要得到与您的示例相同的结果,您可以这样做:

>>> new_arr = np.array(ones, dtype=object)
>>> new_arr[mask[2:, 2:]] = True
>>> print new_arr
array([[True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [True, True, True, True, True, 1.0, 1.0, 1.0],
       [True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [1.0, True, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]], dtype=object)

它能正常工作...但那是一种相当蹩脚的方式,需要复制数组并更改其数据类型...乘以0/1是@9000建议的标准方法。 - mac
@mac 是的,我同意。我期望从 OP 那里得到一些反馈,以了解他真正想要什么。 - jcollado
非常抱歉我之前的问题有误导性。我在我的帖子中澄清了我的问题。我想要的是一种方法来获取原始数组中被掩码覆盖的元素,给定掩码的中心点(y,x)。然后我可以根据需要对它们进行操作。 - user816555

-1
def susanKernel(raduis):
    kernel = np.zeros((2*radius+1, 2*radius+1) ,np.uint8)
    y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
    mask = x**2 + y**2 <= radius**2
    kernel[mask] = 1
    kernel[0,radius-1:kernel.shape[1]-radius+1] = 1
    kernel[kernel.shape[0]-1,radius-1:kernel.shape[1]-radius+1]= 1
    kernel[radius-1:kernel.shape[0]-radius+1,0] = 1
    kernel[radius-1:kernel.shape[0]-radius+1,kernel.shape[1]-1] = 1
    return kernel

这个回答如何回答问题? - 12944qwerty
这并没有提供问题的答案。您可以搜索类似的问题,或者参考页面右侧的相关和链接问题来找到答案。如果您有一个相关但不同的问题,请提出新问题,并包含此问题的链接以帮助提供上下文。请参阅导览 - 12944qwerty
1
虽然这可能回答了问题,但如果可能的话,您应该编辑您的答案,包括解释如何这个代码块回答了问题。这有助于提供上下文,并使您的答案对未来的读者更有用。 - Hoppeduppeanut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接