Skip to content Skip to sidebar Skip to footer

Creating Anti-aliased Circular Mask Efficiently

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

Solution 1:

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)

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.

Solution 2:

If you want to build millions of masks, you should precompute once what never changes, and compute only the strict necessary for each radius.

You can try something like this:

classCircle:
    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 += 1return mask


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

I did the operations inplace as much as possible to optimize for speed and memory, but for small arrays it won't matter much.

Post a Comment for "Creating Anti-aliased Circular Mask Efficiently"