I have a set of 3d coordinates that was generated using meshgrid(). I want to be able to rotate these about the 3 axes.
I tried unraveling the meshgrid and doing a rotation on each point but the meshgrid is large and I run out of memory.
This question addresses this in 2d with einsum(), but I can't figure out the string format when extending it to 3d.
I have read several other pages about einsum() and its format string but haven't been able to figure it out.
EDIT:
I call my meshgrid axes X, Y, and Z, each is of shape (213, 48, 37). Also, the actual memory error came when I tried to put the results back into a meshgrid.
When I attempted to 'unravel' it to do point by point rotation I used the following function:
def mg2coords(X, Y, Z):
return np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
I looped over the result with the following:
def rotz(angle, point):
rad = np.radians(angle)
sin = np.sin(rad)
cos = np.cos(rad)
rot = [[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1]]
return np.dot(rot, point)
After the rotation I will be using the points to interpolate onto.
Working with your definitions:
In [840]: def mg2coords(X, Y, Z):
return np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
In [841]: def rotz(angle):
rad = np.radians(angle)
sin = np.sin(rad)
cos = np.cos(rad)
rot = [[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1]]
return np.array(rot)
# just to the rotation matrix
define a sample grid:
In [842]: X,Y,Z=np.meshgrid([0,1,2],[0,1,2,3],[0,1,2],indexing='ij')
In [843]: xyz=mg2coords(X,Y,Z)
rotate it row by row:
In [844]: xyz1=np.array([np.dot(rot,i) for i in xyz])
equivalent einsum row by row calculation:
In [845]: xyz2=np.einsum('ij,kj->ki',rot,xyz)
They match:
In [846]: np.allclose(xyz2,xyz1)
Out[846]: True
Alternatively I could collect the 3 arrays into one 4d array, and rotate that with einsum. Here np.array adds a dimension at the start. So the dot sum j dimension is 1st, and the 3d of the arrays follow:
In [871]: XYZ=np.array((X,Y,Z))
In [872]: XYZ2=np.einsum('ij,jabc->iabc',rot,XYZ)
In [873]: np.allclose(xyz2[:,0], XYZ2[0,...].ravel())
Out[873]: True
Similary for the 1 and 2.
Alternatively I could split XYZ2 into 3 component arrays:
In [882]: X2,Y2,Z2 = XYZ2
In [883]: np.allclose(X2,xyz2[:,0].reshape(X.shape))
Out[883]: True
Use ji instead of ij if you want to rotate in the other direction, i.e. use rot.T.
Related
I found an article which is about epipolar geometry.
I calculated the fundamental matrix. Now Iam trying to find the line on which a corresponding point lays as described in the article:
I calculated the line which is in homogeneous coordinates. How could I plot this line into the picture like in the example? I thought about transforming the line from homogeneous to inhomogeneous coordinates. I think this can be achieved by dividing x and y by z
For example, homogeneous:
x=0.0295
y=0.9996
z=-265.1531
to inhomogeneous:
x=0.0295/-265.1531
y=0.9996/-265.1531
so:
x=-0.0001112564778612809
y=0.0037698974667842843
Those numbers seem wrong to me, because theyre so small. Is this the correct approach?
How could I plot my result into an image?
the x, y and z you have are the parameters of the "Epipolar Lines" equation that appear under the "line in the image" formula in the slides, but labelled a, b and c respectively, i.e:
au + bv + c = 0
solutions to this are points on the line. e.g. in Python I'd define a as some points on the picture's x-axis, and solve for b:
import numpy as np
F = np.array([
[-0.00310695, -0.0025646, 2.96584],
[-0.028094, -0.00771621, 56.3813],
[13.1905, -29.2007, -9999.79],
])
p_l = np.array([
[343.53],
[221.70],
[ 1.0],
])
lt = F # p_l
# if you want to normalise
lt /= np.sqrt(sum(lt[:2] ** 2))
# should give your values [0.0295, 0.9996, -265.2]
print(lt)
a, b, c = lt.ravel()
x = np.array([0, 400])
y = -(x*a + c) / b
and then just draw a line between these points
I have a 2D Numpy array that represents an image, and I want to create a surface plot of image intensity using matplotlib.surface_plot. For this function, I need to convert the 2D array A[x,y] => z into three arrays: [x0,...,xN], [y0,...,yN] and [z0,...,zN]. I can see how to do this conversion element-by-element:
X = []
Y = []
Z = []
for x in range( A.shape[ 0 ] ):
for y in range( A.shape[ 1 ] ):
X.append( x )
Y.append( y )
Z.append( A[x,y] )
but I'm wondering whether there is a more Pythonic way to do this?
a very simple way to do this could be to basically use the code shown in the matplotlib example. assuming x and y representing the sizes of the two dims in your image array A, you could do
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# generate some input data that looks nice on a color map:
A = np.mgrid[0:10:0.1,0:10:0.1][0]
X = np.arange(0, A.shape[0], 1)
Y = np.arange(0, A.shape[1], 1)
X, Y = np.meshgrid(X, Y)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, A, cmap='viridis',
linewidth=0, antialiased=False)
gives
You possibly don't need to construct the actual grid, because some pyplot functions accept 1d arrays for x and y, implying that a grid is to be constructed. It seems that Axes3D.plot_surface (which I presume you meant) does need 2d arrays as input, though.
So to get your grid the easiest way is using np.indices to get the indices corresponding to your array:
>>> import numpy as np
...
... # dummy data
... A = np.random.random((3,4)) # randoms of shape (3,4)
...
... # get indices
... x,y = np.indices(A.shape) # both arrays have shape (3,4)
...
... # prove that the indices correspond to the values of A
... print(all(A[i,j] == A[x[i,j], y[i,j]] for i in x.ravel() for j in y.ravel()))
True
The resulting arrays all have the same shape as A, which should be correct for most use cases. If for any reason you really need a flattened 1d array, you should use x.ravel() etc. to get a flattened view of the same 2d array.
I should note though that the standard way to visualize images (due to the short-wavelength variation of the data) is pyplot.imshow or pyplot.pcolormesh which can give you pixel-perfect visualization, albeit in two dimensions.
We agree X, Y and Z have different sizes (N for X and Y and N^2 for Z) ? If yes:
X looks not correct (you add several times the same values)
something like:
X = list(range(A.shape[0])
Y = list(range(A.shape[1])
Z = [A[x,y] for x in X for y in Y]
I have used interp2 in Matlab, such as the following code, that is part of #rayryeng's answer in: Three dimensional (3D) matrix interpolation in Matlab:
d = size(volume_image)
[X,Y] = meshgrid(1:1/scaleCoeff(2):d(2), 1:1/scaleCoeff(1):d(1));
for ind = z
%Interpolate each slice via interp2
M2D(:,:,ind) = interp2(volume_image(:,:,ind), X, Y);
end
Example of Dimensions:
The image size is 512x512 and the number of slices is 133. So:
volume_image(rows, columns, slices in 3D dimenson) : 512x512x133 in 3D dimenson
X: 288x288
Y: 288x288
scaleCoeff(2): 0.5625
scaleCoeff(1): 0.5625
z = 1 up to 133 ,hence z: 1x133
ind: 1 up to 133
M2D(:,:,ind) finally is 288x288x133 in 3D dimenson
Aslo, Matlabs syntax for size: (rows, columns, slices in 3rd dimenson) and Python syntax for size: (slices in 3rd dim, rows, columns).
However, after convert the Matlab code to Python code occurred an error, ValueError: Invalid length for input z for non rectangular grid:
for ind in range(0, len(z)+1):
M2D[ind, :, :] = interpolate.interp2d(X, Y, volume_image[ind, :, :]) # ValueError: Invalid length for input z for non rectangular grid
What is wrong? Thank you so much.
In MATLAB, interp2 has as arguments:
result = interp2(input_x, input_y, input_z, output_x, output_y)
You are using only the latter 3 arguments, the first two are assumed to be input_x = 1:size(input_z,2) and input_y = 1:size(input_z,1).
In Python, scipy.interpolate.interp2 is quite different: it takes the first 3 input arguments of the MATLAB function, and returns an object that you can call to get interpolated values:
f = scipy.interpolate.interp2(input_x, input_y, input_z)
result = f(output_x, output_y)
Following the example from the documentation, I get to something like this:
from scipy import interpolate
x = np.arange(0, volume_image.shape[2])
y = np.arange(0, volume_image.shape[1])
f = interpolate.interp2d(x, y, volume_image[ind, :, :])
xnew = np.arange(0, volume_image.shape[2], 1/scaleCoeff[0])
ynew = np.arange(0, volume_image.shape[1], 1/scaleCoeff[1])
M2D[ind, :, :] = f(xnew, ynew)
[Code not tested, please let me know if there are errors.]
You might be interested in scipy.ndimage.zoom. If you are interpolating from one regular grid to another, it is much faster and easier to use than scipy.interpolate.interp2d.
See this answer for an example:
https://stackoverflow.com/a/16984081/1295595
You'd probably want something like:
import scipy.ndimage as ndimage
M2D = ndimage.zoom(volume_image, (1, scaleCoeff[0], scaleCoeff[1])
Normally, numpy arrays are much faster than list operations or loops, but in this case too?:
I have a 4D-array and a boolean index-array for the first three axis'; the output of the indexing is flattened, at least in the index axis', so its a 'list of tuples' (but in array form).
Since the regular structure is broken, I assume that this is much slower than indexing of a regular grid (i.e. indexing each axis independently) would be? Maybe numpy internally really computes a list of tuples and then converts it to an array?
Why I ask: I would like to enumerate the output, to be able to compute for any tuple if it is in the list, and on which position. I try to understand which method might be fast and elegant...
My context:
I have an array of integer koordinates, a grid - so logically I have a 3D-Array of 3-tuples, but for the program it's a 4D-Array.
I want to get all points for which the sum of coordinates equals a constant, which is cutting a plane out of my cube (finally, I take two adjacent planes, which gives me a honeycomb lattice -- it's quite pretty if you like math :))
So the values in the last axis are just the indices of the first three axis'. If I had not only an index array of True and False, but also had assigned an id instead of each True, then I could easily read out the id for each tuple.
This might be an elegant and fast way to the task (the goal is to know for each site in one of the planes which sites of the other are adjacent -- so their coordinates are known, but I want their id).
So, does numpy internally do any magic to get the indexed array? Or would it be similarly fast to take a for-loop ;) (no, I see by trying, that this is much faster, but why...)
Some code (comments in German, sorry)
import numpy as np
Seitenlaenge = 4
kArray = np.zeros((Seitenlaenge, Seitenlaenge, Seitenlaenge, 3)) # 4D-Array, hier soll dann an der Stelle [x, y, z, :] der Vektor (x, y, z) stehen
kArray[:, :, :, 2] = np.arange(Seitenlaenge).reshape((1, 1, Seitenlaenge)).repeat(Seitenlaenge, axis = 0).repeat(Seitenlaenge, axis = 1)
kArray[:, :, :, 1] = np.arange(Seitenlaenge).reshape((1, Seitenlaenge, 1)).repeat(Seitenlaenge, axis = 0).repeat(Seitenlaenge, axis = 2)
kArray[:, :, :, 0] = np.arange(Seitenlaenge).reshape((Seitenlaenge, 1, 1)).repeat(Seitenlaenge, axis = 1).repeat(Seitenlaenge, axis = 2)
# Die Gitterpunkte waehlen die zu A und B gehoeren:
print kArray
Summe = 5 # Seitenlaenge des Dreiecks, das aus dem 1.Oktanten geschnitten wuerde, wenn der Wuerfel nicht kleiner waere
ObA = kArray.sum(axis=-1) == Summe-1 # 3D-boolean Array
ObB = kArray.sum(axis=-1) == Summe-2
print ObA
kA, kB = kArray[ObA], kArray[ObB] # Es bleiben 2D-Arrays: Listen von Koordina-
# tentripeln, in der Form (x, y, z)
print kA
and if you like to see the honeycomb lattice, then do afterwards:
import matplotlib.pyplot as plt
nx = np.array([-1, 1, 0])*2**-0.5
ny = np.array([-1, -1, 2])*6**-0.5
def Projektion(ListeTripel):
return dot(ListeTripel, nx), dot(ListeTripel, ny)
xA, yA = Projektion(kA)
xB, yB = Projektion(kB)
plt.plot(xA.flatten(), yA.flatten(), 'o', c='r', ms=8, mew=0)
plt.plot(xB.flatten(), yB.flatten(), 'o', c='b', ms=8, mew=0)
plt.show()
Numpy is pretty smart about indexing. It will flatten your boolean array, compute nnz, the number of Trues in it, allocate an output array of shape (nnz, 3), then iterate simultaneously over your flattened boolean array item by item, and your flattened array in jumps of 3 items, i.e. with a 3 item stride. Wherever the boolean array has a True it will copy the next 3 items of your array to the output array, then continue with the iteration.
All of this will happen in C, so it is very, very fast, at least by Python standards.
By the way, somewhat unrelated to your question, but use broadcasting:
length = 4
indices = np.arange(length)
k_array = np.empty((length,) * 3 + (3,), dtype=np.intp)
k_array[..., 0] = indices
k_array[... ,1] = indices[:, None]
k_array[... ,2] = indices[:, None, None]
Very similar to RBF interpolation fails: LinAlgError: singular matrix but I think the problem is different, as I have no duplicated coordinates.
Toy example:
import numpy as np
import scipy.interpolate as interp
coords = (np.array([-1, 0, 1]), np.array([-2, 0, 2]), np.array([-1, 0, 1]))
coords_mesh = np.meshgrid(*coords, indexing="ij")
fn_value = np.power(coords_mesh[0], 2) + coords_mesh[1]*coords_mesh[2] # F(x, y, z)
coords_array = np.vstack([x.flatten() for x in coords_mesh]).T # Columns are x, y, z
unique_coords_array = np.vstack({tuple(row) for row in coords_array})
unique_coords_array.shape == coords_array.shape # True, i.e. no duplicate coords
my_grid_interp = interp.RegularGridInterpolator(points=coords, values=fn_value)
my_grid_interp(np.array([0, 0, 0])) # Runs without error
my_rbf_interp = interp.Rbf(*[x.flatten() for x in coords_mesh], d=fn_value.flatten())
## Error: numpy.linalg.linalg.LinAlgError: singular matrix -- why?
What am I missing? The example above uses the function F(x, y, z) = x^2 + y*z. I'd like to use Rbf to approximate that function. As far as I can tell there are no duplicate coordinates: compare unique_coords_array to coords_array.
I believe the problem is your input:
my_rbf_interp = interp.Rbf(*[x.flatten() for x in coords_mesh],d=fn_value.flatten())
Should you change to:
x,y,z = [x.flatten() for x in coords_mesh]
my_rbf_interp = interp.Rbf(x,y,z,fn_value.flatten())
And it should work. I think your original formulation is repeating lines in the matrix that goes for solve and thus having a very similar problem to duplicates (i.e. Singular Matrix).
Also if you would do:
d = fn_value.flatten()
my_rbf_interp = interp.Rbf(*(x,y,z,d))
It should work also.