Skew a diagonal gradient to be vertical - python

I have a not-quite linear gradient at some angle to the horizontal as an image. Here's some toy data:
g = np.ones((5,20))
for x in range(g.shape[0]):
for y in range(g.shape[1]):
g[x,y] += (x+y)*0.1+(y*0.01)
I want to essentially correct the skew in the gradient so that it is horizontal, i.e. the gradient increases to the right and all vertical slices are constant.
This will of course produce a parallelogram with a larger x-axis than the input image. Returning a masked Numpy array would be ideal. Here's a (terrible) cartoon to quickly illustrate.
Any idea how to achieve this? Thanks!

You can intepolate to determine the skewness and interpolate again to correct it.
import numpy as np
from scipy.ndimage.interpolation import map_coordinates
m, n = g.shape
j_shift = np.interp(g[:,0], g[0,:], np.arange(n))
pad = int(np.max(j_shift))
i, j = np.indices((m, n + pad))
z = map_coordinates(g, [i, j - j_shift[:,None]], cval=np.nan)
This works on the example image, but you have to do some additional checks to make it function on other gradients. It does not work on gradients that are nonlinear in the x-direction though. Demo:
Full script:
import numpy as np
from scipy.ndimage.interpolation import map_coordinates
def fix(g):
x = 1 if g[0,0] < g[0,-1] else -1
y = 1 if g[0,0] < g[-1,0] else -1
g = g[::y,::x]
m, n = g.shape
j_shift = np.interp(g[:,0], g[0,:], np.arange(n))
pad = int(np.max(j_shift))
i, j = np.indices((m, n + pad))
z = map_coordinates(g, [i, j - j_shift[:,None]], cval=np.nan)
return z[::y,::x]
import matplotlib.pyplot as plt
i, j = np.indices((50,100))
g = 0.01*i**2 + j
plt.figure(figsize=(6,5))
plt.subplot(211)
plt.imshow(g[::-1], interpolation='none')
plt.title('original')
plt.subplot(212)
plt.imshow(fix(g[::-1]), interpolation='none')
plt.title('fixed')
plt.tight_layout()

Related

The FFT of a top-hat function looks mirrored/flipped (Python)

I am applying numpy.fft.fft function to a simple 'top-hat' function, i.e. all values are 0 except over a given x-range where they are 1.
The problem arises when I apply a fast fourier transform to the data. It outputs what I am expecting, which is a Gaussian profile, but it looks like instead of being centred on 0 the peaks are at the edges. I am expecting the peak to be central. It's as if the output was split down the centre, and mirrored.
Is there a way of centering this output data other than manually flipping the output array? Why is it doing this?
Here's my full code:
import numpy as np
from numpy.fft import fft, ifft
import matplotlib.pyplot as plt
def find_nearest(array, value):
""" Finds the nearest array value index for a particular value."""
array = np.asarray(array)
idx = (np.abs(array - value)).argmin()
return idx
aperture_length = 81.92e-3 # in metres
start = - aperture_length / 2
end = aperture_length / 2
step = 0.02e-3
x = np.arange(start, end, step)
E = np.zeros(len(x))
slit_length = 1e-3
ind1 = find_nearest(x, -slit_length/2)
ind2 = find_nearest(x, slit_length/2)
for i in range(len(E)):
if i <= ind2 and i >= ind1:
E[i] = 1
# Compute FFT
trans = fft(E, norm='ortho')
fig = plt.figure(dpi=150)
ax1 = plt.subplot(1, 1, 1)
ax1.plot(x, E)
ax1.set_title(r'E vs. x')
plt.figure(dpi=150)
ax2 = plt.subplot(1, 1, 1)
ax2.plot(x, trans)
# ax2.set_xlim(-0.02, 0.02)
ax2.set_title(r'FFT of E')
plt.tight_layout()

Remove Overlapping Circles by Keeping Only Largest

Given a set of circles with random centers and radii, I would like to be able to prune this set so that if overlap between circles occurs, only the largest circle is retained. This is a similar question to the one answered here, but the problem listed there seeks to retain the maximum number of non-overlapping circles, from what I understand. I'd like to be able to adapt the ILP solution given there to my needs, if possible, although a brute-force "search and remove"-type approach would be fine too. The latter is what I've tried so far, but failed to accomplish.
import matplotlib.pyplot as plt
from numpy.random import rand, seed
seed(1)
N = 25 # number of circles
L = 10 # domain size
Rmin = 0.5 # min radius
Rmax = 1 # max radius
cx = rand(N)*(L-2*Rmax) + Rmax
cy = rand(N)*(L-2*Rmax) + Rmax
r = rand(N)*(Rmax-Rmin) + Rmin
# Plotting
for i in range(N):
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()
Desired Result:
It got a bit messy, but this creates the Output you wanted.
import matplotlib.pyplot as plt
from numpy.random import rand, seed
import math
import numpy as np
import pandas as pd
def find_larger(df_circles_2, idx):
found_greater = False
for i,row in df_circles_2.iterrows():
if i != idx:
distance = math.sqrt( (row['x'] - df_circles_2['x'][idx])**2 + (row['y'] - df_circles_2['y'][idx])**2 )
if distance < (row['r'] + df_circles_2['r'][i]):
if row['r'] > df_circles_2['r'][idx] and (row['keep'] != "discard"):
if df_circles['keep'][i] == "keep":
return "discard"
found_greater = True
if found_greater:
return "undecided"
else:
return "keep"
seed(1)
N = 25 # number of circles
L = 10 # domain size
Rmin = 0.5 # min radius
Rmax = 1 # max radius
cx = rand(N)*(L-2*Rmax) + Rmax
cy = rand(N)*(L-2*Rmax) + Rmax
r = rand(N)*(Rmax-Rmin) + Rmin
# Plotting
for i in range(N):
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.gca().add_artist(plt.Text(cx[i], cy[i], text = str(i)))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()
# Searching:
df_circles = pd.DataFrame(np.array([cx, cy, r]).T, columns = ['x', 'y', 'r'])
df_circles['keep'] = "undecided"
while(df_circles['keep'].str.contains('undecided').any()):
for i, row in df_circles.iterrows():
if row['keep'] == "undecided":
df_circles.at[i, 'keep'] = find_larger(df_circles, i)
# Plotting 2
plt.figure(2)
for i in range(N):
if df_circles['keep'][i] == "keep":
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='black'))
else:
plt.gca().add_artist(plt.Circle((cx[i], cy[i]), r[i], ec='black', fc='white'))
plt.axis('image')
plt.xlim(0,L)
plt.ylim(0,L)
plt.show()

How to plot a function with a vector and matrix in python?

But function f is a problem because I don't know how to combine the mesh with the matrix, is there a smart way to solve this problem?
It looks like your code for g is very close to the one for f. You could just define your M matrix and include it in the matrix multiplication. See code below for more details:
import numpy as np
import matplotlib.pyplot as plt
def f_function(diagonal_values):
fig = plt.figure(figsize=(15,8))
data = np.linspace(-4, 4, 20)
x_1, x_2 = np.meshgrid(data, data, indexing="ij")
fx = np.zeros_like(x_1)
#Defining M
M=np.diag(diagonal_values)
print(M)
for i in range(data.shape[0]):
for j in range(data.shape[0]):
x = np.array([x_1[i,j], x_2[i,j]])
f = x.T # M # x
fx[i,j] = f
ax = fig.add_subplot(121, projection="3d")
surf = ax.plot_surface(x_1, x_2, fx)
ax.set_xlabel("x_1")
ax.set_ylabel("x_2")
ax.set_zlabel("f")
#Randomly picking diagonal values
diag_values=np.random.uniform(0,10,2)
print('Diagonal values: '+str(diag_values))
f_function(np.array(diag_values))
The output gives:
Diagonal values: [8.62030848 2.68367524]
And the plot:

How to optimize this code having multiple loops?

So i was trying this and I find it really unfeasible. I am not that aware about smart ways to do the following. Can somebody help ? Also inputs of lists are quite big.
This task was to create an image from the values generated by me.
center_star contains list of [x,y] pairs which are centers of various point like objects.
1800 value represents that image to be generated is of 1800x1800 pixel.
Sigma variable has value 2 by default.
final=[[0]*1800]*1800
for i in range(len(center_stars)):
xi=center_stars[i][0]
yi=center_stars[i][1]
print(i)
for j in range(1800):
for k in range(1800):
final[j][k]+=gauss_amplitude[i]*(math.e**((-1*((xi-j)**2+(yi-k)**2))/2*sigma*sigma))
Is there a smarter way to save time using some of the numpy operation and execute this piece of code in less time?
Something like that:
import math
import numpy as np
N = 1800
final = np.empty((N, N))
final1 = np.empty((N, N))
j = np.arange(N)
k = np.arange(N)
jj, kk = np.meshgrid(j, k)
sigma = 2.
s = 0.5 / (sigma * sigma)
for i in range(len(center_stars)):
xi = center_stars[i][0]
yi = center_stars[i][1]
final += gauss_amplitude[i] * np.exp(- ((xi - jj.T)**2 + (yi - kk.T)**2) * s)
# Code below is for comparison:
for j in range(N):
for k in range(N):
final1[j][k]+=gauss_amplitude[i] * (math.e** (-((xi-j)**2+(yi-k)**2)/(2*sigma*sigma)))
Besides, I assume you missed brackets around 2*sigma*sigma
you can try to compact your code like this:
Gauss=lambda i,j,k,xi,yi:gauss_amplitude[i]*(math.e**((-((xi-j)**2+(yi-k)**2))/(2*sigma*sigma)))
final=[[Gauss(i,j,k,x[0],x[1]) for j in range(1800) for k in range(1800)] for i,x in enumerate(center_starts)]
If your sigmas are all the same, you can achieve this with out any loop by using
scipy.signal.convolve2d.
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve2d
from scipy.stats import multivariate_normal
sigma = 3
width = 200 # smaller than yours so you can see the single pixels
n_stars = 50
# draw some random stars
star_row = np.random.randint(0, width, n_stars)
star_col = np.random.randint(0, width, n_stars)
star_amplitude = np.random.normal(50, 10, n_stars)
# assign amplitudes to center pixel of stars
amplitudes = np.zeros((width, width))
amplitudes[star_row, star_col] = star_amplitude
# create 2d gaussian kernel
row = col = np.arange(-4 * sigma, 4 * sigma + 1)
grid = np.stack(np.meshgrid(row, col)).T
kernel = multivariate_normal(
[0, 0],
[[sigma**2, 0], [0, sigma**2]]
).pdf(grid)
kernel /= kernel.sum()
# convolve with 2d gaussian
final = convolve2d(amplitudes, kernel, mode='same')
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(9, 3))
img = ax1.imshow(amplitudes)
fig.colorbar(img, ax=ax1)
ax1.set_label('Before Convolution')
img = ax2.imshow(kernel)
fig.colorbar(img, ax=ax2)
ax2.set_label('Convolution Kernel')
img = ax3.imshow(final)
fig.colorbar(img, ax=ax3)
ax3.set_label('After Convolution')
fig.tight_layout()
fig.savefig('conv2d.png', dpi=300)
Result:
If the sigmas differ, you get a way with a single loop over the possible sigmas.

I am getting two plots for one data set in python

I am working through example 8.1 titled Euler's Method from Mark Newman's book Computational Physics. I rewrote the example as a method with Numpy arrays but when I plot it I get two plots on the same figure not sure how to correct it. Also is there better way to convert my 2 1D arrays into 1 2D array to use for plotting in Matplotlib, thanks.
Newman's example :
from math import sin
from numpy import arange
from pylab import plot,xlabel,ylabel,show
def f(x,t):
return -x**3 + sin(t)
a = 0.0 # Start of the interval
b = 10.0 # End of the interval
N = 1000 # Number of steps
h = (b-a)/N # Size of a single step
x = 0.0 # Initial condition
tpoints = arange(a,b,h)
xpoints = []
for t in tpoints:
xpoints.append(x)
x += h*f(x,t)
plot(tpoints,xpoints)
xlabel("t")
ylabel("x(t)")
show()
My modifications:
from pylab import plot,show,xlabel,ylabel
from numpy import linspace,exp,sin,zeros,vstack,column_stack
def f(x,t):
return (-x**(3) + sin(t))
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = linspace(a,b,N)
x = zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return column_stack((t,x)) #vstack((t,x)).T
plot(Euler(f,0.0,0.0,10.0))
xlabel("t")
ylabel("x(t)")
show()
The reason you get two lines is that t as well as x are plotted against their index, instead of x plotted against t
I don't see why you'd want to stack the two arrays. Just keep then separate, which will also solve the problem of the two plots.
The following works fine.
import numpy as np
import matplotlib.pyplot as plt
f = lambda x,t: -x**3 + np.sin(t)
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = np.linspace(a,b,N)
x = np.zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return t,x
t,x = Euler(f,0.0,0.0,10.0)
plt.plot(t,x)
plt.xlabel("t")
plt.ylabel("x(t)")
plt.show()

Categories

Resources