Render a Circle or Ellipse with Anti-Aliasing - python

Assume I have a square raster of given size, and I want to "draw" (render) a circle (or ellipse) of given radius (or major / minor axes) and center.
One way of doing this in Python with NumPy is:
import numpy as np
def ellipse(box_size, semisizes, position=0.5, n_dim=2):
shape = (box_size,) * n_dim
if isinstance(semisizes, (int, float)):
semisizes = (semisizes,) * n_dim
position = ((box_size - 1) * position,) * n_dim
grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
position = np.ogrid[grid]
arr = np.zeros(shape, dtype=float)
for x_i, semisize in zip(position, semisizes):
arr += (np.abs(x_i / semisize) ** 2)
return arr <= 1.0
print(ellipse(5, 2).astype(float))
# [[0. 0. 1. 0. 0.]
# [0. 1. 1. 1. 0.]
# [1. 1. 1. 1. 1.]
# [0. 1. 1. 1. 0.]
# [0. 0. 1. 0. 0.]]
which produces a rasterization without anti-aliasing.
In particular, the pixels that are only partially included in the circle get a value of 0 (similarly to pixels excluded from the circle), while pixels entirely included in the circle gets a value of 1.
With anti-aliasing, the pixels partially included in the circle would get a value between 0 and 1 depending on how much of their area is included in the circle.
How could I modify the code from above to (possibly cheaply) include anti-aliasing?
I am struggling to see how (if?) I could use the values of arr.
Super-sampling-based methods are out of question here.
Eventually, the result should look something like:
# [[0.0 0.2 1.0 0.2 0.0]
# [0.2 1.0 1.0 1.0 0.2]
# [1.0 1.0 1.0 1.0 1.0]
# [0.2 1.0 1.0 1.0 0.2]
# [0.0 0.2 1.0 0.2 0.0]]
(where 0.2 should be a value between 0.0 and 1.0 representing how much area of that specific pixel is covered by the circle).
EDIT
I see now obvious way on how to adapt the code from Creating anti-aliased circular mask efficiently although obviously, np.clip() must be part of the solution.

One fast but not necessarily mathematically correct way of doing this (loosely based on the code from Creating anti-aliased circular mask efficiently) is:
import numpy as np
def prod(items, start=1):
for item in items:
start *= item
return start
def ellipse(box_size, semisizes, position=0.5, n_dim=2, smoothing=1.0):
shape = (box_size,) * n_dim
if isinstance(semisizes, (int, float)):
semisizes = (semisizes,) * n_dim
position = ((box_size - 1) * position,) * n_dim
grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
position = np.ogrid[grid]
arr = np.zeros(shape, dtype=float)
for x_i, semisize in zip(position, semisizes):
arr += (np.abs(x_i / semisize) ** 2)
if smoothing:
k = prod(semisizes) ** (0.5 / n_dim / smoothing)
return 1.0 - np.clip(arr - 1.0, 0.0, 1.0 / k) * k
elif isinstance(smoothing, float):
return (arr <= 1.0).astype(float)
else:
return arr <= 1.0
n = 1
print(np.round(ellipse(5 * n, 2 * n, smoothing=0.0), 2))
# [[0. 0. 1. 0. 0.]
# [0. 1. 1. 1. 0.]
# [1. 1. 1. 1. 1.]
# [0. 1. 1. 1. 0.]
# [0. 0. 1. 0. 0.]]
n = 1
print(np.round(ellipse(5 * n, 2 * n, smoothing=1.0), 2))
# [[0. 0.65 1. 0.65 0. ]
# [0.65 1. 1. 1. 0.65]
# [1. 1. 1. 1. 1. ]
# [0.65 1. 1. 1. 0.65]
# [0. 0.65 1. 0.65 0. ]]
A slightly more general version of this approach has been included in the raster_geometry Python package (Disclaimer: I am the main author of it).

Related

Creating a Kernel matrix without for-loops in Python

I know there are other posts asking similar questions, but didn't manage to find something that answers my specific question.
I have the code below :
def kernel_function(self, x1, x2):
h = 0.5
return np.exp(-(np.linalg.norm(x2 - x1)/h)**2)
for i, x1 in enumerate(train_x):
for j, x2 in enumerate(train_x):
K[i,j] = self.kernel_function(x1, x2)
where x1 and x2 are arrays of shape (2,). I need to vertorize it for performance. I looked at np.fromfunction, np.outer, but they don't seem to be what I am looking for...
Thank you in advance. Sorry if there is already an answer somewhere!
Assuming train_x has the following format:
>>> train_x = np.array(((-.2, -.1), (0, .1), (.2, 0), (.1, -.1)))
Executing your code you get:
>>> np.set_printoptions(precision=2)
>>> K
[[1. 0.73 0.51 0.7 ]
[0.73 1. 0.82 0.82]
[0.51 0.82 1. 0.92]
[0.7 0.82 0.92 1. ]]
You can reshape train_x:
>>> train_x_cols = train_x.T.reshape(2, -1, 1)
>>> train_x_rows = train_x.T.reshape(2, 1, -1)
So, thanks to broadcasting, you get all the combinations when you subtract them:
>>> train_x_rows - train_x_cols
[[[ 0. 0.2 0.4 0.3]
[-0.2 0. 0.2 0.1]
[-0.4 -0.2 0. -0.1]
[-0.3 -0.1 0.1 0. ]]
[[ 0. 0.2 0.1 0. ]
[-0.2 0. -0.1 -0.2]
[-0.1 0.1 0. -0.1]
[ 0. 0.2 0.1 0. ]]]
And you can rewrite kernel_function() to calculate the norm on the first axis only:
def kernel_function(x1, x2):
h = 0.5
return np.exp(-(np.linalg.norm(x2 - x1, axis=0) / h) ** 2)
Then you get:
>>> kernel_function(train_x_cols, train_x_rows)
[[1. 0.73 0.51 0.7 ]
[0.73 1. 0.82 0.82]
[0.51 0.82 1. 0.92]
[0.7 0.82 0.92 1. ]]

Iterate over padded area in 2D array in python

Assume I have a 2D array in Python and I add some padding. How can I iterate over the new padded area only?
For example
1 2 3
4 5 6
7 8 9
Becomes
x x x x x x x
x x x x x x x
x x 1 2 3 x x
x x 4 5 6 x x
x x 7 8 9 x x
x x x x x x x
x x x x x x x
How can I loop over only the x's?
Not sure if I understand what you are trying to do, but if you are using numpy, you can use masks:
import numpy as np
arr = np.array(np.arange(1,10)).reshape(3,3)
# mask full of True's
mask = np.ones((7,7),dtype=bool)
# setting the interior of the mask as False
mask[2:-2,2:-2] = False
# using zero padding as example
pad_arr = np.zeros((7,7))
pad_arr[2:-2,2:-2] = arr
print(pad_arr)
# loop for elements of the padding, where mask == True
for value in pad_arr[mask]:
print(value)
Returns:
[[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 1. 2. 3. 0. 0.]
[0. 0. 4. 5. 6. 0. 0.]
[0. 0. 7. 8. 9. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]]
and 0.0 40 times (the padded values)

Loop over finite probability weights with SciPy/NumPy

Let us have a single event probability prob which is a scalar between 0-1. If I want to iterate over every possible probability with 0.1 increments, then I can use:
prob = np.arange(0.01, 1, 0.1)
Now assume I have 5 events (independent, probabilities sum to 1), each with probability p_i. I would like to have multi-dimensional probability arrays such as:
1.0 - 0.0 - 0.0 - 0.0 - 0.0
0.9 - 0.1 - 0.0 - 0.0 - 0.0
0.9 - 0.0 - 0.1 - 0.0 - 0.0
0.9 - 0.0 - 0.0 - 0.1 - 0.0
0.9 - 0.0 - 0.0 - 0.0 - 0.1
0.8 - 0.1 - 0.1 - 0.0 - 0.0
0.8 - 0.1 - 0.0 - 0.1 - 0.0
. . . . .
. . . . .
. . . . .
0.2 - 0.2 - 0.2 - 0.2 - 0.2
Is there a more clever way than to consider all the combinations of 0 - 0.1 - ... - 1 and delete the rows not summing up to 1? If yes, what is the easiest way?
You can use itertools.product and filter to create all combinations that sum 10 and pass it to an array:
import itertools
f = filter(lambda x: sum(x) == 10, itertools.product(*[range(11)]*5))
x = np.array(list(f)).astype(np.float)/10
x
>> array([[0. , 0. , 0. , 0. , 1. ],
[0. , 0. , 0. , 0.1, 0.9],
[0. , 0. , 0. , 0.2, 0.8],
...,
[0.9, 0. , 0.1, 0. , 0. ],
[0.9, 0.1, 0. , 0. , 0. ],
[1. , 0. , 0. , 0. , 0. ]])
EDIT
For the record, here's a more efficient way without using filtering. Essentially you create k bins (in your example, 10), and "assign" them to "n" samples (in your example, 3) in all possible combinations, using combinations_with_replacement
Then, you count how many bins each samples gets: this is your probability. This method is more complex to understand but avoids the filter, and thus it is much more efficient. You can try it with divisions of 0.01 (k = 100)
n = 3 # number of samples
k = 100 # number of subdivisions
f = itertools.combinations_with_replacement(range(3),k) #your iterator
r = np.array(list(f)) #your array of combinations
x = np.vstack((r==i).sum(1) for i in range(n)).T/k #your probability matrix
There's likely a more elegant solution using itertools but this is probably fine and uses no dependencies?:
for i in prob:
for j in prob:
for k in prob:
for l in prob:
m = 1 - i - j - l
if m>=0:
print(i,j,k,l,m)

My conditional variable on my if statement is being changed by the statement, even though it doesn't appear in the statement. Why? [duplicate]

This question already has answers here:
Why does my original list change? [duplicate]
(2 answers)
Closed 3 years ago.
I want to create two matrices. Then make the second matrix numbers changed depending on the numbers in the first matrix. So I generate an If statement about my first matrix and if true this will induce a change in my second matrix. However, it induces a change in both matrices?
My code works perfectly with single digit objects. It only occurs when I try to apply it with matrices.
import numpy as np
n = 3
matr = np.zeros((n,n))
matr[0][0] = 1
matr2 = matr
print(matr)
[[1. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
print(matr2)
[[1. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
if matr[0][0] == 1:
matr2[0][0] = 9
print(matr)
[[9. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
print(matr2)
[[9. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
Because "matr" doesn't occur as a subject in my if statement it shouldn't be altered right?
x = 1
y = x
if x == 1:
y = 9
print(x)
1
print(y)
9
Those 2 variables are just two references to the same matrix, not 2 different matrices; matr2 = matr just creates a new reference to the same matrix.
The statement matr2[0][0] = 9 modifies the one and only matrix that exists in your example, and it is exactly the same as using matr[0][0] = 9.

How to interpret the output of scipy.fftpack.fft?

I have 44100Hz audio, which means that there are 44100 samples per second. I would like to analyze it, so I split up the data to sub-arrays with length 1024.
For each array, I apply Fourier transformation (fft), which returns with an array of complex numbers. Those numbers should be the shift and phase values.
The length of the result is 1024, just like a chunk. But I don't know, which element of the array corresponds to which frequency. I checked the documentation, but the only thing I was able to find out, was that the result is symmetric, and I can skip the first part.
from scipy.fftpack import fft
res = fft(chunk)
But how is it possible to find out, that what is the frequency at a given index in the result?
You can see this directly by taking FFT of pure tones. Here I compare: constant function (zero frequency), frequency 1 (period = sampled interval), frequency 2 (period = half of sampled interval), and so on:
import numpy as np
from scipy.fftpack import fft
arr = np.linspace(0, 2*np.pi, 9)[:-1]
for k in range(5):
print np.round(np.abs(fft(np.cos(k*arr))), 10)
Result:
[ 8. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 4. 0. 0. 0. 0. 0. 4.]
[ 0. 0. 4. 0. 0. 0. 4. 0.]
[ 0. 0. 0. 4. 0. 4. 0. 0.]
[ 0. 0. 0. 0. 8. 0. 0. 0.]
So, the 0th entry is constant term, the entries 1 and -1 are for frequency for which the period is the time interval we sampled; the entries 2 and -2 are for period being half of sampled time interval; 3 and -3 for period being 1/3 of sampled time interval, etc, until we reach Nyquist frequency.
For a sample of size 1024:
1 and -1 are for frequency 1/1024 of sampling rate
2 and -2 are for frequency 2/1024 of sampling rate
3 and -3 are for frequency 3/1024 of sampling rate
...
512 is for Nyquist frequency, 1/2 = 512/1024 of sampling rate

Categories