Plot and fill 3D volumes in Python - python

I am working with some 3D (volumetric) data using Python, and for every tetrahedron, I have not only the vertices's coordinates but also a fourth dimension which is the value of some parameter for that tetrahedron volume.
For example:
# nodes coordinates that defines a tetrahedron volume:
x = [0.0, 1.0, 0.0, 0.0]
y = [0.0, 0.0, 1.0, 0.0]
z = [0.0, 0.0, 0.0, 1.0]
# Scaler value of the potential for the given volume:
c = 100.0
I would like to plot a 3D volume (given by the nodes coordinates) filled with some solid color, which would represent the given value C.
How could I do that in Python 3.6 using its plotting libraries?

You can use mayavi.mlab.triangular_mesh():
from mayavi import mlab
from itertools import combinations, chain
x = [0.0, 1.0, 0.0, 0.0, 2.0, 3.0, 0.0, 0.0]
y = [0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.0]
z = [0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 3.0]
c = [20, 30]
triangles = list(chain.from_iterable(combinations(range(s, s+4), 3) for s in range(0, len(x), 4)))
c = np.repeat(c, 4)
mlab.triangular_mesh(x, y, z, triangles, scalars=c)

Related

Open3D registration with ICP shows error of 0 and returns the input transformation

I try to use the ICP algorithm of open3d to find a transformation that minimizes the distance between 2 point clouds and loosely followed their tutorial page: http://www.open3d.org/docs/latest/tutorial/pipelines/icp_registration.html
(I use Ubuntu 20.04)
I tried to use point clouds from my ouster128, but it didn't work and therefore I decided to use 2 'dummy' point clouds that I create with numpy. The icp registration method gets a transformation as input and, in my case, always returns the input transformation (it basically does nothing, probably because the errors are 0). Here's the code (should be ready to use when copy pasted):
import numpy as np
import copy
import open3d as o3d
def draw_registration_result(source, target, transformation):
source_temp = copy.deepcopy(source)
target_temp = copy.deepcopy(target)
source_temp.paint_uniform_color([1, 0.206, 0])
target_temp.paint_uniform_color([0, 0.651, 0.929])
print("Transformation: " + str(transformation))
source_temp.transform(transformation)
coord_frame = o3d.geometry.TriangleMesh.create_coordinate_frame()
o3d.visualization.draw_geometries([source_temp, target_temp, coord_frame],
zoom=0.5,
front=[0.9288, -0.2951, -0.2242],
lookat=[0, 1, 1],
up=[0, 0, 1])
src_points = np.array([
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[2.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
[2.0, 1.0, 0.0],
[0.0, 2.0, 0.0],
[1.0, 2.0, 0.0],
[2.0, 2.0, 0.0],
[0.0, 3.0, 0.0],
[1.0, 3.0, 0.0],
[2.0, 3.0, 0.0],
])
tgt_points = np.array([
[0.0, 0.0, 0.1], # Due to the 0.1 the clouds do not match perfectly
[1.0, 0.0, 0.1],
[2.0, 0.0, 0.1],
[0.0, 1.0, 0.1],
[1.0, 1.0, 0.1],
[2.0, 1.0, 0.1],
[0.0, 2.0, 0.1],
[1.0, 2.0, 0.1],
[2.0, 2.0, 0.1],
[0.0, 3.0, 0.1],
[1.0, 3.0, 0.1],
[2.0, 3.0, 0.1],
])
o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)
source = o3d.geometry.PointCloud()
source.points = o3d.utility.Vector3dVector(src_points)
target = o3d.geometry.PointCloud()
target.points = o3d.utility.Vector3dVector(tgt_points)
trans_init = np.asarray([[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0]])
threshold = 0.02
reg_p2p = o3d.pipelines.registration.registration_icp(
source, target, threshold, trans_init,
o3d.pipelines.registration.TransformationEstimationPointToPoint())
print("Post Registration")
print("Inlier Fitness: ", reg_p2p.fitness)
print("Inlier RMSE: ", reg_p2p.inlier_rmse)
draw_registration_result(source, target, reg_p2p.transformation)
source and target are the same point clouds. Only difference is, that target is translated by 0.1 in z direction. The initial transformation is the identity matrix. The matrix that I would expect as output is the same as I, just with I[2][4]=0.1. Now, the fitness and inlier_rmse are 0. Which makes no sense (unless I completely misunderstood something) as that would mean that the clouds match perfectly, which they obviously don't do. Sometimes the fitness is not zero (for example, when source and target are the same clouds, except that 3 points of target are translated by 0.1).
What I tried before posting this thread:
2 different versions of open3d (0.15.2 and 0.16.0).
different point clouds
different initial transformations
some thresholds, 2e-10, 2e-6, 0.2
(The visualization window is white and the camera has to be rotated to view the clouds)
So, what am I doing wrong here? Thanks in advance.
Ok, I fixed it in the end.
I'd blame the documentation in this case as it says:
"max_correspondence_distance (float) – Maximum correspondence points-pair distance."
In their tutorial this parameter is called "threshold" and I would expect the algorithm to run until the error is less than the threshold. However, the algorithm doesn't start if the error is bigger than the threshold. This should be formulated more precisely in the documentation. And especially in the tutorial this has to be fixed. If I use an threshold of 50 it works as expected.

How to rescale an axis with matplotlib

I can create a plot as follows:
import matplotlib.pyplot as plt
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.colorbar()
plt.xlabel("axis x")
plt.ylabel("axis y")
plt.show()
But how can I change the axis itself, i.e. I want to transform e.g. the x-axis to a different range. For example, the value 0 on the plot that the code above generates corresponds to a value of -4.8573. And the value '4' of the plot above corresponds to a value of 12.443.
Then I want to have an axis with ticks at -5, 0, 10, 15 or so. How can I achieve this?
The real axis value can be calculated by
x_real = a + x * b
To rescale the x-axis range, you can use
plt.xticks(ticks, labels)
ticks: The list of old xtick locations.
labels: The labels to place at the given ticks locations.
so, You just need to provide the following code before plt.show():
plt.xticks(range(0, 5), range(-5, 16, 5))
# range(0, 5): current range
# range(-5, 16, 5): new range
# [0, 1, 2, 3, 4] -> [-5, 0, 5, 10, 15]
import matplotlib.pyplot as plt
import numpy as np
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.colorbar()
plt.xlabel("axis x")
plt.ylabel("axis y")
plt.xticks(range(0, 5), range(-5, 16, 5))
plt.show()
Which product this image(click here)
To auto interpolate, you could do something like this:
import matplotlib.pyplot as plt
import math
import numpy as np
n=5
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.colorbar()
x = [37.59390426045407, 38.00530354847739, 38.28412244348653, 38.74871247986305, 38.73175910429809, 38.869008864244016, 39.188234404976555, 39.92835838352555, 40.881394113153334, 41.686136269465884]
y = [0.1305391767832006, 0.13764519613447768, 0.14573326951792354, 0.15090729309032114, 0.16355823707239897, 0.17327106424274763, 0.17749746339532224, 0.17310384614773594, 0.16545780437882962, 0.1604752704890856]
def ceil_power_of_10(n):
exp = math.log(n, 10)
exp = math.ceil(exp)
return 10**exp
x0 = min(x)
x1 = max(x)
y0 = min(y)
y1 = max(y)
# Fill the 2D array
dx = (x1 - x0)/n
dy = (y1 - y0)/n
dx_steps = ceil_power_of_10(dx)
dy_steps = ceil_power_of_10(dy)
dx_steps_alpha = round((math.ceil(x1/dx_steps)*dx_steps) - (math.floor(x0/dx_steps)*dx_steps) )
dy_steps_alpha = round(((math.ceil(y1/dy_steps)*dy_steps) - (math.floor(y0/dy_steps)*dy_steps) ) / dy_steps)
x_new = np.linspace((math.floor(x0/dx_steps)*dx_steps), (math.ceil(x1/dx_steps)*dx_steps), dx_steps_alpha, endpoint=False)
y_new = np.linspace((math.floor(y0/dy_steps)*dy_steps), (math.ceil(y1/dy_steps)*dy_steps), dy_steps_alpha, endpoint=False)
labels_x = x_new
labels_x = [round(x,dx_steps) for x in labels_x]
positions_x = list(range(0, len(labels_x)))
labels_y = y_new
labels_y = [round(y/dy_steps) * dy_steps for y in labels_y]
positions_y = list(range(0, len(labels_y)))
# In the end, used to create a surface plot
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.xticks(positions_x, labels_x)
plt.yticks(positions_y, labels_y)
plt.xlabel("axis x")
plt.ylabel("axis y")
plt.show()
You mean like this?
import matplotlib.pyplot as plt
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.colorbar()
positions = [0,1,2,3,4]
labels = [-5, 0, 10, 15, 20]
plt.xticks(positions, labels)
plt.yticks(positions, labels)
plt.xlabel("axis x")
plt.ylabel("axis y")
plt.show()
If I understood correctly, this should help
view image
import matplotlib.pyplot as plt
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
plt.imshow(image, cmap="plasma", interpolation='nearest')
plt.colorbar()
plt.xlabel("axis x")
plt.ylabel("axis y")
#sets limes x
plt.xlim([-5,15])
#sets limes y
plt.ylim([-5,15])
plt.show()
You should compute the axes min, max and step values:
xmin = -4.8573
xmax = 12.443
dx = (xmax - xmin)/(np.shape(image)[0] - 1)
ymin = -5
ymax = 15
dy = (ymax - ymin)/(np.shape(image)[1] - 1)
then, then pass those values to extent parameter of imshow:
img = ax.imshow(image, cmap="plasma", interpolation='nearest', extent = [xmin - dx/2, xmax + dx/2, ymin - dy/2, ymax + dy/2])
finally, set up the axes ticks:
ax.set_xticks(np.linspace(xmin, xmax, (np.shape(image)[0])))
ax.set_yticks(np.linspace(ymin, ymax, (np.shape(image)[1])))
Complete Code
import matplotlib.pyplot as plt
import numpy as np
image = [[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2, 0.0, 0.1, 0.0 ,0.0],
[0.0, 0.0, 0.3, 0.0 ,0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0]]
print(image)
xmin = -4.8573
xmax = 12.443
dx = (xmax - xmin)/(np.shape(image)[0] - 1)
ymin = -5
ymax = 15
dy = (ymax - ymin)/(np.shape(image)[1] - 1)
fig, ax = plt.subplots()
img = ax.imshow(image, cmap="plasma", interpolation='nearest', extent = [xmin - dx/2, xmax + dx/2, ymin - dy/2, ymax + dy/2])
plt.colorbar(img)
ax.set_xlabel("axis x")
ax.set_ylabel("axis y")
ax.set_xticks(np.linspace(xmin, xmax, (np.shape(image)[0])))
ax.set_yticks(np.linspace(ymin, ymax, (np.shape(image)[1])))
plt.show()

Dividing certain members of a list in python

I have a matrix formed by a list of lists and I want to divide each member of the second half of each sublist by the integer in the first 3 members of each sublist. Here is my code:
def matrix():
a=[[12.0, 0.0, 0.0, 12, 156, -108], [0.0, 2.667, 0.0, -5.333, -77.333, 53.333], [0.0, 0.0, -0.0937, -0.0937, -1.4687, 1.0]]
for i in range(len(a)):
a[i] = [v/a[i][i] for v in a[i]]
return a
However, this code divides each entire sublist by the integer found in the first half of each sublist which gives me this output:
[[1.0, 0.0, 0.0, 1.0, 13.0, -9.0], [0.0, 1.0, 0.0, -1.9996250468691417, -28.996250468691414, 19.99737532808399], [-0.0, -0.0, 1.0, 1.0, 15.674493062966913, -10.672358591248665]]
I only want the second part of each sublist divided, not the first half. I need to obtain this output, as you can see the first 3 integers of each sublist must stay the same:
[[12,0,0,1,13,-9],[0,2.667,0,-2,-29,20],[0,0,-0.09375,1,15.6667,-10.6667]]
You can split up your logic into two steps:
Find the integer in the first three numbers.
Divide the second half of each sublist by that number.
def matrix():
a = [[12.0, 0.0, 0.0, 12, 156, -108],
[0.0, 2.667, 0.0, -5.333, -77.333, 53.333],
[0.0, 0.0, -0.0937, -0.0937, -1.4687, 1.0]]
for i in range(len(a)):
divisor = 0
for j in range(3):
if a[i][j]:
divisor = a[i][j]
break
for j in range(len(a[i]) // 2, len(a[i])):
a[i][j] = a[i][j] / divisor
return a
print(matrix())
Output:
[[12.0, 0.0, 0.0, 1.0, 13.0, -9.0], [0.0, 2.667, 0.0, -1.9996250468691417, -28.996250468691414, 19.99737532808399], [0.0, 0.0, -0.0937, 1.0, 15.674493062966913, -10.672358591248665]]

Does Pandas have a `contracting` window function

Does Pandas have a pandas.DataFrame.contracting window function that would be the opposite of the pandas.DataFrame.expanding without having to sort the data first? This would be similar to Spark's Window.unboundedFollowing as opposed to Window.unboundedPreceding.
I assume it would be with .rolling
Example:
import pandas as pd
testdf = pd.DataFrame({'A': [2, 2, 2, 5, 1, 0, 1, 2, 0, 1]})
testdf['A'].expanding(min_periods=1).apply(lambda x: (x > 2).any()).tolist()
# [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
The following uses .rolling to produce the same results as above.
testdf['A'].rolling(window=len(testdf), min_periods=1).apply(lambda x: (x > 2).any()).tolist()
# [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
How would I change .rolling to work in the opposite direction?
Results should be:
# [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
You could use
testdf.loc[::-1, 'A'].expanding(min_periods=1).apply(lambda x: (x > 2).any())[::-1]
which yields
0 1.0
1 1.0
2 1.0
3 1.0
4 0.0
5 0.0
6 0.0
7 0.0
8 0.0
9 0.0
Name: A, dtype: float64
This applies an expanding window to the Series in reversed order, then reverses that result, thus making the window contract.
Unfortunately, I don't think there is a option built into expanding for doing this.

Plotting mesh data from vtk python using matplotlib

The following questions makes use of vtk python but what I am attempting to do should not require any knowledge of vtk because I have converted the data I wish to plot into numpy arrays described below. If anyone does know of an improvement to the way I go about actually processing the vtk data into numpy, please let me know!
I have some data that I have extracted using vtk python. The data consists of a 3D unstructured grid and has several 'blocks'. The block I am interested in is block0. The data is contained at each cell rather than at each point. I wish to plot a contourf plot of a scalar variable on this grid using matplotlib. In essence my problem comes down to the following:
Given a set of cell faces with known vertices in space and a known scalar field variable, create a contour plot as one would get if one had created a numpy.meshgrid and used plt.contourf/plt.pcolormesh etc. Basically I post process my vtk data like so:
numCells = block0.GetCells().GetNumberOfCells()
# Array of the 8 vertices that make up a cell in 3D
cellPtsArray = np.zeros((numCells,8,3))
# Array of the 4 vertices that make up a cell face
facePtsArray = np.zeros((numCells,4,3))
#Array to store scalar field value from each cell
valueArray = np.zeros((numCells,1))
for i in xrange(numCells):
cell = block0.GetCell(i)
numCellPts = cell.GetNumberOfPoints()
for j in xrange(numCellPts):
cellPtsArray[i,j,:] = block0.GetPoint(cell.GetPointId(j))
valueArray[i] = block0.GetCellData().GetArray(3).GetValue(i)
xyFacePts = cell.GetFaceArray(3)
facePtsArray[i,:,:] = cellPtsArray[i,xyFacePts,:]
Now I wish to create a contour plot of this data (fill each cell in space according to an appropriate colormap of the scalar field variable). Is there a good built in function in matplotlib to do this? Note that I cannot use any form of automatic triangulation-the connectivity of the mesh is already specified by facePtsArray by the fact that connections between points of a cell have been ordered correctly (see my plot below)
Here is some test data:
import numpy as np
import matplotlib.pyplot as plt
# An example of the array containing the mesh information: In this case the
# dimensionality is (9,4,3) denoting 9 adjacent cells, each with 4 vertices and
# each vertex having (x,y,z) coordinates.
facePtsArray = np.asarray([[[0.0, 0.0, 0.0 ],
[1.0, 0.0, 0.0 ],
[1.0, 0.5, 0.0 ],
[0.0, 0.5, 0.0 ]],
[[0.0, 0.5, 0.0 ],
[1.0, 0.5, 0.0 ],
[1.0, 1.0, 0.0 ],
[0.0, 1.0, 0.0 ]],
[[0.0, 1.0, 0.0 ],
[1.0, 1.0, 0.0 ],
[1.0, 1.5, 0.0 ],
[0.0, 1.5, 0.0 ]],
[[1.0, 0.0, 0.0 ],
[2.0, -0.25, 0.0],
[2.0, 0.25, 0.0],
[1.0, 0.5, 0.0]],
[[1.0, 0.5, 0.0],
[2.0, 0.25, 0.0],
[2.0, 0.75, 0.0],
[1.0, 1.0, 0.0]],
[[1.0, 1.0, 0.0],
[2.0, 0.75, 0.0],
[2.0, 1.25, 0.0],
[1.0, 1.5, 0.0]],
[[2.0, -0.25, 0.0],
[2.5, -0.75, 0.0],
[2.5, -0.25, 0.0 ],
[2.0, 0.25, 0.0]],
[[2.0, 0.25, 0.0],
[2.5, -0.25,0.0],
[2.5, 0.25, 0.0],
[2.0, 0.75, 0.0]],
[[2.0, 0.75, 0.0],
[2.5, 0.25, 0.0],
[2.5, 0.75, 0.0],
[2.0, 1.25, 0.0]]])
valueArray = np.random.rand(9) # Scalar field values for each cell
plt.figure()
for i in xrange(9):
plt.plot(facePtsArray[i,:,0], facePtsArray[i,:,1], 'ko-')
plt.show()

Categories

Resources