For a project I want a grid like this: 5x5. The points should be movable later but I got that i guess.
What i wanna be able to do now is to interpolate for example 100x50 points in this grid of 5x5 marker points but not just linear, CUBIC in both axis. I cant wrap my head around it. I saw how to lay scipy.interpolate.CubicSpline through for example the 5 horizontal markers at the top but how do i combine it with the vertical warp?
is there a fnc to interpolate a grid in a given frame like this?
Use scipy.interpolate.interp2d:
Interpolate over a 2-D grid.
x, y and z are arrays of values used to approximate some function f: z = f(x, y) which returns a scalar value z. This class returns a function whose call method uses spline interpolation to find the value of new points.
So you have an array orig that you want to generate 100x50 array res using bicubic interpolation
# Adapted from https://stackoverflow.com/a/58126099/17595968
from scipy import interpolate as interp
import numpy as np
orig = np.random.randint(0, 100, 25).reshape((5, 5))
W, H = orig.shape
new_W, new_H = (100, 50)
map_range = lambda x: np.linspace(0, 1, x)
f = interp.interp2d(map_range(W), map_range(H), orig, kind="cubic")
res = f(range(new_W), range(new_H))
Edit:
If what you want is coordinates of 100x50 grid in 5x5 grid you can use numpy.meshgrid:
#From https://stackoverflow.com/a/32208788/17595968
import numpy as np
W, H = 5, 5
new_W, new_H = 100, 50
x_step = W / new_W
y_step = H / new_H
xy = np.mgrid[0:H:y_step, 0:W:x_step].reshape(2, -1).T
xy = xy.reshape(new_H, new_W, 2)
xy
Related
TL;DR: NumPy FFT creates non uniform output when output is wanted to be uniform. I want the output to be a uniform corona.
I am trying to eventually run a Gerchberg-Saxton phase retrieval algorithm. I have been trying to make sure that I understand how FFT works in NumPy. I have used fftshift to create the correct looking output but the image does not have uniform intensity afterwards.
My input image is a circle, output should be a coronagraph looking thing from the circle aperture. I am trying to reproduce the results detailed in https://www.osapublishing.org/optica/fulltext.cfm?uri=optica-2-2-147&id=311836#articleSupplMat.
My algorithm to produce the error:
Initial image, f
FT(f)
x exp ( i phase_mask)
IFT(FT(f)x exp( i phase_mask)
Happy to clear anything up.
import numpy as np
import matplotlib.pyplot as plt
#Create 'pixels' for circle
pixels = 400
edge = np.linspace(-10, 10, pixels)
xv, yv = np.meshgrid(edge, edge)
def circle(x, y, r):
'''
x, y : dimensions of grid to place circle on
r : radius
Function defines aperture
'''
x0 = 0
y0 = 0
return np.select([((x-x0)**2+(y-y0)**2)>=r**2,
((x-x0)**2+(y-y0)**2)<r**2],
[0,
1.])
#Create input and output images
radius = 4
input_img = circle(xv, yv, radius)
constraint_img = xcircle(xv, yv, radius)
img = input_img
constraint = 1 - img
max_iter = 10
re,im = np.mgrid[-1:1:400j, -1:1:400j] #Creates grid of values, 400=pixels
mask = 2*np.angle(re + 1j*im) #Gets angle from centre of grid
mask_i = mask
#Initial focal plane field, F. Initial image f.
f = np.sqrt(img)
F = np.fft.fftshift(np.fft.fft2(f)) * np.exp(mask * 1j) #Focal plane field
F_1 = F
am_f = np.abs(F_1) #Initial amplitude
g = np.fft.ifft2(F)
mask = np.angle(F/(F_1+1e-18)) #Final phase mask
recovery = (np.fft.ifft2(F*np.exp(-1j * mask)))
im3 = plt.imshow(np.abs(g)**2, cmap='gray')
plt.title('Recovered image')
plt.tight_layout()
plt.show()
plt.imshow(mask_i)
plt.colorbar()
plt.show()
Your issue is in this bit of code:
pixels = 400
edge = np.linspace(-10, 10, pixels)
as well as this one:
re,im = np.mgrid[-1:1:400j, -1:1:400j]
Because you use fftshift*, you need the origin to be at pixels//2. However, you don't sample the origin at all, it is in between two samples.
* You should really be using ifftshift instead, which moves the origin from pixels//2 to 0. fftshift moves the origin from 0 to pixels//2. For an even number of samples, these two do the same thing though.
To properly sample the origin, create edge as follows:
edge = np.linspace(-10, 10, pixels, endpoint=False)
We now see that edge[pixels//2] is equal to 0.
For np.mgrid there's no equivalent option. You will have to do this manually by creating one more sample, then deleting the last sample:
re,im = np.mgrid[-1:1:401j, -1:1:401j] #Creates grid of values, 400=pixels
mask = 2*np.angle(re + 1j*im) #Gets angle from centre of grid
mask = mask[:-1, :-1]
With these two changes, you will see a symmetric output.
When I create my canvas (0,0) is in the top right. I need a Cartesian coordinate system with its origin in the bottom middle of the window. Is there a way to change the coordinate system? If not how do I make a new one with these characteristics?
You can use a meshgrid.
For example see https://docs.scipy.org/doc/numpy/reference/generated/numpy.meshgrid.html
"meshgrid is very useful to evaluate functions on a grid."
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> x = np.arange(-5, 5, 0.1)
>>> y = np.arange(-5, 5, 0.1)
>>> xx, yy = np.meshgrid(x, y, sparse=True)
>>> z = np.sin(xx**2 + yy**2) / (xx**2 + yy**2)
>>> h = plt.contourf(x,y,z)
>>> plt.show()
Or without using a meshgrid you could also use two vectors to index the image:
Assuming your canvas is saved in a variable im you can the two cartesian axis with the origin in the bottom middle and then use them to index the pixels of you image:
x = np.arange(minx, maxx, 1)
y = np.arange(0, maxy, 1)
where minx and maxx should be -(im.shape[1] / 2) and (im.shape[1] / 2) (assuming your canvas has even number of columns.
And finally get one coordinate e.g. coord=(your_x, your_y) from im:
im[np.where(y==coord[1])[0], np.where(x==coord[0])[0]]
I have several data points in 3 dimensional space (x, y, z) and have interpolated them using scipy.interpolate.Rbf. This gives me a spline nicely representing the surface of my 3D object. I would now like to determine several x and y pairs that have the same, arbitrary z value. I would like to do that in order to compute the cross section of my 3D object at any given value of z. Does someone know how to do that? Maybe there is also a better way to do that instead of using scipy.interpolate.Rbf.
Up to now I have evaluated the cross sections by making a contour plot using matplotlib.pyplot and extracting the displayed segments. 3D points and interpolated spline
segments extracted using a contour plot
I was able to solve the problem. I have calculated the area by triangulating the x-y data and cutting the triangles with the z-plane I wanted to calculate the cross-sectional area of (z=z0). Specifically, I have searched for those triangles whose z-values are both above and below z0. Then I have calculated the x and y values of the sides of these triangles where the sides are equal to z0. Then I use scipy.spatial.ConvexHull to sort the intersected points. Using the shoelace formula I can then determine the area.
I have attached the example code here:
import numpy as np
from scipy import spatial
import matplotlib.pyplot as plt
# Generation of random test data
n = 500
x = np.random.random(n)
y = np.random.random(n)
z = np.exp(-2*(x-.5)**2-4*(y-.5)**2)
z0 = .75
# Triangulation of the test data
triang= spatial.Delaunay(np.array([x, y]).T)
# Determine all triangles where not all points are above or below z0, i.e. the triangles that intersect z0
tri_inter=np.zeros_like(triang.simplices, dtype=np.int) # The triangles which intersect the plane at z0, filled below
i = 0
for tri in triang.simplices:
if ~np.all(z[tri] > z0) and ~np.all(z[tri] < z0):
tri_inter[i,:] = tri
i += 1
tri_inter = tri_inter[~np.all(tri_inter==0, axis=1)] # Remove all rows with only 0
# The number of interpolated values for x and y has twice the length of the triangles
# Because each triangle intersects the plane at z0 twice
x_inter = np.zeros(tri_inter.shape[0]*2)
y_inter = np.zeros(tri_inter.shape[0]*2)
for j, tri in enumerate(tri_inter):
# Determine which of the three points are above and which are below z0
points_above = []
points_below = []
for i in tri:
if z[i] > z0:
points_above.append(i)
else:
points_below.append(i)
# Calculate the intersections and put the values into x_inter and y_inter
t = (z0-z[points_below[0]])/(z[points_above[0]]-z[points_below[0]])
x_new = t * (x[points_above[0]]-x[points_below[0]]) + x[points_below[0]]
y_new = t * (y[points_above[0]]-y[points_below[0]]) + y[points_below[0]]
x_inter[j*2] = x_new
y_inter[j*2] = y_new
if len(points_above) > len(points_below):
t = (z0-z[points_below[0]])/(z[points_above[1]]-z[points_below[0]])
x_new = t * (x[points_above[1]]-x[points_below[0]]) + x[points_below[0]]
y_new = t * (y[points_above[1]]-y[points_below[0]]) + y[points_below[0]]
else:
t = (z0-z[points_below[1]])/(z[points_above[0]]-z[points_below[1]])
x_new = t * (x[points_above[0]]-x[points_below[1]]) + x[points_below[1]]
y_new = t * (y[points_above[0]]-y[points_below[1]]) + y[points_below[1]]
x_inter[j*2+1] = x_new
y_inter[j*2+1] = y_new
# sort points to calculate area
hull = spatial.ConvexHull(np.array([x_inter, y_inter]).T)
x_hull, y_hull = x_inter[hull.vertices], y_inter[hull.vertices]
# Calculation of are using the shoelace formula
area = 0.5*np.abs(np.dot(x_hull,np.roll(y_hull,1))-np.dot(y_hull,np.roll(x_hull,1)))
print('Area:', area)
plt.figure()
plt.plot(x_inter, y_inter, 'ro')
plt.plot(x_hull, y_hull, 'b--')
plt.triplot(x, y, triangles=tri_inter, color='k')
plt.show()
I have an image:
>>> image.shape
(720, 1280)
My image is a binary array of 0s and 255s. I've done some cursory edge detection, and now I want to fit a polynomial through the points.
I want to see these points back on my original image, in image-space.
As far as I can tell, the standard way to do this is to unwrap the x,y- image with a reshape, fit on the unwrapped version, then re-reshape back into the original image.
pts = np.array(image).reshape((-1, 2))
xdata = pts[:,0]
ydata = pts[:,1]
z1 = np.polyfit(xdata, ydata, 1)
z2 = np.polyfit(xdata, ydata, 2) # or quadratic...
f = np.poly1d(z)
Now that I have this function, f, how do I use it to paint my lines in the original image space?
In particular:
What's the right inverse indexing of .reshape() to get back into image space?
This seems a bit cumbersome. Is this reshape reshape dance a common thing in image processing? Is what is described above the standard way to do this, or is there a different approach?
If mapping onto the 720, 1280, 1 array is called the image space, what is the reshaped space called? data-space? Linearized space?
You don't need to do this. You can combine np.nonzero, np.polyfit and np.polyval to do this. It would look like this:
import numpy as np
from matplotlib import pyplot as plt
# in your case, you would read your image
# > cv2.imread(...) # import cv2 before
# but we are going to create an image based on a polynomial
img = np.zeros((400, 400), dtype=np.uint8)
h, w = img.shape
xs = np.arange(150, 250)
ys = np.array(list(map(lambda x: 0.01 * x**2 - 4*x + 600, xs))).astype(np.int)
img[h - ys, xs] = 255
# I could use the values I have, but if you have a binary image,
# you will need to get them, and you could do something like this
ys, xs = np.nonzero(img) # use (255-img) if your image is inverted
ys = h - ys
# compute the coefficients
coefs = np.polyfit(xs, ys, 2)
xx = np.arange(0, w).astype(np.int)
yy = h - np.polyval(coefs, xx)
# filter those ys out of the image, because we are going to use as index
xx = xx[(0 <= yy) & (yy < h)]
yy = yy[(0 <= yy) & (yy < h)].astype(np.int) # convert to int to use as index
# create and display a color image just to viz the result
color_img = np.repeat(img[:, :, np.newaxis], 3, axis=2)
color_img[yy, xx, 0] = 255 # 0 because pyplot is RGB
f, ax = plt.subplots(1, 2)
ax[0].imshow(img, cmap='gray')
ax[0].set_title('Binary')
ax[1].imshow(color_img)
ax[1].set_title('Polynomial')
plt.show()
The results look like this:
If you print coefs, you will have [ 1.00486819e-02 -4.01966712e+00 6.01540472e+02] which are very close to the [0.01, -4, 600] we chose.
I have pieced together the
following code to plot a triangular mesh with the colors specified by an
additional scalar function:
#! /usr/bin/env python
import numpy as np
from mayavi import mlab
# Create cone
n = 8
t = np.linspace(-np.pi, np.pi, n)
z = np.exp(1j*t)
x = z.real.copy()
y = z.imag.copy()
z = np.zeros_like(x)
triangles = [(0, i, i+1) for i in range(n)]
x = np.r_[0, x]
y = np.r_[0, y]
z = np.r_[1, z]
t = np.r_[0, t]
# These are the scalar values for each triangle
f = np.mean(t[np.array(triangles)], axis=1)
# Plot it
mesh = mlab.triangular_mesh(x, y, z, triangles,
representation='wireframe',
opacity=0)
cell_data = mesh.mlab_source.dataset.cell_data
cell_data.scalars = f
cell_data.scalars.name = 'Cell data'
cell_data.update()
mesh2 = mlab.pipeline.set_active_attribute(mesh,
cell_scalars='Cell data')
mlab.pipeline.surface(mesh2)
mlab.show()
This works reasonably well. However, instead of having every triangle
with a uniform color and sharp transitions between the triangles, I'd
much rather have a smooth interpolation over the entire surface.
Is there a way to do that?
I think you want to use point data instead of cell data. With cell data, a single scalar value is not localized to any point. It is assigned to the entire face. It looks like you just want to assign the t data to the vertices instead. The default rendering of point scalars will smoothly interpolate across each face.
point_data = mesh.mlab_source.dataset.point_data
point_data.scalars = t
point_data.scalars.name = 'Point data'
point_data.update()
mesh2 = mlab.pipeline.set_active_attribute(mesh,
point_scalars='Point data')