5. operations on coordinates, scalar fields, colors, normals, meshes with Numpy
5.1. Read, modify or create cloud coordinates with Numpy
There are two ways to access the cloud coordinates from Numpy.
If you want to modify the data in place, without copy, use
toNpArray()
if you prefer to copy the scalar field data, use
toNpArrayCopy()
cloud = cc.loadPointCloud(getSampleCloud(5.0)) coords = cloud.toNpArrayCopy() if coords.shape != (cloud.size(), 3): raise RuntimeErrorThe above code snippet is from
test004.py
.
You can fill the cloud coordinates with coordsFromNPArray_copy()
.
The array should have a shape (numberOfPoints,3).
WARNING Be sure to have an array data in C-style contiguous order. For instance, a transpose operation do not reorder the data in memory. Check the boolean array.flags
array.flags['C_CONTIGUOUS']
and, ifFalse
, reorder witharray.copy(order='C')
.The cloud memory is reserved /resized automatically.
See below An example of point cloud with scalar field created from Numpy arrays.
5.2. Read, modify or create a scalar field with Numpy
There are two ways to access to the scalar field data from Numpy.
If you want to modify the data in place, without copy, use
toNpArray()
dic = cloud1.getScalarFieldDic() sf1 = cloud1.getScalarField(dic['Coord. Z']) max1 = sf1.getMax() asf1 = sf1.toNpArray() # access to Numpy array, without copy asf1[0] = 2*max1 # modification in place sf1.computeMinAndMax()The above code snippet is from
test002.py
.
if you prefer to copy the scalar field data, use
toNpArrayCopy()
You can fill a cloud scalar field with fromNpArrayCopy()
.
The method checks if the scalar field and the array have the same size, before copy.
See below An example of point cloud with scalar field created from Numpy arrays.
5.3. An example of point cloud with scalar field created from Numpy arrays
# --- generate a set of coords and a scalar field
npts = 10000000
phi = 2*np.pi*np.random.random((npts))
theta = 2*np.pi*np.random.random((npts))
r = 5 + 0.3*np.sin(2*2*np.pi*phi + 3*2*np.pi*theta)
x = np.float32(r*np.sin(phi)*np.cos(theta))
y = np.float32(r*np.sin(phi)*np.sin(theta))
z = np.float32(r*np.cos(phi))
coords = np.column_stack((x,y,z))
dr = np.float32(np.sqrt(x*x + y*y + z*z) -5)
# --- create the pointCloud, add the scalar field
cl = cc.ccPointCloud("boule")
cl.coordsFromNPArray_copy(coords)
cl.addScalarField("delta")
sf = cl.getScalarField(0)
sf.fromNpArrayCopy(dr)
# --- save the point cloud
res = cc.SavePointCloud(cl, os.path.join(dataDir, "boule.bin"))
The above code snippet is from test017.py
.
5.4. Access to the indexes of triangles nodes in a mesh
In a mesh, the array of triangles indexes has a shape(number of triangles, 3), with indexes corresponding to the indexes of nodes in the associated cloud.
The method IndexesToNpArray()
gives access to the array without copy.
the corresponding nodes coordinates are given by the method toNpArray()
from the associated cloud (getAssociatedCloud()
.
The method IndexesToNpArray_copy()
creates a copy of the array of indexes.
# --- access to the numpy array of node indexes (one row per triangle)
d = mesh1.IndexesToNpArray()
if d.shape != (19602, 3):
raise RuntimeError
if d.dtype != np.dtype('uint32'):
raise RuntimeError
d2 = mesh1.IndexesToNpArray_copy()
if d2.shape != (19602, 3):
raise RuntimeError
if d2.dtype != np.dtype('uint32'):
raise RuntimeError
The above code snippet is from test011.py
.
5.5. Access to the array of colors in cloud
The colors are stored in an array of shape(number of nodes, 4), the four components are R, G, B and alpha, each stored in a 8bits integer.
The method colorsToNpArray()
gives access to the array of colors without copy,
the method colorsToNpArrayCopy()
creates a copy of the array.
cola = cloud.colorsToNpArray()
if not cola.shape == (1000000, 4):
raise RuntimeError
if not cola.dtype == 'uint8':
raise RuntimeError
colaCopy = cloud.colorsToNpArrayCopy()
if not colaCopy.shape == (1000000, 4):
raise RuntimeError
if not colaCopy.dtype == 'uint8':
raise RuntimeError
The above code snippet is from test029.py
.
5.6. Read, modify or create normals with Numpy
Normals can be exported as Numpy arrays with normalsToNpArrayCopy()
.
As normals are stored compressed in the CloudCompare cloud,
the method decompress the values in a new Numpy array (owned by Python).
In the example below, we check that this is the same as converting normals into scalar fields
and exporting these scalar fields to Numpy arrays.
cloud = cc.loadPointCloud(getSampleCloud(5.0))
cc.computeNormals([cloud])
cloud.exportNormalToSF(True, True, True)
dic = cloud.getScalarFieldDic()
sfx = cloud.getScalarField(dic['Nx'])
sfy = cloud.getScalarField(dic['Ny'])
sfz = cloud.getScalarField(dic['Nz'])
asfx = sfx.toNpArray()
asfy = sfy.toNpArray()
asfz = sfz.toNpArray()
normals=cloud.normalsToNpArrayCopy()
dx = normals[:,0] -asfx
dy = normals[:,1] -asfy
dz = normals[:,2] -asfz
if dx.max() != 0 or dx.min() !=0:
raise RuntimeError
if dy.max() != 0 or dy.min() !=0:
raise RuntimeError
if dz.max() != 0 or dz.min() !=0:
raise RuntimeError
It is also possible to import Numpy arrays as normals with normalsFromNpArrayCopy()
.
The numpy array must have the right type, shape and size.
During import, the normals are normalized and compressed.
In the following example, whe invert and denormalize the numpy array of normals before import/
normals *=-0.5 # an example of modification of the normals: invert and denormalize
cloud.normalsFromNpArrayCopy(normals)
The above code snippets are from test054.py
.