How can I generate random points faster in a large arrays? - python

I am trying to generate random points inside a 3d pepper like shape however when the arr_size is large it takes too long to generate these points.
arr_size is (30,30,30) it takes very little time to generate 1000 random point inside the 3d shape however when arr_size = (265,490,286) it takes a realy long time.
from matplotlib import pyplot as plt
import numpy as np
def create_bin_pepper(arr_size, center):
coords = np.ogrid[:arr_size[0], :arr_size[1], :arr_size[2]]
c = 10
a1 = np.random.randint(low=5,high=10)
b1 = np.random.randint(low=7,high=10)
a2 = np.random.randint(low=5,high=10)
b2 = np.random.randint(low=7,high=10)
a3 = np.random.randint(low=5,high=10)
b3 = np.random.randint(low=7,high=10)
ellipse1 = ((np.square(coords[0] - center[0]))/np.square(a1) + (np.square(coords[1]-center[1]))/np.square(b1) + (np.square(coords[2]-center[2]))/np.square(c) <= 1)
ellipse2 = ((np.square(coords[0] - center[0]-3))/np.square(a2) + (np.square(coords[1]-center[1]-5))/np.square(b2) + (np.square(coords[2]-center[2]))/np.square(c) <= 1)
ellipse3 = ((np.square(coords[0] - center[0]+3))/np.square(a3) + (np.square(coords[1]-center[1]-5))/np.square(b3) + (np.square(coords[2]-center[2]))/np.square(c) <= 1)
pepper = ellipse1|ellipse2|ellipse3
pepper2 = np.where(pepper==1,230,pepper)
for im in range(0,1000):
#r2=1
centre_x1 = np.random.randint(low=center[0]-a1+4,high=center[0]+a1-4)#low=11,high=20
centre_y1 = np.random.randint(low=center[1]-b1+4,high=center[1]+b1-4)#low=15,high=23
centre_z1 = np.random.randint(low=center[2]-c+4,high=center[2]+c-4)#low=10,high=20
centre_x2 = np.random.randint(low=center[0]-a2+4,high=center[0]+a2-4)#low=11,high=20
centre_y2 = np.random.randint(low=center[1]-b2+4,high=center[1]+b2-4)#low=15,high=23
centre_z2 = np.random.randint(low=center[2]-c+4,high=center[2]+c-4)#low=10,high=20
centre_x3 = np.random.randint(low=center[0]-a3+4,high=center[0]+a3-4)#low=11,high=20
centre_y3 = np.random.randint(low=center[1]-b3+4,high=center[1]+b3-4)#low=15,high=23
centre_z3 = np.random.randint(low=center[2]-c+4,high=center[2]+c-4)#low=10,high=20
inside_ellipse1 = ((np.square(coords[0] - centre_x1))/np.square(a1) + (np.square(coords[1]-centre_y1))/np.square(b1) + (np.square(coords[2]-centre_z1))/np.square(c) <= (1/((np.square(a1))*(np.square(b1))*(np.square(c)))))
inside_ellipse2 = ((np.square(coords[0] - centre_x2-3))/np.square(a2) + (np.square(coords[1]-centre_y2-5))/np.square(b2) + (np.square(coords[2]-centre_z2))/np.square(c) <= (1/((np.square(a2))*(np.square(b2))*(np.square(c)))))
inside_ellipse3 = ((np.square(coords[0] - centre_x3+3))/np.square(a3) + (np.square(coords[1]-centre_y3-5))/np.square(b3) + (np.square(coords[2]-centre_z3))/np.square(c) <= (1/((np.square(a3))*(np.square(b3))*(np.square(c)))))
pepper2 = inside_ellipse1 | inside_ellipse2 | inside_ellipse3 | pepper2
pepper3 = np.where((pepper2!=230)&(pepper2!=0),160,pepper2)
return pepper3
arr_size = (265,490,286)
sphere_center1 = (133,216,40)
pepper = create_bin_pepper(arr_size,sphere_center1)
axis = pepper[:,:,40]
plt.imshow(axis,cmap='gray')#,interpolation='bicubic'
plt.show()

You can generate close to 1000 points by using np.random.rand on an array with the same shape as arr_size and masking it with the ellipses and the condition < 1000 / (area of ellipses):
from matplotlib import pyplot as plt
import numpy as np
POINTS = 1000
def ellipse(coords, center, offset):
a = np.random.randint(low=5, high=10)
b = np.random.randint(low=7, high=10)
c = 10
xs, ys, zs = coords
cx, cy, cz = center
ox, oy, oz = offset
return ((xs - cx - ox) / a)**2 + ((ys - cy - oy) / b)**2 + ((zs - cz - oz) / c)**2 <= 1
def create_bin_pepper(arr_size, center):
x, y, z = arr_size
coords = np.ogrid[:x, :y, :z]
ellipses = [ellipse(coords, center, offset) for offset in ((0, 0, 0), (3, 5, 0), (-3, 5, 0))]
ellipses = np.logical_or.reduce(ellipses)
area = ellipses.sum()
random_points = np.where(ellipses, np.random.rand(*arr_size) < POINTS / area, 0)
return random_points
arr_size = (300, 300, 300)
sphere_center = (150, 150, 150)
pepper = create_bin_pepper(arr_size, sphere_center)
print(pepper.sum())
Which should be close to the number of points you need to generate

Welcome to StackOverflow!
It looks like you are re-assigning the centre_* and inside_ellipse* variables on each loop iteration without using the previous values at all. I assume it was left in from benchmarking tests, but if that is not the case, you can simply remove the for im in range(0,1000) loop and already achieve a 1000x speedup.

Related

How to simulate a heat diffusion on a rectangular ring with FiPy?

I am new to solving a PDE and experimenting with a heat diffusion on a copper body of a rectangular ring shape using FiPy.
And this is a plot of simulation result at some times.
I am using the Grid2D() for a mesh and the CellVariable.constrain() to specify boundary conditions. The green dots are centers of exterior faces where T = 273.15 + 25 (K), and blue dots are centers of interior faces where T = 273.15 + 30 (K).
Obviously, I am doing something wrong, because the temperature goes down to 0K. How should I specify boundary conditions correctly?
These are the code.
import numpy as np
import matplotlib.pyplot as plt
import fipy
def get_mask_of_rect(mesh, x, y, w, h):
def left_id(i, j): return mesh.numberOfHorizontalFaces + i*mesh.numberOfVerticalColumns + j
def right_id(i, j): return mesh.numberOfHorizontalFaces + i*mesh.numberOfVerticalColumns + j + 1
def bottom_id(i, j): return i*mesh.nx + j
def top_id(i, j): return (i+1)*mesh.nx + j
j0, i0 = np.floor(np.array([x, y]) / [mesh.dx, mesh.dy]).astype(int)
n, m = np.round(np.array([w, h]) / [mesh.dx, mesh.dy]).astype(int)
mask = np.zeros_like(mesh.exteriorFaces, dtype=bool)
for i in range(i0, i0 + n):
mask[left_id(i, j0)] = mask[right_id(i, j0 + m-1)] = True
for j in range(j0, j0 + m):
mask[bottom_id(i0, j)] = mask[top_id(i0 + n-1, j)] = True
return mask
mesh = fipy.Grid2D(Lx = 1, Ly = 1, nx = 20, ny = 20) # Grid of size 1m x 1m
k_over_c_rho = 3.98E2 / (3.85E2 * 8.96E3) # The thermal conductivity, specific heat capacity, and density of Copper in MKS
dt = 0.1 * (mesh.dx**2 + mesh.dy**2) / (4*k_over_c_rho)
T0 = 273.15 # 0 degree Celsius in Kelvin
T = fipy.CellVariable(mesh, name='T', value=T0+25)
mask_e = mesh.exteriorFaces
T.constrain(T0+25., mask_e)
mask_i = get_mask_of_rect(mesh, 0.25, 0.25, 0.5, 0.5)
T.constrain(T0+30, mask_i)
eq = fipy.TransientTerm() == fipy.DiffusionTerm(coeff=k_over_c_rho)
viewer = fipy.MatplotlibViewer(vars=[T], datamin=0, datamax=400)
plt.ioff()
viewer._plot()
plt.plot(*mesh.faceCenters[:, mask_e], '.g')
plt.plot(*mesh.faceCenters[:, mask_i], '.b')
def update():
for _ in range(10):
eq.solve(var=T, dt=dt)
viewer._plot()
plt.draw()
timer = plt.gcf().canvas.new_timer(interval=50)
timer.add_callback(update)
timer.start()
plt.show()
.constrain() does not work for internal faces (see the warning at the end of Applying internal “boundary” conditions).
You can achieve an internal fixed value condition using sources, however. As a first cut, try
mask_i = get_mask_of_rect(mesh, 0.25, 0.25, 0.5, 0.5)
mask_i_cell = fipy.CellVariable(mesh, value=False)
mask_i_cell[mesh.faceCellIDs[..., mask_i]] = True
largeValue = 1e6
eq = (fipy.TransientTerm() == fipy.DiffusionTerm(coeff=k_over_c_rho)
- fipy.ImplicitSourceTerm(mask_i_cell * largeValue)
+ mask_i_cell * largeValue * (T0 + 30))
This constrains the cells on either side of the faces identified by mask_i to be at T0+30.
I am posting my own answer for future readers.
For boundary conditions for internal faces, you should use the implicit and explicit source terms on the equation, as in the jeguyer's answer.
By using source terms, you don't need to calculate a mask for faces, like this.(The get_mask_of_rect() in my question isn't required.)
T = fipy.CellVariable(mesh, name = 'T', value = T0 + 25)
mask_e = mesh.exteriorFaces
T.constrain(T0 + 25., mask_e)
mask_i_cell = (
(0.25 < mesh.x) & (mesh.x < 0.25 + 0.5) &
(0.25 < mesh.y) & (mesh.y < 0.25 + 0.5)
)
large_value = 1E6
eq = fipy.TransientTerm() == (
fipy.DiffusionTerm(coeff = k_over_c_rho) -
fipy.ImplicitSourceTerm(mask_i_cell * large_value) +
mask_i_cell * (large_value * (T0 + 30) # explicit source
))
viewer = fipy.MatplotlibViewer(vars = [T], datamin = T0, datamax = T0+50)

Could someone help me with creating a single output after a delay from my code?

I'm using a thermal camera with Python code on my Raspberry Pi. I inserted some code yesterday that'll allow me to find the radius of where a fire is on the thermal camera and I'm going to output the theta in a different code.
What I'm having trouble with however is showcasing one output rather than a consistent output every second (or in respect to the refresh rate). Is there a way to accomplish this?
Here is my code below:
import time,board,busio
import numpy as np
import adafruit_mlx90640
import matplotlib.pyplot as plt
import math
extent = (-16, 16, -12.5, 12.5)
i2c = busio.I2C(board.SCL, board.SDA, frequency=800000)
mlx = adafruit_mlx90640.MLX90640(i2c)
mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_1_HZ
mlx_shape = (24,32)
plt.ion()
fig,ax = plt.subplots(figsize=(12,7))
therm1 = ax.imshow(np.zeros(mlx_shape),vmin=0, vmax=60, extent=extent)
cbar = fig.colorbar(therm1)
cbar.set_label('Temperature [$^{\circ}$C]', fontsize=14)
frame = np.zeros((2432,))
t_array = []
np.array
print("Starting loop")
while True:
t1 = time.monotonic()
try:
mlx.getFrame(frame)
data_array = (np.reshape(frame,mlx_shape))
therm1.set_data(np.reshape(frame,mlx_shape))
therm1.set_clim(vmin=np.min(data_array))
cbar.update_normal(therm1)
plt.title("Max")
plt.pause(0.001)
t_array.append(time.monotonic() - t1)
# fig.savefig('mlx90640_test_fliplr.png', dpi=300, facecolor = '#FCFCFC', bbox_inches='tight')
highest_num = data_array[0][0]
x = 0
y = 0
for i in range (len(data_array)):
for j in range(len(data_array[i])):
if data_array[x][y] < data_array[i][j]:
x = i
y = j
highest_num = data_array[i][j]
idx = np.argmax(data_array)
m, n = len(data_array), len(data_array[0])
r, c = m - (idx // n) - 1 , idx % n
y, x = r - (m // 2), c - (n // 2)
radius = math.sqrt( x x + y * y)
theta = math.atan(y/x)
theta = 180 * theta/math.pi
print("Radius", radius)
except ValueError:
continue

Efficient boolean disk packing mask

I need to make a mask of hexagonal packed disks. The code below does the job, but I don't feel like its efficient. I'm learning python as well so I'd love to get some expert advice on how to do this more computationally efficient.
r = 0.01
X, Y = np.mgrid[0:1:1000j, 0:1:1000j]
mask = np.full(X.shape, False)
px, py = np.mgrid[r : 1 : 2 * r * np.sqrt(3), r : 1 + r + np.finfo(float).eps: 2 * r]
px = np.vstack((px, px + r * np.sqrt(3)))
py = np.vstack((py, py - r))
fig, ax = plt.subplots(figsize= (12, 12), dpi=50)
img = ax.imshow(mask * 1, cmap = 'gray', vmin = 0, vmax = 1, extent = [0, 1, 0, 1])
for i, _ in np.ndenumerate(px): #is this loop dumb and inefficient?
C = (X - px[i]) ** 2 + (Y - py[i]) ** 2
mask = mask | (C < r ** 2)
img.set_data(mask * 1)
ax.set_aspect(1)
In particular, is there a way to vectorize the for loop?
Thanks
It may be efficient to create a single tile of the pattern, and then repeat it horizontally and vertically as needed:
Create a tile:
import numpy as np
import matplotlib.pyplot as plt
r = 0.01
sq3 = np.sqrt(3)
samples = 1000
X, Y = np.mgrid[1:(1 + 2 * sq3):int(sq3 * samples) * 1j, 0:2:samples * 1j]
XY = np.c_[X.flatten(), Y.flatten()]
# coordinates of centers of disks; suffices to take disks of radius 1 here
p = np.array([[1, 1], [(1 + sq3), 0], [(1 + sq3), 2], [(1 + 2 * sq3), 1]])
# compute the distance from each point of XY to each disk center
dist = (XY**2).sum(axis=1).reshape(-1, 1) + (p**2).sum(axis=1) - 2 * (XY # p.T)
# mask points outside the disks
tile = (np.min(dist, axis=1) < 1).reshape(X.shape)
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_aspect(1)
plt.imshow(tile, extent=[0, 2 * r, r, (1 + 2 * sq3) * r]);
It gives:
Repeat the tile:
# the number of times to repeat the tile in the horizontal and vertical directions
h, w = 20, 30
# donwsample the tile as needed and replicate
sample_rate = 10
mask = np.tile(tile[::sample_rate, ::sample_rate], (h, w))
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect(1)
ax.imshow(mask, extent=[0, 2 * r * w, 0, 2 * sq3 * r * h]);
This gives:

Notch Reject Filtering in Python

I'm trying to implement notch-reject filtering in python for an assignment. I have tried using the notch reject filter formula from Rafael Gonzales book and all I got was a edge detected image. Then I tried ideal notch rejecting and here are the results:
Input image--Output of my program -- Expected output
Here is my code:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def notch_reject_filter(shape, d0=9, u_k=0, v_k=0):
P, Q = shape
# Initialize filter with zeros
H = np.zeros((P, Q))
# Traverse through filter
for u in range(0, P):
for v in range(0, Q):
# Get euclidean distance from point D(u,v) to the center
D_uv = np.sqrt((u - P / 2 + u_k) ** 2 + (v - Q / 2 + v_k) ** 2)
D_muv = np.sqrt((u - P / 2 - u_k) ** 2 + (v - Q / 2 - v_k) ** 2)
if D_uv <= d0 or D_muv <= d0:
H[u, v] = 0.0
else:
H[u, v] = 1.0
return H
img = cv2.imread('input.png', 0)
img_shape = img.shape
original = np.fft.fft2(img)
center = np.fft.fftshift(original)
NotchRejectCenter = center * notch_reject_filter(img_shape, 32, 50, 50)
NotchReject = np.fft.ifftshift(NotchRejectCenter)
inverse_NotchReject = np.fft.ifft2(NotchReject) # Compute the inverse DFT of the result
plot_image = np.concatenate((img, np.abs(inverse_NotchReject)),axis=1)
plt.imshow(plot_image, "gray"), plt.title("Notch Reject Filter")
plt.show()
all I got was a edge detected image because your implementation was High pass filter which is a black circle in the middle, and that works as Edge detector.
Then I tried ideal notch rejecting This is correct if you applied that correctly.
The main concept is to filter the undesired Noise in the frequency domain, the noise can be seen as white spots, and your role is to suppress that white spots by multiplying them by black circles in frequency domain(known as filtering).
to improve this result add more notch filters (H5, H6, ...) to suppress the noise.
import cv2
import numpy as np
import matplotlib.pyplot as plt
#------------------------------------------------------
def notch_reject_filter(shape, d0=9, u_k=0, v_k=0):
P, Q = shape
# Initialize filter with zeros
H = np.zeros((P, Q))
# Traverse through filter
for u in range(0, P):
for v in range(0, Q):
# Get euclidean distance from point D(u,v) to the center
D_uv = np.sqrt((u - P / 2 + u_k) ** 2 + (v - Q / 2 + v_k) ** 2)
D_muv = np.sqrt((u - P / 2 - u_k) ** 2 + (v - Q / 2 - v_k) ** 2)
if D_uv <= d0 or D_muv <= d0:
H[u, v] = 0.0
else:
H[u, v] = 1.0
return H
#-----------------------------------------------------
img = cv2.imread('input.png', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
phase_spectrumR = np.angle(fshift)
magnitude_spectrum = 20*np.log(np.abs(fshift))
img_shape = img.shape
H1 = notch_reject_filter(img_shape, 4, 38, 30)
H2 = notch_reject_filter(img_shape, 4, -42, 27)
H3 = notch_reject_filter(img_shape, 2, 80, 30)
H4 = notch_reject_filter(img_shape, 2, -82, 28)
NotchFilter = H1*H2*H3*H4
NotchRejectCenter = fshift * NotchFilter
NotchReject = np.fft.ifftshift(NotchRejectCenter)
inverse_NotchReject = np.fft.ifft2(NotchReject) # Compute the inverse DFT of the result
Result = np.abs(inverse_NotchReject)
plt.subplot(222)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.subplot(221)
plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('magnitude spectrum')
plt.subplot(223)
plt.imshow(magnitude_spectrum*NotchFilter, "gray")
plt.title("Notch Reject Filter")
plt.subplot(224)
plt.imshow(Result, "gray")
plt.title("Result")
plt.show()
Drive by comment, using the for-loop for the notch filter generation is very slow. That operation can be optimized
def notch_reject_filter_vec(shape: tuple[int, int], d0: int, u_k: int, v_k: int):
(M, N) = shape
H_0_u = np.repeat(np.arange(M), N).reshape((M, N))
H_0_v = np.repeat(np.arange(N), M).reshape((N, M)).transpose()
D_uv = np.sqrt((H_0_u - M / 2 + u_k) ** 2 + (H_0_v - N / 2 + v_k) ** 2)
D_muv = np.sqrt((H_0_u - M / 2 - u_k) ** 2 + (H_0_v - N / 2 - v_k) ** 2)
selector_1 = D_uv <= d0
selector_2 = D_muv <= d0
selector = np.logical_or(selector_1, selector_2)
H = np.ones((M, N))
H[selector] = 0
return H

Offset for SciPy.optimize least squares is not correct

I have a script which generates a noisy curve composed of 3 different sine curves. Using LombScargle I find the periods which are dominant because of these curves and detrend the original curve.
This involves phase folding, using SciPy optimize to fit a sine curve and then extrapolating the fitted curve out of phase space and onto the original multi-sine-wave-curve, and then subtracting this fit to detrend the data.
I iterate this process so that eventually when the signal on the LombScargle is less than 3 times some pre-generated noise height, the iteration stops. (equivalent to signal-to-noise ratio becoming too small).
But after the 1st iteration the phase of the fitted curve is way off! I can't see why this happens though. Can anyone help?
import matplotlib.pyplot as plt
import numpy as np
import math
from scipy.optimize import leastsq
from astropy.stats import LombScargle
t = np.linspace(15,30,1000)
y = 5 * np.sin(2*np.pi * 1./2. * (t + 1)) + 30
y1 = 11 * np.sin(2*np.pi * 1./3. * (t + 1))
y4 = 2 * np.sin(2*np.pi * 1./7. * (t + 1))
sampl = np.random.uniform(low = 0, high = 2.5, size = len(t))
y2 = y+y1+y4+sampl
y2 = y2/np.nanmedian(y2)
freq = np.linspace(1./150.,1./0.5,10000)
power2 = LombScargle(t,sampl).power(freq)
for _ in range(5):
plt.figure()
plt.subplot(221)
plt.plot(t,y2,'-')
plt.grid()
power = LombScargle(t,y2).power(freq)
p = 1./freq[np.argmax(power)]
plt.title(p)
plt.subplot(222)
plt.plot(1./freq,power)
plt.grid()
# print p
if np.max(power) <= np.max(power2)*3:
print 'done'
break
else:
foldtimes = (t - t[0])/p
FoldTimes = foldtimes % 1
p_0 = np.min(FoldTimes)
dp = 0.01
its = math.ceil((np.max(FoldTimes)-p_0)/dp)
n = np.linspace(0,its,its+1)
binned_flux = []
binned_phase = []
for i in range(len(n)):
indices = np.where(((p_0 + (n[i]*dp)) <= FoldTimes) & (FoldTimes <= (p_0 + (n[i]*dp)+dp)))[0]
if len(indices)>1:
binned_flux.append( np.nanmedian(y2[indices])) ##continuously adds the averaged fluxes to list
binned_phase.append (np.mean(FoldTimes[indices])) #same for hjs averages
binned_flux = np.array(binned_flux)
binned_phase = np.array(binned_phase)
plt.subplot(223)
plt.grid()
plt.plot(binned_phase,binned_flux,'.',linestyle='None')
plt.ylabel('Relative Flux')
plt.xlabel('Phase')
guess_mean = np.mean(y2)
guess_A = (np.max(y2) - np.min(y2))/2.
guess_phase = 0
optimize_func = lambda x: ((x[0] * np.sin(2*np.pi*(np.sort(binned_phase) + x[1]))) + x[2]) - binned_flux
est_A, est_phase, est_mean = leastsq(optimize_func, [guess_A,guess_phase,guess_mean])[0]
print est_phase
fit = (est_A*np.sin(2*np.pi*(1./p)*(t+ (est_phase*p)))) + est_mean
fit_p = (est_A*np.sin(2*np.pi*(1.)*(np.sort(binned_phase)+(est_phase)))) + est_mean
plt.plot(binned_phase,fit_p)
plt.subplot(224)
plt.plot(t,y2,'-')
plt.plot(t,fit)
plt.grid()
y2 = (y2 - fit) + est_mean #rewrites the original curve minus the fit

Categories

Resources