How can I avoid tetrahedra lying in a plane using scipy.Delaunay? - python

I'm trying to create a tetrahedral mesh in Python 3.7.3.. But some tetrahedra are flat, i.e. their vertices are on a flat surface.
import numpy as np
from scipy.spatial import Delaunay
# Coordinates of 3x3x3 equally distant points of a cube
x = np.linspace(0, 1, 3)
X1, X2, X3 = np.meshgrid(x,x,x)
vertices = np.hstack([X1.reshape(-1,1),X2.reshape(-1,1),X3.reshape(-1,1)])
# Using Delaunay
tri = Delaunay(vertices).simplices
# tetrahedra
simplices = vertices[tri,:]
As you can see below, already with the third tetrahedron all y-coordinates are 0.5. This leads later to a singular matrix.
print(simplices[0:3])
[[[1. 0.5 1. ]
[0.5 1. 0.5]
[1. 0.5 0.5]
[0.5 0.5 0.5]]
[[1. 0.5 1. ]
[1. 1. 0.5]
[0.5 1. 0.5]
[1. 0.5 0.5]]
[[1. 0.5 1. ]
[0.5 0.5 1. ]
[1. 0.5 0.5]
[0.5 0.5 0.5]]]
Do you know how I can work around this problem? Thank you very much.

Unlike 2D, for 3D there is no known algorithm that definitely generates a Delaunay triangulation of a given domain. There are however some mesh generation packages which produce pretty good tetrahedral meshes. For example
gmsh/pygmsh
pygalmesh
mshr
...
(Disclaimer: I'm the author of pygmsh and pygalmesh.)

Related

Slice mesh with trimesh

I work on a big .stl file which I want to cut into pieces using a bounding box.
For this purpose, I use trimesh python package to load the .stl.
Here is the piece of code used to generate the bounding box :
box = trimesh.creation.box(extents=[1.5, 1.5, 1.5])
print(box.facets_origin)
print(box.facets_normal)
So I get as a return :
print(box.facets_origin)
[[-0.75 -0.75 0.75]
[ 0.75 -0.75 -0.75]
[-0.75 0.75 -0.75]
[-0.75 -0.75 0.75]
[-0.75 0.75 0.75]
[ 0.75 0.75 -0.75]]
print(box.facets_normal)
[[-1. 0. 0.]
[ 0. -1. 0.]
[ 0. 0. -1.]
[ 0. 0. 1.]
[ 0. 1. 0.]
[ 1. 0. 0.]]
This means that the box's center of gravity is at (0, 0, 0)
And then I plan to cut the big stl using slice_plane function.
However, I would like to change the location of the bounding box's center of mass, or facets' location.
How this could be done using trimesh ? Or another Python package ?
Thanks in advance for your help !
Joachim
Can you not translate the box using
mesh.apply_transform(trimesh.transformations.scale_and_translate())
https://github.com/mikedh/trimesh/blob/master/trimesh/transformations.py

How to blur 3D array of points, while maintaining their original values? (Python)

I have a sparse 3D array of values. I am trying to turn each "point" into a fuzzy "sphere", by applying a Gaussian filter to the array.
I would like the original value at the point (x,y,z) to remain the same. I just want to create falloff values around this point... But applying the Gaussian filter changes the original (x,y,z) value as well.
I am currently doing this:
dataCube = scipy.ndimage.filters.gaussian_filter(dataCube, 3, truncate=8)
Is there a way for me to normalize this, or do something so that my original values are still in this new dataCube? I am not necessarily tied to using a Gaussian filter, if that is not the best approach.
You can do this using a convolution with a kernel that has 1 as its central value, and a width smaller than the spacing between your data points.
1-d example:
import numpy as np
import scipy.signal
data = np.array([0,0,0,0,0,5,0,0,0,0,0])
kernel = np.array([0.5,1,0.5])
scipy.signal.convolve(data, kernel, mode="same")
gives
array([ 0. , 0. , 0. , 0. , 2.5, 5. , 2.5, 0. , 0. , 0. , 0. ])
Note that fftconvolve might be much faster for large arrays. You also have to specify what should happen at the boundaries of your array.
Update: 3-d example
import numpy as np
from scipy import signal
# first build the smoothing kernel
sigma = 1.0 # width of kernel
x = np.arange(-3,4,1) # coordinate arrays -- make sure they contain 0!
y = np.arange(-3,4,1)
z = np.arange(-3,4,1)
xx, yy, zz = np.meshgrid(x,y,z)
kernel = np.exp(-(xx**2 + yy**2 + zz**2)/(2*sigma**2))
# apply to sample data
data = np.zeros((11,11,11))
data[5,5,5] = 5.
filtered = signal.convolve(data, kernel, mode="same")
# check output
print filtered[:,5,5]
gives
[ 0. 0. 0.05554498 0.67667642 3.0326533 5. 3.0326533
0.67667642 0.05554498 0. 0. ]

affinity propagation in python

I am seeing something strange while using AffinityPropagation from sklearn. I have a 4 x 4 numpy ndarray - which is basically the affinity-scores. sim[i, j] has the affinity score of [i, j]. Now, when I feed into the AffinityPropgation function, I get a total of 4 labels.
here is an similar example with a smaller matrix:
In [215]: x = np.array([[1, 0.2, 0.4, 0], [0.2, 1, 0.8, 0.3], [0.4, 0.8, 1, 0.7], [0, 0.3, 0.7, 1]]
.....: )
In [216]: x
Out[216]:
array([[ 1. , 0.2, 0.4, 0. ],
[ 0.2, 1. , 0.8, 0.3],
[ 0.4, 0.8, 1. , 0.7],
[ 0. , 0.3, 0.7, 1. ]])
In [217]: clusterer = cluster.AffinityPropagation(affinity='precomputed')
In [218]: f = clusterer.fit(x)
In [219]: f.labels_
Out[219]: array([0, 1, 1, 1])
This says (according to Kevin), that the first sample (0th-indexed row) is a cluster (Cluster # 0) on its own and the rest of the samples are in another cluster (cluster # 1). But, still, I do not understand this output. What is a sample here? What are the members? I want to have a set of pairs (i, j) assigned to one cluster, another set of pairs assigned to another cluster and so on.
It looks like a 4-sample x 4-feature matrix..which I do not want. Is this the problem? If so, how to convert this to a nice 4-sample x 4-sample affinity-matrix?
The documentation (http://scikit-learn.org/stable/modules/generated/sklearn.cluster.AffinityPropagation.html) says
fit(X, y=None)
Create affinity matrix from negative euclidean distances, then apply affinity propagation clustering.
Parameters:
X: array-like, shape (n_samples, n_features) or (n_samples, n_samples) :
Data matrix or, if affinity is precomputed, matrix of similarities / affinities.
Thanks!
By your description it sounds like you are working with a "pairwise similarity matrix": x (although your example data does not show that). If this is the case your matrix should be symmertric so that: sim[i,j] == sim[j,i] with your diagonal values equal to 1. Example similarity data S:
S
array([[ 1. , 0.08276253, 0.16227766, 0.47213595, 0.64575131],
[ 0.08276253, 1. , 0.56776436, 0.74456265, 0.09901951],
[ 0.16227766, 0.56776436, 1. , 0.47722558, 0.58257569],
[ 0.47213595, 0.74456265, 0.47722558, 1. , 0.87298335],
[ 0.64575131, 0.09901951, 0.58257569, 0.87298335, 1. ]])
Typically when you already have a distance matrix you should use affinity='precomputed'. But in your case, you are using similarity. In this specific example you can transform to pseudo-distance using 1-D. (The reason to do this would be because I don't know that Affinity Propagation will give you expected results if you give it a similarity matrix as input):
1-D
array([[ 0. , 0.91723747, 0.83772234, 0.52786405, 0.35424869],
[ 0.91723747, 0. , 0.43223564, 0.25543735, 0.90098049],
[ 0.83772234, 0.43223564, 0. , 0.52277442, 0.41742431],
[ 0.52786405, 0.25543735, 0.52277442, 0. , 0.12701665],
[ 0.35424869, 0.90098049, 0.41742431, 0.12701665, 0. ]])
With that being said, I think this is where your interpretation was off:
This says that the first 3-rows are similar, 4th row is a cluster on its own, and the 5th row is also a cluster on its own. Totally of 3 clusters.
The f.labels_ array:
array([0, 1, 1, 1, 0])
is telling you that samples (not rows) 0 and 4 are in cluster 0 AND that samples 2, 3, and 4 are in cluster 1. You don't need 25 different labels for a 5 sample problem, that wouldn't make sense. Hope this helps a little, try the demo (inspect the variables along the way and compare them with your data), which starts with raw data; it should help you decide if Affinity Propagation is the right clustering algorithm for you.
According to this page https://scikit-learn.org/stable/modules/clustering.html
you can use a similarity matrix for AffinityPropagation.

Python ( robot module): conversion hom. transformation (rotation) matrix and Euler or RPY angles

I'm using in the robotics toolbox (c.q. robot module) in Python http://code.google.com/p/robotics-toolbox-python/) and I'm utterly confused how to interpret the following conversion results (I've done things like this in the past could always work them out...).
A simple phi=PI/10 rotation about the x-axes produces the following (3x3) rotation matrix:
R =
[ 1. 0. 0. ]
[ 0. 0.99609879 -0.08824514]
[-0. 0.08824514 0.99609879]]
( where 0.996..=cos(phi) and 0.0882..=sin(phi) )
with corresponding (4x4) homogeneous transformation matrix:
T = | R 0 | =
| 0 1 |
[[ 1. 0. 0. 0. ]
[ 0. 0.99609879 -0.08824514 0. ]
[-0. 0.08824514 0.99609879 0. ]
[ 0. 0. 0. 1. ]]
Conversion of T into angle representation produces the following:
RPY (Roll, pitch, yaw) angles (around z, y and x axes, respectively, ... I presume):
print robot.tr2rpy(T)
[[ 0. 0. 0.08836007]]
Problem: How can the x-rotation be the last element (rather than the first)....?
Further:
Euler angles (around x, y and z axes, respectively, ... I presume):
print robot.tr2eul(T)
[[-1.57079633 0.08836007 1.57079633]]
( = [[ -PI/4, sin(phi), PI/4 ]] ))
Problem: My interpretation (sequential rotation around x,y,z axes) tells me the result is dead wrong...?
What am I missing? Thanks.
-- Henk
Solved:
With successive angles, a[0],a[1],a[2],
1) For Euler angles these are successive rotations around Z-Y-Z axes;
2) For RPY angles these are successive Y(aw)-P(itch)-R(roll) rotations (i.e around Z-Y-X axes).

Euclidian Distances between points

I have an array of points in numpy:
points = rand(dim, n_points)
And I want to:
Calculate all the l2 norm (euclidian distance) between a certain point and all other points
Calculate all pairwise distances.
and preferably all numpy and no for's. How can one do it?
If you're willing to use SciPy, the scipy.spatial.distance module (the functions cdist and/or pdist) do exactly what you want, with all the looping done in C. You can do it with broadcasting too but there's some extra memory overhead.
This might help with the second part:
import numpy as np
from numpy import *
p=rand(3,4) # this is column-wise so each vector has length 3
sqrt(sum((p[:,np.newaxis,:]-p[:,:,np.newaxis])**2 ,axis=0) )
which gives
array([[ 0. , 0.37355868, 0.64896708, 1.14974483],
[ 0.37355868, 0. , 0.6277216 , 1.19625254],
[ 0.64896708, 0.6277216 , 0. , 0.77465192],
[ 1.14974483, 1.19625254, 0.77465192, 0. ]])
if p was
array([[ 0.46193242, 0.11934744, 0.3836483 , 0.84897951],
[ 0.19102709, 0.33050367, 0.36382587, 0.96880535],
[ 0.84963349, 0.79740414, 0.22901247, 0.09652746]])
and you can check one of the entries via
sqrt(sum ((p[:,0]-p[:,2] )**2 ))
0.64896708223796884
The trick is to put newaxis and then do broadcasting.
Good luck!

Categories