有效创建抗锯齿的圆形蒙版

时间:2018-06-23 15:59:51

标签: python arrays numpy mask

我正在尝试创建抗锯齿(加权而不是布尔值)的圆形蒙版,以制作用于卷积的圆形内核。

radius = 3  # no. of pixels to be 1 on either side of the center pixel
            # shall be decimal as well; not the real radius
kernel_size = 9                
kernel_radius = (kernel_size - 1) // 2
x, y = np.ogrid[-kernel_radius:kernel_radius+1, -kernel_radius:kernel_radius+1]
dist = ((x**2+y**2)**0.5)
mask = (dist-radius).clip(0,1)
print(mask)

输出为

array([[1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  ],
       [1.  , 1.  , 0.61, 0.16, 0.  , 0.16, 0.61, 1.  , 1.  ],
       [1.  , 0.61, 0.  , 0.  , 0.  , 0.  , 0.  , 0.61, 1.  ],
       [1.  , 0.16, 0.  , 0.  , 0.  , 0.  , 0.  , 0.16, 1.  ],
       [1.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 1.  ],
       [1.  , 0.16, 0.  , 0.  , 0.  , 0.  , 0.  , 0.16, 1.  ],
       [1.  , 0.61, 0.  , 0.  , 0.  , 0.  , 0.  , 0.61, 1.  ],
       [1.  , 1.  , 0.61, 0.16, 0.  , 0.16, 0.61, 1.  , 1.  ],
       [1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  ]])

那我们就可以做

mask = 1 - mask
print(mask)

获得

array([[0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  ],
       [0.  , 0.  , 0.39, 0.84, 1.  , 0.84, 0.39, 0.  , 0.  ],
       [0.  , 0.39, 1.  , 1.  , 1.  , 1.  , 1.  , 0.39, 0.  ],
       [0.  , 0.84, 1.  , 1.  , 1.  , 1.  , 1.  , 0.84, 0.  ],
       [0.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 1.  , 0.  ],
       [0.  , 0.84, 1.  , 1.  , 1.  , 1.  , 1.  , 0.84, 0.  ],
       [0.  , 0.39, 1.  , 1.  , 1.  , 1.  , 1.  , 0.39, 0.  ],
       [0.  , 0.  , 0.39, 0.84, 1.  , 0.84, 0.39, 0.  , 0.  ],
       [0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  ]])

我现在可以对其进行规范化并将其用作卷积操作中的循环滤波器(内核)。

注意:半径可以是十进制。例如:get_circular_kernel(0.5,(5,5))应该给

array([[0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.08578644, 0.5       , 0.08578644, 0.        ],
       [0.        , 0.5       , 1.        , 0.5       , 0.        ],
       [0.        , 0.08578644, 0.5       , 0.08578644, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ]])

我想至少产生一百万个,并固定kernel_size并更改radius,所以有一种更好或更有效的方法这个? (也许无需像sqrt这样的昂贵操作,并且仍然保持足够的精度以产生弧形积分,即特定像素中曲线所覆盖的面积?)

2 个答案:

答案 0 :(得分:1)

如果要构建数百万个蒙版,则应预先计算一次永不更改的蒙版,并仅计算每个半径所需的严格度。

您可以尝试以下操作:

class Circle:
    def __init__(self, kernel_size):
        self._kernel_size = kernel_size
        self._kernel_radius = (self._kernel_size - 1) // 2

        x, y = np.ogrid[
            -self._kernel_radius:self._kernel_radius+1,
            -self._kernel_radius:self._kernel_radius+1]
        self._dist = np.sqrt(x**2 + y**2)

    def __call__(self, radius):
        mask = self._dist - radius
        mask = np.clip(mask, 0, 1, out=mask)
        mask *= -1
        mask += 1
        return mask


circle = Circle(kernel_size=9)
for radius in range(1, 4, 0.2):
    mask = circle(radius)  
    print(mask)

我尽可能地执行了操作以优化速度和内存,但是对于小型阵列而言,并没有多大关系。

答案 1 :(得分:1)

由于您要生成大量具有相同大小的内核 ,因此可以通过一步构建每个内核而不是循环构建一个内核来大大提高性能。您可以为每个内核指定(num_radii, kernel_size, kernel_size)值来创建形状为num_radii的单个数组。这种向量化的代价是内存:您必须将所有这些值都放入RAM中,否则应将数百万个半径分块成几个较小的批次,然后分别再次生成每个批次。

您唯一需要更改的是获取一个半径数组(而不是标量半径),并注入两个尾随的单例尺寸,以便您的蒙版创建触发broadcasting

import numpy as np 

kernel_size = 9
kernel_radius = (kernel_size - 1) // 2
x, y = np.ogrid[-kernel_radius:kernel_radius+1, -kernel_radius:kernel_radius+1]
dist = (x**2 + y**2)**0.5 # shape (kernel_size, kernel_size)

# let's create three kernels for the sake of example
radii = np.array([3, 3.5, 4])[...,None,None] # shape (num_radii, 1, 1)
# using ... allows compatibility with arbitrarily-shaped radius arrays

masks = 1 - (dist - radii).clip(0,1) # shape (num_radii, kernel_size, kernel_size)

现在masks[0,...](或简称为masks[0],但我更喜欢显式版本)在您的问题中包含示例掩码,而masks[1,...]masks[2,...]包含用于以下内容的内核半径3.54