How does CloudCompare convert normals to Dip/Dip directions
Posted: Mon Mar 21, 2016 11:49 am
Hi!
First of all, I want to say thanks to Daniel and all the people involved in the develop of this amazing software, really thank you all for doing this. CloudCompare is awesome and I use it every day.
So:
I'm working (more like learn by doing) on a Python's library for managing pointclounds and I'm getting weird results when I use my functions add_inclination and add_orientation and compare with the Dip/Dip directions SF obtained from CC.
Here is the structure of a pointcloud open with my library (in this example a sphere created in CC):
And here are my functions for Inclination:
And orientation:
The problem happens when I use this functions over the pointcloud and check the results by comparing with CC's SF:
As you can see, I'm having errors (aprox: 0.1-0.2 decimals) on every inclination results and I honestly don't know what happens with the orientations, as i get the same results in angles < 180 and but I have a 180 degrees difference in some others.
My approach (I've been programming for only 6 months so it may be something wrong with it) is to ignore the z-value of the normal and compute de orientation as follows:
So I would like to know how CloudCompare "extracts" the inclination and orientation values from normals, and if it is possible, discover what is the problem with my code.
Thank you very much.
First of all, I want to say thanks to Daniel and all the people involved in the develop of this amazing software, really thank you all for doing this. CloudCompare is awesome and I use it every day.
So:
I'm working (more like learn by doing) on a Python's library for managing pointclounds and I'm getting weird results when I use my functions add_inclination and add_orientation and compare with the Dip/Dip directions SF obtained from CC.
Here is the structure of a pointcloud open with my library (in this example a sphere created in CC):
Code: Select all
esfera = PyntCloud.from_ply('Sphere.ply')
esfera.vertex
Out[3]:
array([ (0.2515081465244293, 0.05602749437093735, 1.9830318689346313, 0.12660565972328186, 0.02801010198891163, 0.9915575981140137, 7.450349807739258, 77.52488708496094),
(0.09723527729511261, 0.02066999115049839, 1.9934484958648682, 0.048643846064805984, 0.011384730227291584, 0.9987513422966003, 2.863548517227173, 76.82744598388672),
(0.17640848457813263, 0.028193067759275436, 1.9881943464279175, 0.08916780352592468, 0.01611466333270073, 0.9958862066268921, 5.198856830596924, 79.75591278076172),
...,
(0.17817874252796173, -0.046098098158836365, -1.9879237413406372, 0.08992616087198257, -0.02275240235030651, -0.9956884980201721, 5.322407245635986, 284.19854736328125),
(0.2002459168434143, -0.002330917865037918, -1.986855149269104, 0.09960971027612686, -0.0010710721835494041, -0.9950260519981384, 5.717002868652344, 270.6160583496094),
(0.12885123491287231, -0.03245270624756813, -1.9912745952606201, 0.06637085974216461, -0.01580258458852768, -0.9976698756217957, 3.912114381790161, 283.3924865722656)],
dtype=[('x', '<f4'), ('y', '<f4'), ('z', '<f4'), ('nx', '<f4'), ('ny', '<f4'), ('nz', '<f4'), ('scalar_Dip_(degrees)', '<f4'), ('scalar_Dip_direction_(degrees)', '<f4')])
esfera.vertex['nx']
Out[4]:
array([ 0.12660566, 0.04864385, 0.0891678 , ..., 0.08992616,
0.09960971, 0.06637086], dtype=float32)
esfera.vertex[-1]['nx']
Out[5]: 0.06637086
Code: Select all
def add_inclination(self, degrees=True):
""" Adds inclination (with respect to z-axis) values to PyntCloud.vertex
This function expects the PyntCloud to have a numpy structured array
with normals x,y,z values (correctly named) as the corresponding vertex
atribute.
Args:
degrees (Optional[bool]): Set the oputput inclination units.
If True(Default) set units to degrees.
If False set units to radians.
"""
#: set copy to False for efficience in large pointclouds
nx = self.vertex['nx'].astype(np.float64, copy=False)
nz = self.vertex['nz'].astype(np.float64, copy=False)
#: get inclination
angle = np.arctan(np.absolute(nx / nz))
if degrees == False:
inclination = np.array(angle, dtype=[('inr', 'f4')])
else:
inclination = np.array((180 * angle / np.pi), dtype=[('ind', 'f4')])
#: merge the structured arrays and replace the old vertex attribute
self.vertex = join_struct_arrays([self.vertex, inclination])
Code: Select all
def add_orientation(self, degrees=True):
""" Adds orientation (with respect to y-axis) values to PyntCloud.vertex
This function expects the PyntCloud to have a numpy structured array
with normals x,y,z values (correctly named) as the corresponding vertex
atribute.
Args:
degrees (Optional[bool]): Set the oputput orientation units.
If True(Default) set units to degrees.
If False set units to radians.
"""
#: set copy to False for efficience in large pointclouds
nx = self.vertex['nx'].astype(np.float64, copy=False)
ny = self.vertex['ny'].astype(np.float64, copy=False)
#: get orientations
angle = np.arctan(np.absolute(nx / ny))
#: mask for every quadrant
q2 = np.logical_and((self.vertex['nx']>0),(self.vertex['ny']<0))
q3 = np.logical_and((self.vertex['nx']<0),(self.vertex['ny']<0))
q4 = np.logical_and((self.vertex['nx']<0),(self.vertex['ny']>0))
#: apply modification for every quadrant
angle[q2] = np.pi - angle[q2]
angle[q3] = np.pi + angle[q3]
angle[q4] = (2*np.pi) - angle[q4]
if degrees == False:
orientation = np.array(angle, dtype=[('orir', 'f4')])
else:
orientation = np.array((180 * angle / np.pi), dtype=[('orid', 'f4')])
#: merge the structured arrays and replace the old vertex attribute
self.vertex = join_struct_arrays([self.vertex, orientation])
Code: Select all
esfera.vertex[-1]
Out[8]: (0.12885123491287231, -0.03245270624756813, -1.9912745952606201, 0.06637085974216461, -0.01580258458852768, -0.9976698756217957, 3.912114381790161, 283.3924865722656, 3.8060436248779297, 103.39249420166016)
esfera.vertex[0]
Out[9]: (0.2515081465244293, 0.05602749437093735, 1.9830318689346313, 0.12660565972328186, 0.02801010198891163, 0.9915575981140137, 7.450349807739258, 77.52488708496094, 7.276360511779785, 77.52488708496094)
My approach (I've been programming for only 6 months so it may be something wrong with it) is to ignore the z-value of the normal and compute de orientation as follows:
So I would like to know how CloudCompare "extracts" the inclination and orientation values from normals, and if it is possible, discover what is the problem with my code.
Thank you very much.