How to use interpolation with solve_ivp? - python

I am trying to make some code that calculates the trajectory of a particle, given its initial position and the velocity field. Here is an example of a working bit of code that nearly does what I want:
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def velocity(t, coord):
# velocity gives the velocity of the particle
# coord gives the coordinate of the particle where
# coord[0] = x, coord[1] = y and coord[2] = z
return [coord[0], coord[1], -2 * coord[2]]
n_points = 100
lim = 5
coord0 = [2, 2, 5]
t = np.linspace(0, lim, n_points)
sol = solve_ivp(velocity, [0, lim], t_eval = t, y0 = coord0)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ln = ax.plot3D(sol.y[0], sol.y[1], sol.y[2])
ax.set_xlim((0, lim))
ax.set_ylim((0, lim))
ax.set_zlim((0, lim))
plt.savefig('particle_trajectory.png')
Here is the outputted figure:
I want the code to calculate the particle's trajectory if I only have an array describing the velocity field. My plan is to use interpolation so I can calculate the particle's trajectory using a similar method to that shown above. Here is my attempt to do this:
import numpy as np
from scipy.interpolate import RegularGridInterpolator
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def velocity(t, coord):
# velocity gives the velocity of the particle
# coord gives the coordinate of the particle where
# coord[0] = x, coord[1] = y and coord[2] = z
return [vx_interp([coord[0], coord[1], coord[2]]), \
vy_interp([coord[0], coord[1], coord[2]]), \
vz_interp([coord[0], coord[1], coord[2]])]
n_points = 100
lim = 5
# Create an example of a velocity array
x = np.linspace(0, lim, n_points)
y = np.linspace(0, lim, n_points)
z = np.linspace(0, lim, n_points)
X, Y, Z = np.meshgrid(x, y, z)
vx = X
vy = Y
vz = -2 * Z
vx_interp = RegularGridInterpolator((x, y, z), vx)
vy_interp = RegularGridInterpolator((x, y, z), vy)
vz_interp = RegularGridInterpolator((x, y, z), vz)
coord0 = [2, 2, 5]
t_range = np.linspace(0, 0.2 * lim, n_points)
sol = solve_ivp(velocity, [0, 2 * lim], t_eval = t_range, y0 = coord0)
But this gives the following error:
File "/home/lex/.local/lib/python3.8/site-packages/scipy/integrate/_ivp/ivp.py", line 542, in solve_ivp
solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
File "/home/lex/.local/lib/python3.8/site-packages/scipy/integrate/_ivp/rk.py", line 96, in __init__
self.h_abs = select_initial_step(
File "/home/lex/.local/lib/python3.8/site-packages/scipy/integrate/_ivp/common.py", line 111, in select_initial_step
f1 = fun(t0 + h0 * direction, y1)
File "/home/lex/.local/lib/python3.8/site-packages/scipy/integrate/_ivp/base.py", line 138, in fun
return self.fun_single(t, y)
File "/home/lex/.local/lib/python3.8/site-packages/scipy/integrate/_ivp/base.py", line 20, in fun_wrapped
return np.asarray(fun(t, y), dtype=dtype)
File "Python/low_and_lou/stack2.py", line 10, in velocity
return [vx_interp([coord[0], coord[1], coord[2]]), \
File "/home/lex/.local/lib/python3.8/site-packages/scipy/interpolate/interpolate.py", line 2509, in __call__
raise ValueError("One of the requested xi is out of bounds "
ValueError: One of the requested xi is out of bounds in dimension 2
I think the problem is that I am using a 'RegularGridInterpolator' inside of the solve_ivp routine. Do you know how to fix this problem?

Related

plt.savefig() overwrite the previous image

I am working on a image unfolding assignment, in my program, I using glob to read multiple images from my pc, and after that after the program unfolded my image, I want to save it in another directory at once, but when I run the program, every new save image will overwrite or replace the previous image, I had try different way to solve it, but all of them failed. Please help me to solve, I will be very thankful for any assistance.
Here is my code:
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.ndimage
from PIL import Image
#import cv2
import matplotlib
import glob
images = glob.glob(r'D:\Master of FOE\pythonProject\Program\Dataset\For New Training\NORMAL IMAGE\GOOD\*.png')
thumb_width = 200
Unfolded_image=[]
def main():
for im in images:
im = Image.open(im)
im = im.convert('RGB')
data = np.array(im)
print('Data',data.shape,im.size)
ch0=np.array(im.getchannel(0))
ch0 = np.where(ch0 > 0, 1, 0)
ch1=np.array(im.getchannel(1))
ch1 = np.where(ch1 > 0, 1, 0)
ch2=np.array(im.getchannel(2))
ch2 = np.where(ch2 > 0, 1, 0)
dt0 = np.where(((ch2+ch1+ch0) > 0), 1, 0).T
print('DTTT',dt0.shape)
tx=0
ty=0
for xx in range(im.width):
for yy in range(im.height):
tx+=xx*dt0[xx][yy]
ty+=yy*dt0[xx][yy]
ts = np.sum(dt0)
txc=tx/ts
tyc=ty/ts
rr=max(im.width,im.height)
for xx in range(im.width):
for yy in range(im.height):
if dt0[xx][yy]==0:
r=np.sqrt((xx -txc)** 2 + (yy-tyc) ** 2)
if r<rr:
rr=r
print('RSRS',rr)
rm=rr
print('RRR',rm)
print('Center',txc,tyc)
print('DT0',dt0)
print(11, data[0])
image = plot_polar_image(data, rm, origin=(txc, tyc))
Unfolded_image.append(image)
for i in enumerate(Unfolded_image):
plt.axis('off')
plt.savefig(r'D:\Master of FOE\pythonProject\Program\Dataset\For New Training\UNFOLLDED IMAGE\UNFOLDED_GOOD\UG{0}.png'.format(i), bbox_inches='tight', transparent=True, pad_inches=0)
def plot_polar_image(data, rm,origin=None):
polar_grid, r, theta = reproject_image_into_polar(data,rm, origin)
plt.imshow(polar_grid, extent=(theta.min(), theta.max(), r.max(), r.min()))
plt.axis('auto')
def index_coords(data, origin=None):
ny, nx = data.shape[:2]
if origin is None:
origin_x, origin_y = nx // 2, ny // 2
else:
origin_x, origin_y = origin
x, y = np.meshgrid(np.arange(nx), np.arange(ny))
x=x.astype('float64')
y=y.astype('float64')
x -= origin_x
y -= origin_y
print(999,x,y)
return x, y
def cart2polar(x, y):
r = np.sqrt(x**2 + y**2)
print('RR',r)
theta = np.arctan2(y, x)
print(33,r,theta)
return r, theta
def polar2cart(r, theta):
x = r * np.cos(theta)
y = r * np.sin(theta)
return x, y
def bin_by(x, y, nbins=30):
bins = np.linspace(y.min(), y.max(), nbins+1)
bins[-1] += 1
indicies = np.digitize(y, bins)
output = []
for i in range(1, len(bins)):
output.append(x[indicies==i])
bins = bins[:-1]
return output, bins
def reproject_image_into_polar(data,rm, origin=None):
print(22,data.shape)
nn=data.shape[:2]
ny=nx=min(nn)
if origin is None:
origin = (nx//2, ny//2)
rmin=min(nx-origin[0],origin[0],nx//2,ny-origin[1],origin[1],ny//2)
x, y = index_coords(data, origin=origin)
r, theta = cart2polar(x, y)
print(111,r.min(),r.max())
r_i = np.linspace(min(0,r.min(),rm), min(r.max(),rmin), nx)
print(222,theta.min(), theta.max())
theta_i = np.linspace(-np.pi,np.pi, ny)
theta_grid, r_grid = np.meshgrid(theta_i, r_i)
print(66,theta_grid,r_grid)
xi, yi = polar2cart(r_grid, theta_grid)
xi += origin[0]
yi += origin[1]
xi, yi = xi.flatten(), yi.flatten()
print(99,xi,yi)
coords = np.vstack((xi, yi))
print(55,coords)
bands = []
print(88,data.shape)
print(77,data.T.shape)
for band in data.T:
print(44,band)
zi = sp.ndimage.map_coordinates(band, coords, order=1)
bands.append(zi.reshape((nx, ny)))
output = np.dstack(bands)
return output, r_i, theta_i
if __name__ == '__main__':
main()
I am expecting a program that can successfully read, process and save my images, please fo me a favor, it will be very meaningful for me!

Reconstructing polynomials from scipy.interpolate.RectBivariateSpline

I have fitted a 2-D cubic spline using scipy.interpolate.RectBivariateSpline. I would like to access/reconstruct the underlying polynomials within each rectangular cell. How can I do this? My code so far is written below.
I have been able to get the knot points and the coefficients with get_knots() and get_coeffs() so it should be possible to build the polynomials, but I do not know the form of the polynomials that the coefficients correspond to. I tried looking at the SciPy source code but I could not locate the underlying dfitpack.regrid_smth function.
A code demonstrating the fitting:
import numpy as np
from scipy.interpolate import RectBivariateSpline
# Evaluate a demonstration function Z(x, y) = sin(sin(x * y)) on a mesh
# of points.
x0 = -1.0
x1 = 1.0
n_x = 11
x = np.linspace(x0, x1, num = n_x)
y0 = -2.0
y1 = 2.0
n_y = 21
y = np.linspace(y0, y1, num = n_y)
X, Y = np.meshgrid(x, y, indexing = 'ij')
Z = np.sin(np.sin(X * Y))
# Fit the sampled function using SciPy's RectBivariateSpline.
order_spline = 3
smoothing = 0.0
spline_fit_func = RectBivariateSpline(x, y, Z,
kx = order_spline, ky = order_spline, s = smoothing)
And to plot it:
import matplotlib.pyplot as plt
# Make axes.
fig, ax_arr = plt.subplots(1, 2, sharex = True, sharey = True, figsize = (12.0, 8.0))
# Plot the input function.
ax = ax_arr[0]
ax.set_aspect(1.0)
d_x = x[1] - x[0]
x_edges = np.zeros(n_x + 1)
x_edges[:-1] = x - (d_x / 2.0)
x_edges[-1] = x[-1] + (d_x / 2.0)
d_y = y[1] - y[0]
y_edges = np.zeros(n_y + 1)
y_edges[:-1] = y - (d_y / 2.0)
y_edges[-1] = y[-1] + (d_y / 2.0)
ax.pcolormesh(x_edges, y_edges, Z.T)
ax.set_title('Input function')
# Plot the fitted function.
ax = ax_arr[1]
ax.set_aspect(1.0)
n_x_span = n_x * 10
x_span_edges = np.linspace(x0, x1, num = n_x_span)
x_span_centres = (x_span_edges[1:] + x_span_edges[:-1]) / 2.0
#
n_y_span = n_y * 10
y_span_edges = np.linspace(y0, y1, num = n_y_span)
y_span_centres = (y_span_edges[1:] + y_span_edges[:-1]) / 2.0
Z_fit = spline_fit_func(x_span_centres, y_span_centres)
ax.pcolormesh(x_span_edges, y_span_edges, Z_fit.T)
x_knot, y_knot = spline_fit_func.get_knots()
X_knot, Y_knot = np.meshgrid(x_knot, y_knot)
# Plot the knots.
ax.scatter(X_knot, Y_knot, s = 1, c = 'r')
ax.set_title('Fitted function and knots')
plt.show()

Plotting vector field for first order differential equation

I'm trying to plot the direction fields for a simple velocity equation. I understand what I have to do when I'm working with two variables. I can understand the vector I have to create, but I don't understand how to do it for only one variable. My program is:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def modelo2(y, t):
dydt = 32 - 0.16 * y
return dydt
t0 = 0 ; tf = 25 ; h = 0.1
t = np.arange(t0,tf+h,h)
for y0 in np.arange(0, 400, 25):
y = odeint(modelo2,y0,t )
plt.plot(t,y,'b')
x = np.arange(0, 400, 20)
z = np.arange(0, 400, 20)
X, Z = np.meshgrid(x, z)
U = modelo2(X,t)
V = modelo2 (Z, t)
plt.quiver(X, Z, U, V, scale = 70)
plt.quiver(X, Z, U, V, scale = 60)
plt.xlabel('time')
plt.ylabel('y(t)')
plt.axis([0,20,0, 500])
plt.show()
I get this
When I expect something like this
Can someone explain what I'm doing wrong?
Change this
U = modelo2(X,t)
V = modelo2 (Z, t)
to this
U = 1.0
V = modelo2(Z, None)
N = np.sqrt(U**2 + V**2)
U /= N
V /= N
As you can see you defined U wrong. Diving both U and V by N is necessary to normalise the magnitude of the vectors, otherwise their length in the plot will vary according to the strength of the field at each point. Just set U = np.ones(Z.shape) and don't divide either by N to see what I'm talking about.
Secondly, you need to set the following argument in plt.quiver()
plt.quiver(X, Z, U, V, angles='xy')
From the docs:
angles : {'uv', 'xy'} or array-like, optional, default: 'uv'
Method for determining the angle of the arrows.
- 'uv': The arrow axis aspect ratio is 1 so that
if *U* == *V* the orientation of the arrow on the plot is 45 degrees
counter-clockwise from the horizontal axis (positive to the right).
Use this if the arrows symbolize a quantity that is not based on
*X*, *Y* data coordinates.
- 'xy': Arrows point from (x, y) to (x+u, y+v).
Use this for plotting a gradient field, for example.
- Alternatively, arbitrary angles may be specified explicitly as an array
of values in degrees, counter-clockwise from the horizontal axis.
In this case *U*, *V* is only used to determine the length of the
arrows.
Note: inverting a data axis will correspondingly invert the
arrows only with ``angles='xy'``.
All in all, your code should look like this (with some minor variable name edits):
def modelo2(y, t):
dydt = 32 - 0.16 * y
return dydt
t0, tf, h = 0, 25, 0.1
t = np.arange(t0, tf+h, h)
ymin, ymax, ystep = 0, 400, 25
y = np.arange(ymin, ymax+ystep, ystep)
for y0 in y:
line = odeint(modelo2, y0, t)
plt.plot(t, line, 'b')
x = np.linspace(t0, tf, 20)
X, Y = np.meshgrid(x, y)
U = 1
V = modelo2(Y, None)
N = np.sqrt(U**2 + V**2)
U /= N
V /= N
plt.quiver(X, Y, U, V, angles='xy')
plt.xlabel('time')
plt.ylabel('y(t)')
plt.axis([t0, tf, ymin, ymax])
plt.show()
Result

How to use scipy.optimize.minimize(...) to find the optimal parameters of z = f(x, y) (like an ellipse)?

I am trying to learn how to optimize data fits in higher dimensions using python / numpy / scipy. Having successfully used scipy to fit a line of the form y = f(x), I tried extending the logic to fit an ellipse of the form z = f(x, y); both are shown below. I'm hoping this approach can be generalized to fit shapes in higher dimensions (ie, sphere).
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
## LINE EXAMPLE
npoints = 30
x = np.linspace(-5, 5, npoints)
m, b = 3, -4 # slope, y-intercept
initial_parameter_guess = (2.5, -1)
y = m * x + b # exact line
noise = np.random.uniform(-1, 1, size=x.size)
yn = y + noise # line with noise
def get_residuals(prms, x, y):
""" """
return (y - (prms[0] * x + prms[1]))**2
def f_error(prms, x, y):
""" """
resid = get_residuals(prms, x, y)
return np.sum(resid)
result = minimize(f_error, x0=initial_parameter_guess, args=(x, yn))
# print(result)
yf = result.x[0] * x + result.x[1]
fig, ax = plt.subplots()
ax.scatter(x, yn, color='b', marker='.')
ax.plot(x, yf, color='r', alpha=0.5)
ax.grid(color='k', alpha=0.3, linestyle=':')
plt.show()
plt.close(fig)
Applying this logic to the case of an ellipse,
## ELLIPSE EXAMPLE
npoints = 75
theta = np.random.uniform(0, 2*np.pi, size=npoints)
a, b = (3, 5)
initial_parameter_guess = (2.5, 6)
xnoise = np.random.uniform(-1, 1, size=theta.size)
ynoise = np.random.uniform(-1, 1, size=theta.size)
x = a**2 * np.cos(theta)
xn = x + xnoise
y = b**2 * np.sin(theta)
yn = y + ynoise
def get_residuals(prms, x, y):
""" """
return 1 - ((x/prms[0])**2 + (y/prms[1])**2)
def f_error(prms, x, y):
""" """
resid = get_residuals(prms, x, y)
return np.sum(resid)
result = minimize(f_error, x0=initial_parameter_guess, args=(xn, yn))
# print(result)
The minimize algorithm via scipy does not find the optimal parameters; the following output is shown with print(result):
fun: -4243.611573066522
hess_inv: array([[41.44400248, 39.68101343],
[39.68101343, 37.99343048]])
jac: array([-1496.81719971, 2166.68896484])
message: 'Desired error not necessarily achieved due to precision loss.'
nfev: 174
nit: 1
njev: 42
status: 2
success: False
x: array([-1.51640333, 2.93879038])
I have seen another solution to this problem, which use the matrix formulation of least squares, but the example is a bit difficult for me to follow. Almost all approaches I've seen are based on the approach in the posted link. I'd prefer using minimize unless the linear algebra approach was better for a reason that I am currently unaware of.
Anyways, can my approach above be adapted/tweaked in a way that will work and can be generalized for higher dimensions?
Two issues with the code . Instead of minimizing the sum of residuals , minimize the sum of squares of residuals. Secondly , elliptic equation should be defined as x=a*np.cos(theta) and y=b*np.sin(theta)
npoints = 75
theta = np.random.uniform(0, 2*np.pi, size=npoints)
a, b = (3, 5)
initial_parameter_guess = (2.5, 6)
xnoise = np.random.uniform(-1, 1, size=theta.size)
ynoise = np.random.uniform(-1, 1, size=theta.size)
x = a * np.cos(theta)
xn = x + xnoise
y = b * np.sin(theta)
yn = y + ynoise
def get_residuals(prms, x, y):
""" """
return 1 - ((x/prms[0])**2 + (y/prms[1])**2)
def f_error(prms, x, y):
""" """
resid = get_residuals(prms, x, y)
return np.sum(np.square(resid))
result = minimize(f_error, x0=initial_parameter_guess,args=(xn, yn))
result
fun: 5.85099318913613
hess_inv: array([[ 0.07025572, -0.02902779],
[-0.02902779, 0.12040811]])
jac: array([-5.96046448e-08, 1.19209290e-07])
message: 'Optimization terminated successfully.'
nfev: 48
nit: 10
njev: 12
status: 0
success: True
x: array([3.35248219, 5.13728323])

Numpy mask from cylinder coordinates

I generated the coordinates of a cylinder. Its two faces connect two arbitrary points already given.
Is it possible to build a 3D numpy mask of the filled cylinder from the coordinates with standard Python libraries? Creating a 2D mask seems simple enough, but I'm encountering some difficulties with 3D.
Here the code for generating the cylinder, taken from here and here:
import scipy
import scipy.linalg
import numpy as np
import nibabel as nib
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# defining mask
shape = (100, 100, 100)
image = np.zeros(shape=shape)
# set radius and centres values
r = 3
start = [30, 45, 60]
end = [40, 58, 70]
p1 = np.array(start)
p2 = np.array(end)
# # calculate p2-p1 distance
# dx = p2[0] - p1[0]
# dy = p2[1] - p1[1]
# dz = p2[2] - p1[2]
# dist = math.sqrt(dx**2 + dy**2 + dz**2)
# vector in direction of axis
v = p2 - p1
# find magnitude of vector
mag = scipy.linalg.norm(v)
# unit vector in direction of axis
v = v / mag
# make some vector not in the same direction as v
not_v = np.array([1, 0, 0])
if (v == not_v).all():
not_v = np.array([0, 1, 0])
# make vector perpendicular to v
n1 = np.cross(v, not_v)
# normalize n1
n1 /= scipy.linalg.norm(n1)
# make unit vector perpendicular to v and n1
n2 = np.cross(v, n1)
#surface ranges over t from 0 to length of axis and 0 to 2*pi
t = np.linspace(0, mag, 100)
theta = np.linspace(0, 2 * np.pi, 100)
rsample = np.linspace(0, r, 2)
#use meshgrid to make 2d arrays
t, theta2 = np.meshgrid(t, theta)
rsample, theta = np.meshgrid(rsample, theta)
# generate coordinates for surface
# "Tube"
X, Y, Z = [p1[i] + v[i] * t + r * np.sin(theta2) * n1[i] + r * np.cos(theta2) * n2[i] for i in [0, 1, 2]]
# "Bottom"
X2, Y2, Z2 = [p1[i] + rsample[i] * np.sin(theta) * n1[i] + rsample[i] * np.cos(theta) * n2[i] for i in [0, 1, 2]]
# "Top"
X3, Y3, Z3 = [p1[i] + v[i] * mag + rsample[i] * np.sin(theta) * n1[i] + rsample[i] * np.cos(theta) * n2[i] for i in [0, 1, 2]]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z)
ax.plot_surface(X2, Y2, Z2)
ax.plot_surface(X3, Y3, Z3)
plt.show()
I need the 3D numpy mask to select all the values inside the cylinder of a 3D image. The shape of mask and image is the same.
In the end I looped through the coordinates of tube and faces.
I got the coordinates following this link: 3D points from Numpy meshgrid coordinates
tube = np.stack((X.ravel(), Y.ravel(), Z.ravel()), axis=1)
face1 = np.stack((X2.ravel(), Y2.ravel(), Z2.ravel()), axis=1)
face2 = np.stack((X3.ravel(), Y3.ravel(), Z3.ravel()), axis=1)
# filling numpy mask
for i in range(len(tube)):
image[int(tube[i][0]), int(tube[i][1]), int(tube[i][2])] = 255
for j in range(len(face1)):
image[int(face1[j][0]), int(face1[j][1]), int(face1[j][2])] = 255
for k in range(len(face2)):
image[int(face2[k][0]), int(face2[k][1]), int(face2[k][2])] = 255
mask_new = nib.Nifti1Image(image.astype(np.float32), ctsurg_file.affine)
nib.save(mask_new, os.path.join(currdir, 'mask_cyl.nii.gz'))

Categories