Project 3D points to 2D points in python - python

I'm trying to project 3D body keypoints to 2D keypoints,
My 3D points are:
points = np.array([[-7.55801499e-02, -3.69511306e-01, -2.63576955e-01],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 3.08661222e-01, -2.93346141e-02, 3.72593999e-02],
[ 5.96781611e-01, -2.82074720e-01, 4.71359938e-01],
[ 5.38534284e-01, -8.05779934e-01, 4.68694866e-01],
[-3.67936224e-01, -1.09069087e-01, 9.90774706e-02],
[-5.24732828e-01, -2.87176669e-01, 6.09635711e-01],
[-4.37022656e-01, -7.87327409e-01, 4.43706572e-01],
[ 1.33009470e-09, -5.10657072e-09, 1.00000000e+00],
[ 1.13241628e-01, 3.25177647e-02, 1.24026799e+00],
[ 3.43442023e-01, -2.51034945e-01, 1.90472209e+00],
[ 2.57550180e-01, -2.86886752e-01, 2.75528717e+00],
[-1.37361348e-01, -2.60521360e-02, 1.19951272e+00],
[-3.26779515e-01, -5.59706092e-01, 1.75905156e+00],
[-4.65996087e-01, -7.69565761e-01, 2.56634569e+00],
[-1.89841837e-02, -3.19088846e-01, -3.69913191e-01],
[-1.61812544e-01, -3.10732543e-01, -3.47061515e-01],
[ 7.68100023e-02, -1.19293019e-01, -3.72248143e-01],
[-2.24317372e-01, -1.02143347e-01, -3.32051814e-01],
[-3.77829641e-01, -1.19915462e+00, 2.56900430e+00],
[-5.45104921e-01, -1.13393784e+00, 2.57149625e+00],
[-5.66698492e-01, -6.89325571e-01, 2.67840290e+00],
[ 4.65222150e-01, -6.44857705e-01, 2.83186650e+00],
[ 5.27995050e-01, -4.69421804e-01, 2.87518311e+00],
[ 1.77749291e-01, -1.74753308e-01, 2.88810611e+00]])
I plotted them using:
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim3d(1, -1)
ax.set_ylim3d(1, -1)
ax.set_zlim3d(1, -1)
ax.scatter3D(points[:, 0], points[:, 1],
points[:, 2], cmap='Greens')
The result is:
I want an array of 2D points with the same camera view, so my desired result a 2D array:
I have tried so far:
import cv2
ans = []
for k in range(25):
tmp = np.array(s[0, k, :]).reshape(1,3)
revc = np.array([0, 0, 0], np.float) # rotation vector
tvec = np.array([0, 0, 0], np.float) # translation vector
fx = fy = 1.0
cx = cy = 0.0
cameraMatrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])
result = cv2.projectPoints(tmp, revc, tvec, cameraMatrix, None)
ans.append(result[0])
ans = np.array(ans).squeeze()
But the result I'm getting is:
plt.scatter(ans[:,0], ans[:, 1])
I can't figure out why the information is lost during projection, kindly help me in this. Also its not necessary for me to use OpenCV so you can suggest other methods like using numpy too.
Thanks

Here's a way to do this from "scratch". I have the following import statements:
import numpy as np
import matplotlib.pyplot as plt
from numpy import sin,cos,pi
from scipy.linalg import norm
After your 3d plotting code, I added the following:
azim = ax.azim*pi/180
elev = ax.elev*pi/180
elev *= 1.2 # this seems to improve the outcome
a_vec = np.array([cos(azim),sin(azim),0])
normal = cos(elev)*a_vec + np.array([0,0,sin(elev)])
z_vec = np.array([0,0,1])
y_comp = z_vec - (z_vec#normal)*normal
y_comp = y_comp/norm(y_comp)
x_comp = np.cross(y_comp,normal)
proj_mat = np.vstack([x_comp,y_comp]) # build projection matrix
proj_mat = -proj_mat # account for flipped axes
points_2D = points # proj_mat.T # apply projection
plt.figure()
plt.scatter(*points_2D.T)
plt.gca().set_aspect('equal', adjustable='box')
plt.axis('off')
plt.show()
The resulting points:

Related

Converting points back to 3D

I have points in a 3D plane that I have converted to a 2D projection using the following method:
import numpy as np
# Calculate axes for 2D projection
# Create random vector to cross
rv = np.add(self.plane.normal, [-1.0, 0.0, 1.0])
rv = np.divide(rv, np.linalg.norm(rv))
horizontal = np.cross(self.plane.normal, rv)
vertical = np.cross(self.plane.normal, horizontal)
diff2 = np.zeros((len(point23D), 3), dtype=np.float32)
diff2[:, 0] = np.subtract(point23D[:, 0], self.plane.origin[0])
diff2[:, 1] = np.subtract(point23D[:, 1], self.plane.origin[1])
diff2[:, 2] = np.subtract(point23D[:, 2], self.plane.origin[2])
x2 = np.add(np.add(np.multiply(diff2[:, 0], horizontal[0]), np.multiply(diff2[:, 1], horizontal[1])), np.multiply(diff2[:, 2], horizontal[2]))
y2 = np.add(np.add(np.multiply(diff2[:, 0], vertical[0]), np.multiply(diff2[:, 1], vertical[1])), np.multiply(diff2[:, 2], vertical[2]))
twodpoints2 = np.zeros((len(point23D), 3), dtype=np.float32)
twodpoints2[:, 0] = x2
twodpoints2[:, 1] = y2
I then do some calculations on these points in 2D space. After that I need to get the points back in 3D space on the same relative position. I have written the following code for that:
# Transform back to 3D
rotation_matrix = np.array([[horizontal[0], vertical[0], -self.plane.normal[0]],
[horizontal[1], vertical[1], -self.plane.normal[1]],
[horizontal[2], vertical[2], -self.plane.normal[2]]])
transformed_vertices = np.matmul(twodpoints, rotation_matrix)
transformed_vertices = np.add(transformed_vertices, self.plane.origin)
But this doesn't seem to do the projection correctly, the points projected back in 3D do not lie on the original 3D plane at all. Does anyone know why this is wrong or does anyone have a suggestion that would work better?
In this example I just projected the same points back into 3D to see if it works correctly, which it doesn't. In reality I'll have different points that need to be projected back, but they still need to be in the same plane in 3D space.
# You have a plane perpendicular to a vector
# N = np.array([x_N, y_N, z_N])
# and passing through a point
# Q = np.array([x_Q, y_Q, z_Q])
U = np.zeros((3,3))
U[2,:] = N / np.linalg.norm(N)
e = np.array([0,0,0])
e[np.argmin(np.abs(U[0,:]))] = 1
U[0, :] = np.cross(e, U[2,:])
U[0, :] = U[0, :] / np.linalg.norm(U[0, :])
U[1, :] = np.cross(U[2, :], U[0, :])
point2D = (point23D - Q).dot(U)
result_point2D = some_calcs(point2D)
result_point23D = res_point2D.dot(U.transpose())

MayaVi contour3d after coordinate transformation

I have a 3D scalar field mesh given in non-cartesian coordinate system.
After coordinate transformation back to conventional cartesian coordinates
mlab.contour3d displays wrong result, while mlab.points3d works as expected. How can I view isosurfaces of given mesh in different coordinate systems?
This is my code
import os
import numpy as np
# fix incorrect order in foregroud objects
os.environ['ETS_TOOLKIT'] = 'qt4'
os.environ['QT_API'] = 'pyqt'
from mayavi import mlab
def plot_cell(cell, mlab):
for nr, i in enumerate(cell):
coord = np.zeros((4, 3), dtype=float)
coord[1] = i
for nr2, j in enumerate(cell):
if nr == nr2:
continue
coord[2] = i + j
for nr3, k in enumerate(cell):
if nr3 == nr or nr3 == nr2:
continue
coord[3] = i + j + k
mlab.plot3d(*coord.T, color=(0, 0, 0), line_width=0.01)
# generate data in non-cartesian coordinate system
scaled_coord = [np.linspace(0, 1, 20, endpoint=False) for i in range(3)]
XYZ = np.array(np.meshgrid(*scaled_coord, indexing="ij"))
data = np.sin(2*np.pi*XYZ[0])*np.sin(2*np.pi*XYZ[1])*np.sin(2*np.pi*XYZ[2])
plot_cell(np.eye(3), mlab)
mlab.contour3d(*XYZ, data)
mlab.savefig("old_coord.png")
mlab.close()
# transform to cartesian coordinates
cell = np.array(
[[ 1. , 0. , 0. ],
[-0.5 , 0.87, 0. ],
[ 0. , 0. , 3.07]])
transformation_matrix = cell.T
XYZ2 = np.einsum('ij,jabc->iabc', transformation_matrix, XYZ)
# plot transformed result
plot_cell(cell, mlab)
mlab.contour3d(*XYZ2, data)
mlab.savefig("new_coord.png")
mlab.close()
# plot points
plot_cell(cell, mlab)
mlab.points3d(*XYZ2, data)
mlab.savefig("new_coord_expected.png")
mlab.close()
After watching 3D Visualization with Mayavi I managed to solve it myself.
Problem is that mlab.contour3d works only with rectilinear grid data (grid generated with np.meshgrid or np.mgrid). In my case one can use tvtk.StructuredGrid object for structured grids which have same topology as
rectilinear grid but nonuniform spacing and directions between points.
This is working code:
import os
import numpy as np
from tvtk.api import tvtk
# fix incorrect order in foregroud objects
os.environ['ETS_TOOLKIT'] = 'qt4'
os.environ['QT_API'] = 'pyqt'
from mayavi import mlab
def plot_cell(cell, mlab):
for nr, i in enumerate(cell):
coord = np.zeros((4, 3), dtype=float)
coord[1] = i
for nr2, j in enumerate(cell):
if nr == nr2:
continue
coord[2] = i + j
for nr3, k in enumerate(cell):
if nr3 == nr or nr3 == nr2:
continue
coord[3] = i + j + k
mlab.plot3d(*coord.T, color=(0, 0, 0), line_width=0.01)
scaled_coord = [np.linspace(0, 1, 20, endpoint=False) for i in range(3)]
ABC = np.array(np.meshgrid(*scaled_coord, indexing="ij"))
data = np.sin(2*np.pi*ABC[0])*np.sin(2*np.pi*ABC[1])*np.sin(2*np.pi*ABC[2])
# transform to cartesian coordinates
cell = np.array(
[[ 1. , 0. , 0. ],
[-0.5 , 0.87, 0. ],
[ 0. , 0. , 3.07]])
transformation_matrix = cell.T
x, y, z = np.einsum('ij,jabc->iabc', transformation_matrix, ABC)
def generate_structured_grid(x, y, z, scalars):
pts = np.empty(z.shape + (3,), dtype=float)
pts[..., 0] = x
pts[..., 1] = y
pts[..., 2] = z
pts = pts.transpose(2, 1, 0, 3).copy()
pts.shape = int(pts.size / 3), 3
scalars = scalars.T.copy()
sg = tvtk.StructuredGrid(dimensions=x.shape, points=pts)
sg.point_data.scalars = scalars.ravel()
sg.point_data.scalars.name = 'scalars'
return sg
sgrid = generate_structured_grid(x, y, z, data)
src = mlab.pipeline.add_dataset(sgrid)
isosurface = mlab.pipeline.iso_surface(src)
plot_cell(cell, mlab)
mlab.show()

Matplotlib render all internal voxels (with alpha)

I want to render a volume in matplotlib. The volume is a simple 7x7x7 cube, and I want to be able to see all internal voxels (even though I know it will look like a mess).
I've been able to render voxels with transparency, but any voxel not on the surface seems to never be drawn.
Each 7x7 slice of the volume should look like this:
I've thrown together a MWE
The following code creates a 5x5x5 volume with a red,green,blue,yellow, and cyan 5x5 layers. The alpha of each layer is set to .5, so the whole thing should be see-through.
Then I chang the colors of all non-surface voxels to black with alpha 1, so if they were showing we should be able to see a black box in the center.
Rendering it by itself produces the figure on the left, but if we remove the fill from the cyan layer, we can see that the black box does indeed exist, it is just not being shown because it is 100% occluded even though those occluding voxels have alpha less than 1.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # NOQA
spatial_axes = [5, 5, 5]
filled = np.ones(spatial_axes, dtype=np.bool)
colors = np.empty(spatial_axes + [4], dtype=np.float32)
alpha = .5
colors[0] = [1, 0, 0, alpha]
colors[1] = [0, 1, 0, alpha]
colors[2] = [0, 0, 1, alpha]
colors[3] = [1, 1, 0, alpha]
colors[4] = [0, 1, 1, alpha]
# set all internal colors to black with alpha=1
colors[1:-1, 1:-1, 1:-1, 0:3] = 0
colors[1:-1, 1:-1, 1:-1, 3] = 1
fig = plt.figure()
ax = fig.add_subplot('111', projection='3d')
ax.voxels(filled, facecolors=colors, edgecolors='k')
fig = plt.figure()
ax = fig.add_subplot('111', projection='3d')
filled[-1] = False
ax.voxels(filled, facecolors=colors, edgecolors='k')
Is there any way to render all occluded voxels?
To turn my comments above into an answer:
You may always just plot all voxels as in
Representing voxels with matplotlib
3D discrete heatmap in matplotlib
The official example solves this problem by offsettingt the faces of the voxels by a bit, such they are all drawn.
This matplotlib issue discusses the missing faces on internal cubes. There is a pull request which has some issues still and it hence not merged yet.
Despite the small issues, you may monkey patch the current status of the pull request into your code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, art3d # NOQA
from matplotlib.cbook import _backports
from collections import defaultdict
import types
def voxels(self, *args, **kwargs):
if len(args) >= 3:
# underscores indicate position only
def voxels(__x, __y, __z, filled, **kwargs):
return (__x, __y, __z), filled, kwargs
else:
def voxels(filled, **kwargs):
return None, filled, kwargs
xyz, filled, kwargs = voxels(*args, **kwargs)
# check dimensions
if filled.ndim != 3:
raise ValueError("Argument filled must be 3-dimensional")
size = np.array(filled.shape, dtype=np.intp)
# check xyz coordinates, which are one larger than the filled shape
coord_shape = tuple(size + 1)
if xyz is None:
x, y, z = np.indices(coord_shape)
else:
x, y, z = (_backports.broadcast_to(c, coord_shape) for c in xyz)
def _broadcast_color_arg(color, name):
if np.ndim(color) in (0, 1):
# single color, like "red" or [1, 0, 0]
return _backports.broadcast_to(
color, filled.shape + np.shape(color))
elif np.ndim(color) in (3, 4):
# 3D array of strings, or 4D array with last axis rgb
if np.shape(color)[:3] != filled.shape:
raise ValueError(
"When multidimensional, {} must match the shape of "
"filled".format(name))
return color
else:
raise ValueError("Invalid {} argument".format(name))
# intercept the facecolors, handling defaults and broacasting
facecolors = kwargs.pop('facecolors', None)
if facecolors is None:
facecolors = self._get_patches_for_fill.get_next_color()
facecolors = _broadcast_color_arg(facecolors, 'facecolors')
# broadcast but no default on edgecolors
edgecolors = kwargs.pop('edgecolors', None)
edgecolors = _broadcast_color_arg(edgecolors, 'edgecolors')
# include possibly occluded internal faces or not
internal_faces = kwargs.pop('internal_faces', False)
# always scale to the full array, even if the data is only in the center
self.auto_scale_xyz(x, y, z)
# points lying on corners of a square
square = np.array([
[0, 0, 0],
[0, 1, 0],
[1, 1, 0],
[1, 0, 0]
], dtype=np.intp)
voxel_faces = defaultdict(list)
def permutation_matrices(n):
""" Generator of cyclic permutation matices """
mat = np.eye(n, dtype=np.intp)
for i in range(n):
yield mat
mat = np.roll(mat, 1, axis=0)
for permute in permutation_matrices(3):
pc, qc, rc = permute.T.dot(size)
pinds = np.arange(pc)
qinds = np.arange(qc)
rinds = np.arange(rc)
square_rot = square.dot(permute.T)
for p in pinds:
for q in qinds:
p0 = permute.dot([p, q, 0])
i0 = tuple(p0)
if filled[i0]:
voxel_faces[i0].append(p0 + square_rot)
# draw middle faces
for r1, r2 in zip(rinds[:-1], rinds[1:]):
p1 = permute.dot([p, q, r1])
p2 = permute.dot([p, q, r2])
i1 = tuple(p1)
i2 = tuple(p2)
if filled[i1] and (internal_faces or not filled[i2]):
voxel_faces[i1].append(p2 + square_rot)
elif (internal_faces or not filled[i1]) and filled[i2]:
voxel_faces[i2].append(p2 + square_rot)
# draw upper faces
pk = permute.dot([p, q, rc-1])
pk2 = permute.dot([p, q, rc])
ik = tuple(pk)
if filled[ik]:
voxel_faces[ik].append(pk2 + square_rot)
# iterate over the faces, and generate a Poly3DCollection for each voxel
polygons = {}
for coord, faces_inds in voxel_faces.items():
# convert indices into 3D positions
if xyz is None:
faces = faces_inds
else:
faces = []
for face_inds in faces_inds:
ind = face_inds[:, 0], face_inds[:, 1], face_inds[:, 2]
face = np.empty(face_inds.shape)
face[:, 0] = x[ind]
face[:, 1] = y[ind]
face[:, 2] = z[ind]
faces.append(face)
poly = art3d.Poly3DCollection(faces,
facecolors=facecolors[coord],
edgecolors=edgecolors[coord],
**kwargs
)
self.add_collection3d(poly)
polygons[coord] = poly
return polygons
spatial_axes = [5, 5, 5]
filled = np.ones(spatial_axes, dtype=np.bool)
colors = np.empty(spatial_axes + [4], dtype=np.float32)
alpha = .5
colors[0] = [1, 0, 0, alpha]
colors[1] = [0, 1, 0, alpha]
colors[2] = [0, 0, 1, alpha]
colors[3] = [1, 1, 0, alpha]
colors[4] = [0, 1, 1, alpha]
# set all internal colors to black with alpha=1
colors[1:-1, 1:-1, 1:-1, 0:3] = 0
colors[1:-1, 1:-1, 1:-1, 3] = 1
fig = plt.figure()
ax = fig.add_subplot('111', projection='3d')
ax.voxels = types.MethodType(voxels, ax)
ax.voxels(filled, facecolors=colors, edgecolors='k',internal_faces=True)
fig = plt.figure()
ax = fig.add_subplot('111', projection='3d')
ax.voxels = types.MethodType(voxels, ax)
filled[-1] = False
ax.voxels(filled, facecolors=colors, edgecolors='k',internal_faces=True)
plt.show()

What is output from OpenCV's Dense optical flow (Farneback) function? How can this be used to build an optical flow map in Python?

I am trying to use the output of Opencv's dense optical flow function to draw a quiver plot of the motion vectors but have not been able to find what the function actually outputs. Here is the code:
import cv2
import numpy as np
cap = cv2.VideoCapture('GOPR1745.avi')
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255
count=0
while(1):
ret, frame2 = cap.read()
next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prvs,next,None, 0.5, 3, 15, 3, 10, 1.2, 0)
mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
hsv[...,0] = ang*180/np.pi/2
hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
if count==10:
count=0
print "flow",flow
cv2.imshow('frame2',rgb)
count=count+1
k = cv2.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
prvs = next
cap.release()
cv2.destroyAllWindows()
This is effectively the same code as given in the OpenCv tutorial on dense optical flow. I receive the following output from the print function:
flow [[[ 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00]
...,
[ 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00]]
...,
[[ -3.54891084e-14 -1.38642463e-14]
[ -2.58058853e-14 -1.54020863e-14]
[ -5.56561768e-14 -1.88019359e-14]
...,
[ -7.59403916e-15 1.16633225e-13]
[ 7.22156371e-14 -1.61951507e-13]
[ -4.30715618e-15 -4.39530987e-14]]
[[ -3.54891084e-14 -1.38642463e-14]
[ -2.58058853e-14 -1.54020863e-14]
[ -5.56561768e-14 -1.88019359e-14]
...,
[ -7.59403916e-15 1.16633225e-13]
[ 7.22156371e-14 -1.61951507e-13]
[ -4.30715618e-15 -4.39530987e-14]]
I would like to know what exactly these values are? Original X,Y coordinates? Final X,Y coordinates? Distance moved?
I plan to try and find the initial and final coordinates to make a quiver plot using code from the following page:
https://www.getdatajoy.com/examples/python-plots/vector-fields
This is because in python there is no function that i am aware of that plots an optical flow map for you.
Thank you in advance!
You were almost there. Lets first take a look at the calcOpticalFlowFarneback Documentation it says there:
flow – computed flow image that has the same size as prev and type
CV_32FC2.
So what you are actually getting is a matrix that has the same size as your input frame.
Each element in that flow matrix is a point that represents the displacement of that pixel from the prev frame. Meaning that you get a point with x and y values (in pixel units) that gives you the delta x and delta y from the last frame.
I'm going to hijack this because it is the same topic.
If units are pixels as stated by #shravya, why this code does not show maximum flow equal to one?
I really dont get the units
Code
import numpy as np
import cv2
import seaborn as sns
# Generating img
img = np.zeros(shape=(3,50,50)) # 3 time frames, 50x50
center0 = np.array((10,10))
center1 = np.array((30,30))
for each_time, each_x, each_y in itertools.product(range(img.shape[0]), range(img.shape[1]), range(img.shape[2])):
img[each_time, each_x, each_y] = img[each_time, each_x, each_y] + 1000 * 1/( 0.1* ((center0[0]+each_time*displacement_x - each_x)**2 + 1*(center0[1]+each_time*displacement_y - each_y)**2)**0.5 + 1)
img[each_time, each_x, each_y] = img[each_time, each_x, each_y] + 1000 * 1/( 0.1* ((center1[0]+each_time*displacement_x - each_x)**2 + 1*(center1[1]+each_time*displacement_y - each_y)**2)**0.5 + 1)
img = (img - img.min())/(img.max()-img.min()) # Normalizing
## Ploting
fig, axs = plt.subplots(ncols=3, squeeze=True, figsize=(20,5))
for i in range(3):
im = sns.heatmap(img[i,:,:], ax = axs[i], vmin=0, vmax=np.max(img))
fig.suptitle('Image')
def calc_flow(img):
## Optical flow
img = img.astype(np.int16)
prev = np.zeros(img[0, :, :].shape).astype(np.int16)
flows = np.zeros(shape=(img.shape[0], img.shape[1], img.shape[2], 2))
for i, each_frame in enumerate(img):
if i > img.shape[0]:
break
next_ = each_frame
flow = cv2.calcOpticalFlowFarneback(prev, next_, None,
pyr_scale = 0.5,
levels = 3,
winsize = 12,
iterations = 5,
poly_n = 5,
poly_sigma = 1.2,
flags = 0)
flows[i, :, :, 0] = flow[..., 0]
flows[i, :, :, 1] = flow[..., 1]
prev = next_
return flows
flow = calc_flow(img)
fig, axs = plt.subplots(ncols=3, nrows=2, squeeze=True, figsize=(20,10))
for i in range(3):
im = sns.heatmap(flow[i,:,:, 0] ,ax = axs[0,i], vmin=0, vmax = np.max(flow))
im = sns.heatmap(flow[i,:,:, 1] ,ax = axs[1,i], vmin=0, vmax = np.max(flow))
fig.suptitle('Flow x and y plots')
mag_img, pha_img = cv2.cartToPolar(flow[..., 0], flow[..., 1])
fig, axs = plt.subplots(ncols=3, nrows=2, squeeze=True, figsize=(20,10))
for i in range(3):
im = sns.heatmap(mag_img[i,:,:], ax=axs[0,i], vmin=0, vmax = np.max(mag_img))
im = sns.heatmap(pha_img[i,:,:], ax=axs[1,i], vmin=0, vmax = np.max(pha_img))
fig.suptitle('Magnitude and phase plots')
## Output
print(flow.max()) # This should be equal to displacement!
print(np.abs(flow).min()) # this should be zero
print(mag_img.max()) # This should be equal to displacement!
print(mag_img.min()) # this should be zero

Python: calculate the magnetic field of a wire using biot-savart-law

I want to calculate the magnetic field of a wire using biot-savart-law. Some people recommend to use numpy arrays. At first i did it with vpython and it worked. But know I want to use Matplotlib for visualisation. Therefore I need arrays right? But I stuck now.
I also posted this question to codereview, but they send me to stackoverflow.
The problem is in this line --> bfield2 = konstante*I*cross(dl, (rx,ry,rz))/r**3
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from visual import *
I = 1
mu0 = 1e-7
konstante = mu0/(4*np.pi)
# wire elements; always lenght one
coord = [(0,0), (1,0), (2,0), (3,0), (4,0), (5,0), (6,0), (7,0), (8,0), (9,0), (9,1),
(9,2), (9,3), (9,4), (9,5), (9,6), (9,7), (9,8)]
# draw the wires
#for i in range(len(coord)-1):
# wire = curve(pos=(coord[i],coord[i+1]), radius=0.2)
# calculate the b-field
def bfield(x,y,z):
bfield3 = 0
# number of wire elements
for i in range(len(coord)-1):
# center of the wire element
wiremiddlex = coord[i][0]+(coord[i+1][0]-coord[i][0])/2.0
wiremiddley = coord[i][1]+(coord[i+1][1]-coord[i][1])/2.0
wiremiddlez = 0
rx = x-wiremiddlex
ry = y-wiremiddley
rz = 0
r = (rx**2+ry**2+rz**2)**0.5
dl = ((coord[i+1][0]-coord[i][0]), (coord[i+1][1]-coord[i][1]), 0)
bfield2 = konstante*I*cross(dl, (rx,ry,rz))/r**3 # i have to use numpy arrays
bfield3 += (bfield2[0]**2 + bfield2[1]**2 + bfield2[2]**2)**0.5
return bfield3
# visualize
xwidth=10
ywidth=10
delta = 1
x = np.arange(0, xwidth, delta)
y = np.arange(0, ywidth, delta)
X, Y = np.meshgrid(x, y)
slicee = 3
Z = bfield(X,Y,slicee)
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
plt.show()
EDIT No. 7: I delete the other Edits. I don't want to confuse. The output in not correct. Please see the next Edit.
# Calculation of a magnetic field of a wire
# later I want to to it three dimensional
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
I = 10000000000
mu0 = 1e-7
constant = mu0/(4*np.pi)
# wire elements; always lenght one
coord = [(0,0), (1,0), (2,0), (3,0), (4,0), (5,0), (6,0), (7,0), (8,0),
(9,0), (9,1), (9,2), (9,3), (9,4), (9,5), (9,6), (9,7), (9,8),
(8,8), (7,8), (6,8), (5,8)]
# calculate the b-field
def bfield(x,y,z):
b2 = np.zeros((xwidth,ywidth))
for x in range(xwidth):
for y in range(ywidth):
# number of wire elements
for i in range(21):
rx = (coord[i][0]+coord[i+1][0])/2. - x
ry = (coord[i][1]+coord[i+1][1])/2. - y
rz = z * 1.0 # = z-0
r = (rx**2+ry**2+rz**2)**0.5 # distance r between field and middle of the wire
dl = np.array([(coord[i+1][0]-coord[i][0]), (coord[i+1][1]-coord[i][1]), 0])
b = np.cross(dl, np.array([rx,ry,rz]))
e = constant*I*b/r**3
b2[y][x] += e[2] # why not x y?
return b2
xwidth = 15
ywidth = 15
delay = 1
x = np.arange(0, xwidth, delay)
y = np.arange(0, ywidth, delay)
X, Y = np.meshgrid(x, y)
slicee = 0.1
Z = bfield(X,Y,slicee)
# visualize
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
x1 = array([0,1,2,3,4,5,6,7,8,9,9,9,9,9,9,9,9,9,8,7,6,5])
y1 = array([0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,8,8,8,8])
plot(x1,y1)
plt.title('magnetic field')
plt.show()
Last edit:
Finally i did it without numpy.
The following version works.
# Calculation of a magnetic field of a wire
# later I want to to it three dimensional
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
# constant
I = 10000000000
mu0 = 1e-7
constant = mu0/(4*np.pi)
# wire position
coord = [(10,10), (20,10), (20,20), (10,20), (10,25)]
coord2 = []
# devide path of the wire in parts of length one
parts = 0
for n in range(len(coord)-1):
lengthx = coord[n+1][0] - coord[n][0]
lengthy = coord[n+1][1] - coord[n][1]
length = (lengthx**2 + lengthy**2)**.5
for m in range(int(length)):
coord2.append((coord[n][0]+lengthx/length*m, coord[n][1]+lengthy/length*m))
parts += 1
# calculate the b-field
def bfield(x,y,z):
b = 0
for i in range(parts-1):
dlx = coord2[i+1][0]-coord2[i][0]
dly = coord2[i+1][1]-coord2[i][1]
dlz = 0
dl = np.array([dlx,dly,dlz])
rspace_minus_rwire_x = x - (coord2[i][0]+dlx)
rspace_minus_rwire_y = y - (coord2[i][1]+dly)
rspace_minus_rwire_z = z - 0
rspace_minus_rwire = np.array([rspace_minus_rwire_x, rspace_minus_rwire_y, rspace_minus_rwire_z])
absr = (rspace_minus_rwire_x**2 + rspace_minus_rwire_y**2 + rspace_minus_rwire_z**2)**0.5
a = constant * I * np.cross(dl, rspace_minus_rwire) / absr**3
b += (a[0]**2 + a[1]**2 + a[2]**2)**0.5
return b
xwidth = 26
ywidth = 26
z = 1
bmatrix = np.zeros((xwidth,ywidth))
for x in range(xwidth):
for y in range(ywidth):
bmatrix[x][y] = bfield(x,y,z)
# visualize
plt.figure()
x = range(xwidth)
y = range(ywidth)
z = bmatrix[x][y].T
contour(x,y,z,35)
plt.show()
Change
dl = ((coord[i+1][0]-coord[i][0]), (coord[i+1][1]-coord[i][1]), 0)
bfield2 = konstante*I*cross(dl, (rx,ry,rz))/r**3 # i have to use numpy arrays
To
dl = np.array([(coord[i+1][0]-coord[i][0]), (coord[i+1][1]-coord[i][1]), 0])
bfield2 = konstante*I*cross(dl, np.array([rx,ry,rz]))/r**3 # i have to use numpy arrays
I don't have Numpy on this machine, so this is untested. Basically, change your tuples into a numpy arrays using np.array.
You could probably also leave dl alone and change bfield2 to use np.array(dl) instead of dl.
This is not any answer tou your original question, but just a hint how to operate with numpy arrays:
In []: coord = [(0,0), (1,0), (2,0), (3,0), (4,0), (5,0), (6,0), (7,0), (8,0), (9,0), (9,1), (9,2), (9,3), (9,4), (9,5), (9,6), (9,7), (9,8)]
In []: coord= np.array(coord).T
In []: coord
Out[]:
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]])
In [170]: wiremiddle= (coord[:, 1:]- coord[:, :-1])/ 2.
In []: wiremiddle
Out[]:
array([[ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],
[ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]])
I'll hope this will help to rewrite your code.
you do this:
bfield3 = 0
maybe you should do something like this:
bfield3 = np.zeros((len(...),len(...)))
Or maybe bfield3 is allocated already? and you just want to set all values to zero? Then do this:
bfield3[:,:] = 0
I would avoid bringing in the visual module, which it appears you are using only for the call to 'cross' and use numpy's cross in its place:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.cross.html
You'll have to change a couple of lines to make dl and the second argument of cross numpy arrays
dl = np.array([(coord[i+1][0]-coord[i][0]), (coord[i+1][1]-coord[i][1]), 0])
and double check to make sure that numpy's cross is doing the same thing that visual's was.
If you insist on using the visual cross method, then it is clear from the error that you're having a type conflict which you'll have to resolve

Categories