本文介绍了有效创建抗锯齿的圆形蒙版的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create anti-aliased (weighted and not boolean) circular masks for making circular kernels for use in convolution.

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.  ]])

我现在可以将其标准化并用作卷积运算中的循环滤波器(内核).

I can now normalize and use this as my circular filter (kernel) in convolution operations.

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

Note: Radius can be decimal. Eg: get_circular_kernel(0.5,(5,5)) should give

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这样的昂贵操作,仍然保持足够的精度以产生弧形积分,即特定像素中曲线所覆盖的面积?)

I want to generate a million of these at the very least, with the kernel_size fixed and radius changing, so is there a better or more efficient way to do this? (maybe without costly operations like sqrt and still stay accurate enough to arc integrals i.e., area covered by the curve in the particular pixel?)

推荐答案

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

Since you want to generate a large number of kernels with the same size, you can greatly improve performance by constructing every kernel in one step rather than one after the other in a loop. You can create a single array of shape (num_radii, kernel_size, kernel_size) given num_radii values for each kernel. The price of this vectorization is memory: you'll have to fit all these values in RAM, otherwise you should chunk up your millions of radii into a handful of smaller batches and generate each batch again separately.

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

The only thing you need to change is to take an array of radii (rather than a scalar radius), and inject two trailing singleton dimensions so that your mask creation triggers 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的内核分别.

Now masks[0,...] (or masks[0] for short, but I prefer the explicit version) contains the example mask in your question, and masks[1,...] and masks[2,...] contain the kernels for radii 3.5 and 4, respectively.

这篇关于有效创建抗锯齿的圆形蒙版的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 10:28