<a id="e3d-point-cloud"></a>

# Point Cloud

A point cloud is a set of 3D points (x, y and z coordinates) representing the scanned object in the world metric space.

![image](assets/images/easy3d/basic_concepts_2.webp)

In addition to the calibration process included in **Easy3D**, point clouds can be produced using various 3D acquisition techniques, like stereo reconstruction or time of flight cameras.

<a id="e3d-pc-attributes"></a>

## Mapping Attributes

* In addition to the $(x, y, z)$ coordinates, you can store other components in an [`EPointCloud`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html) such as normals, colors, intensities, textures, indexes, confidences, distances, curvatures and other custom attributes.
* When you use additional components, a mapping exists between each 3D point and an attribute and the different operations performed on the point cloud conserve this mapping.
* To add components to an [`EPointCloud`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html), use the methods [`EPointCloud::FillAttributeBuffer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#a4d6278fee0930784df2ae415ab13e285) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#ac540c0552664fae5966352b4aa6dacc1) or [`EPointCloud::AddCustomAttributeBuffer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#ad72d01ea31f58089060327b1539e0d88) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#aafdb541010f2a3b1d10e9ee6652706e8).
  * To retrieve the attribute, use the method [`EPointCloud::GetAttributeBuffer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#a241a852cc7493d7ed317e945a7519dd3) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#ae633bf81af0650fdd084c632b38223fc) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html#ac64ad48d9a4bbcb95cd494e40fe08df1).
  * To allocate the attribute, use the methods [`EPointCloud::AllocateAttributeBuffer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#a30ac2047c199e308a8ee9da6e4b5834b) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#a7524c3fe39548c6c07cd8418f5b5512a) and [`EPointCloud::AllocateCustomAttributeBuffer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#a8859e252ebcb6ef07a53e6138c656e08) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#adbdb4a532b96eff7d49638a628ff3646).
  * You can also save and load these attributes from point cloud files.
  * Several file formats are supported, but we recommend the formats [`PCD`](http://pointclouds.org/documentation/tutorials/pcd_file_format.html) and [`PLY`](http://paulbourke.net/dataformats/ply/) for handling additional components.

These methods are showcased in the following code snippets:

* [“Add an Attribute to an EPointCloud with Initial Data”](../../../resources/code_snippets/3d_processing/easy3d/addattributetocloud1.html.md)
* [“Add an Attribute to an EPointCloud without Initial Data”](../../../resources/code_snippets/3d_processing/easy3d/addattributetocloud2.html.md)
* [“Retrieve an Attribute from an EPointCloud”](../../../resources/code_snippets/3d_processing/easy3d/retrieveattributefromcloud.html.md)

<a id="e3d-pc-normals"></a>

## Normals and Curvatures

Use the function [`EPointCloud::ComputeNormalsAndCurvatures`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html#ae093fb751052321629c18ddd49867d7b) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html#a3185c9232a518e9c4f6baf23139a3153) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html#a75380d5161e6c4768bf36926f318c9a0) to compute the normals and, optionally, the curvatures of all of the points in the point cloud:

* The normals are stored in the `Normal` attribute buffer.
* The curvatures are stored in the `Curvature` attribute buffer.

<a id="e3d-pc-transforms"></a>

## Coordinate Transformations

Transforms allow you to reposition the point cloud inside the 3D space.

**eVision** provides you with the following basic transformations:

* Rotation around the X, Y or Z axis
* Translation along the X, Y and/or Z axis
* Scaling, around the origin, and either isotropic (the same in all directions) or anisotropic (different along the different axis)

It also provides you with projection transformations, both orthographic and perspective:

* An orthographic projection transforms a volume of space in the shape of a rectangular parallelepiped (and the points it contains) into the canonical view (a cubic space of size 2 and centered on the origin).

![image](assets/images/easy3d/affine_transforms.webp)
* A perspective projection transforms a volume of space in the shape of a frustum (basically a truncated pyramid) into the canonical view. This projection allows you to simulate the perspective effect given by an eye or a camera.
* Use the class [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) to create the geometric transformations. The class [`EAffineTransformer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EAffineTransformer.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EAffineTransformer.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAffineTransformer.html) applies a transformation defined by a [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) to a point cloud.

<a id="e3d-pc-reducing"></a>

## Reducing a Point Cloud

<a id="e3d-pc-cropping"></a>

### Cropping

Cropping allows you to exclude points from the point cloud based on geometrical considerations.

**eVision** provides the following cropping functions:

* [`ESimpleCropper`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ESimpleCropper.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ESimpleCropper.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ESimpleCropper.html): simple cropping on the X, Y and/or Z coordinates (aligned rectangle 3D region)
* [`ERectangularCropper`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ERectangularCropper.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ERectangularCropper.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ERectangularCropper.html): cropping the points outside (or inside) an oriented rectangular parallelepiped
* [`ESphericalCropper`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ESphericalCropper.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ESphericalCropper.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ESphericalCropper.html): cropping the points outside (or inside) a sphere.
* [`EPlaneCropper`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneCropper.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneCropper.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneCropper.html): cropping the points depending on their position with respect to a plane

These classes produce a new point cloud with the selected points.

<a id="e3d-pc-decimation"></a>

### Decimation

* **eVision** offers 2 capabilities to decimate an [`EPointCloud`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html):
  * [`ERandomDecimator`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ERandomDecimator.html) decimates the cloud by copying a specified number of points, randomly selected, to a new point cloud.
  * [`EGridDecimator`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EGridDecimator.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EGridDecimator.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EGridDecimator.html) splits the space using a grid of given size; the new cloud is created by averaging the points of every grid together, resulting in a regularly sampled cloud.
* To use [`ERandomDecimator`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ERandomDecimator.html), specify the number of points to keep as a parameter of the constructor.

  ### C++

  ```cpp
  #include <eVision/Easy3D/ERandomDecimator.h>
  ```

  ```cpp
  using namespace eVision::Easy3D;

  EPointCloud pc;
  pc.Load("");

  // Explicitly decimate the point cloud to keep 5000 points
  ERandomDecimator decimator(5000);
  EPointCloud pcDecimated;

  decimator.Decimate(pc, pcDecimated);
  ```

  ### C#

  ```cs
  using eVision.Easy3D;
  ```

  ```cs
  EPointCloud pc = new EPointCloud();
  pc.Load("");

  // Explicitly decimate the point cloud to keep 5000 points
  ERandomDecimator decimator = new ERandomDecimator(5000);
  EPointCloud pcDecimated = new EPointCloud();

  decimator.Decimate(pc, pcDecimated);
  ```

  ### Python

  ```py
  from eVision import Easy3D
  pc = Easy3D.EPointCloud()
  pc.Load("")

  # Explicitly decimate the point cloud to keep 5000 points
  decimator = Easy3D.ERandomDecimator(5000)
  pc_decimated = Easy3D.EPointCloud()

  decimator.Decimate(pc, pc_decimated)
  ```
* To use [`EGridDecimator`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EGridDecimator.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EGridDecimator.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EGridDecimator.html), specify the cell size, either as a [`E3DPoint`](../../../doxygen_cpp/structeVision_1_1Easy3D_1_1E3DPoint.html) [<sub>.NET</sub>](../../../doxygen_cs/structeVision_1_1Easy3D_1_1E3DPoint.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPoint.html) or as a float if the cell is cubic.

  ### C++

  ```cpp
  #include <eVision/Easy3D/EGridDecimator.h>
  ```

  ```cpp
  using namespace eVision;
  using namespace eVision::Easy3D;
  EPointCloud pc;
  pc.Load("");

  // Explicitly decimate the point cloud to keep
  // one point in every cube of 10*10*10
  EGridDecimator decimator(10.f);
  EPointCloud pcDecimated;

  decimator.Decimate(pc, pcDecimated);
  ```

  ### C#

  ```cs
  using eVision.Easy3D;
  ```

  ```cs
  EPointCloud pc = new EPointCloud();
  pc.Load("");

  // Explicitly decimate the point cloud to keep
  // one point in every cube of 10*10*10
  EGridDecimator decimator = new EGridDecimator(10.0F);
  EPointCloud pcDecimated = new EPointCloud();

  decimator.Decimate(pc, pcDecimated);
  pcDecimated.SavePCD("");
  ```

  ### Python

  ```py
  from eVision import Easy3D
  pc = Easy3D.EPointCloud()
  pc.Load("")

  # Explicitly decimate the point cloud to keep
  # one point in every cube of 10*10*10
  decimator = Easy3D.EGridDecimator(10.)
  pc_decimated = Easy3D.EPointCloud()

  decimator.Decimate(pc, pc_decimated)
  pc_decimated.SavePCD("")
  ```

<a id="e3d-pc-filtering"></a>

### Filtering Out Noise

The [`EPointCloudFilter`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloudFilter.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloudFilter.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloudFilter.html) class filters a point cloud in the same way [`EFilters::RemoveNoise`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFilters.html#aee51ea6faafd409244b7b6ecf92e2558) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFilters.html#a24a5f2f4441398b576d0e6f1df1f3c61) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFilters.html#aec317239bb99033ac48069d0381670ee) filters a depth map or ZMap.

A criterion based on the neighborhood of each point is computed. The points that have a criterion score above the threshold are removed.

* Select between the following criterions:
  * The average distance of the point from its neighborhood. It may remove some points at the borders of a noiseless surface.
  * The standard derivation within the point neighborhood. It is more likely to remove noiseless points that are close to noisy points.
* You can set the size of the neighborhood.
* The threshold is defined as:
  $\overline{\text{criterion over the cloud}} + \sigma(\text{criterion over the cloud}) \times \text{factor provided by the user}$
  * The default value for the factor is 1, as it is a good value in most cases.
* You can replace the noisy points by the average of their neighborhood instead of removing them.

<a id="e3d-pc-planes"></a>

## Managing Planes

<a id="e3d-pc-e3dplane"></a>

### E3DPlane

A plane can be represented as an [`E3DPlane`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPlane.html) object.

This plane is characterized by:

* Its normal which is a vector of norm 1, perpendicular to the plane.
* Its signed distance from the origin, which is the smallest distance from the origin to the plane. The signed distance is positive when the vector binding the origin to the closest point on the plane has the same direction as the normal and is negative when it has the opposite direction.

![image](assets/images/easy3d/plane_finder_01.webp)

Once a plane is defined, you can measure the signed distance between this plane and any point in the space using the method [`E3DPoint::DistanceTo`](../../../doxygen_cpp/structeVision_1_1Easy3D_1_1E3DPoint.html#af92120de9620917814cc4f05877e215b) [<sub>.NET</sub>](../../../doxygen_cs/structeVision_1_1Easy3D_1_1E3DPoint.html#a5ac835dbb23e0b255ad12f97960a5ff7) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPoint.html#ae1c2f6e819089f6adc80ef388a893e59).

* A positive distance means that the vector connecting the plane to the point has the same direction as the normal.
* A negative distance means that the vector has the opposite direction.

![image](assets/images/easy3d/plane_finder_02.webp)

<a id="e3d-plane-finder"></a>

### EPlaneFinder

You can search for a plane in a point cloud using the object [`EPlaneFinder`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFinder.html) object.

The main parameters of this object are:

* The maximum distance between the searched plane and a point that belongs to this plane.
* The expected ratio between the numbers of inliers and the total number of points in the point cloud.
  * An **inlier** is a point that belongs to a plane (closer than this maximum distance).
  * An **outlier** is a point that is not an inlier.

The picture below illustrates how points of the space are classified as inliers (in green) and outliers (in red) according to their distance to the searched plane.

![image](assets/images/easy3d/plane_finder_03.webp)
* An [`EPlaneFinder`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFinder.html) object produces a [`E3DPlane`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPlane.html) object.
  * The algorithm searches for a plane containing as many inliers as possible.
  * This plane is the biggest plane if the samples are evenly distributed.
* The maximum distance between the plane and the inliers is a mandatory parameter.
  * It should include the deviation due to the noise.
  * It should also take the warpage into account.
* The parameter that specifies the ratio of inliers with respect to the total number of points has a default value of 0.3. This means that we estimate that about 30% of the points belong to the plane.
  * This parameter is not as critical as the maximum distance.
  * It affects the maximum time that the algorithm spends to search a plane and its robustness.
* For more fine-grained control, you can specify the ratio of inliers as a range.
  * The min of the range is the minimum ratio of inliers for a plane to be considered as valid.
  * The algorithm stops searching for a plane when it finds one with the max of the range inliers.
  * The bigger the min of the range and the smaller the max, the faster the algorithm is.
  * Specifyng the range as a single value $x$ is equivalent to setting a range of $[x/2, x]$.
* You can specify the expected normal vector to the searched plane.
  * Specify also an angular tolerance with respect to this direction.
  * The algorithm only searches for a plane that satisfies the condition.
  * Setting this condition may speed up the plane search.

![image](assets/images/easy3d/plane_finder_04.webp)
* You can specify 1 or 2 points contained in the searched plane.
  * These points are not specified as inliers (points closer to the plane than its maximum distance) but as points exactly contained in the main plane.
  * They may not be exactly in the final plane after the final fitting step detailed below.
  * The algorithm only searches for a plane that satisfies the condition.
  * Setting this condition may speed up the plane search.

Once the main plane is found, a fit is done on all the inliers points and the result is returned (see [`EPlaneFitter`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFitter.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFitter.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFitter.html) below).

<a id="e3d-plane-finder-decimation"></a>

#### Decimation

By default, the [`EPlaneFinder`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFinder.html) decimates the input point cloud to accelerate the search.

* The default decimator reduces the input point cloud to 10,000 points.
* You can disable this decimation.
* You can change the number of points the cloud is reduced to.
* You can decimate a point cloud explicitly.
  * Use an [`ERandomDecimator`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ERandomDecimator.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ERandomDecimator.html) object.
  * Use the decimated point cloud as input for the [`EPlaneFinder`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFinder.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFinder.html).
  * Disable the default decimator.

<a id="e3d-plane-fitter"></a>

### EPlaneFitter

The [`EPlaneFitter`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPlaneFitter.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPlaneFitter.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPlaneFitter.html) operator computes a fit on all the points of a point cloud and returns a [`E3DPlane`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DPlane.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPlane.html) object. The “average” plane, that minimizes the orthogonal distance of the points to the plane, is returned.

<a id="e3d-pc-aligning"></a>

## Aligning

<a id="e3d-principal-axis"></a>

### EPrincipalAxisExtractor

The [`EPrincipalAxisExtractor`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) computes the “principal axis” of an object from a point cloud ([`EPointCloud`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html)) and returns a [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) containing a solid transformation that defines a new orthogonal basis.

This new orthogonal basis has the following characteristics:

* The center is the center of gravity of the point cloud.
* The axes are oriented along the “principal axis” of the object. This is the result of the “PCA” calculation (principal axis analysis).
* The directions of the axes are selected so that the new basis is as close as possible of the basis defined by the reference transformation.

The next figure illustrates the orientation of the principal axes of an object.

![image](assets/images/easy3d/point_cloud_01.webp)

The principal axes extraction is done using the [`EPrincipalAxisExtractor::Extract`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html#a7a7cd31dfff42bd1d0f3d4121ad13596) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html#a8ba2f3bbe2ff66cafb61c9cd54b623ee) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html#a3b40c4b7301a564ca4291ca6558fe97a) method that takes a [`EPointCloud`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloud.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloud.html) as input and returns an [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html). Optionally, you can pass 3 other output parameters by reference to retrieve the value of the standard deviation along the 3 principal axes.

You can use the returned [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) object to transform the 3D coordinates of a point. For example, apply the transformation matrix to the origin $(0, 0, 0)$ to return the center of gravity of the object.

<a id="e3d-reference-transformation"></a>

#### Specification of a Reference Transformation

The reference transformation is an optional parameter of the [`EPrincipalAxisExtractor`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EPrincipalAxisExtractor.html) object. It defines a reference basis used to select an orthogonal basis out of the principal axes. The selected basis will be the closest to the reference basis.

#### HINT
If no reference transformation was supplied, the default reference basis is $((1, 0, 0), (0, 1, 0), (0, 0, 1))$, that corresponds to the identity transformation. On the figure below, the default reference basis determines the direction of the axes $e_x$, $e_y$ and $e_z$.

![image](assets/images/easy3d/point_cloud_02.webp)

<a id="e3d-features-aligner"></a>

### EFeaturesAligner

A [`EFeaturesAligner`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) object finds the best transformation that maps a list of points to another list of points.

* The first list of points is called the “model”. It is stored in the object [`EFeaturesAligner`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFeaturesAligner.html).
* The second list of points is called “measured points”. It is passed as a parameter to the [`EFeaturesAligner::Compute`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#ad62d8fefa9aed20f4b614a94c596e589) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#aefc8daf7cb7e5251c0c301b5fa455ae1) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#a4a145476ab8703b75339aa0e17f2da5d) method. If successful, the result of this method is a [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) object.
* By default, the 2 lists should form matching pairs. In other words, the first point of the first list matches the first point of the second list, the second point of the first list matches the second point of the second list, and so on…
  * To align unordered points, set the parameter [`EAlignmentType`](../../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad3948c111a90d5cda002238ad65916e2) [<sub>.NET</sub>](../../../doxygen_cs/namespaceeVision_1_1Easy3D.html#a996aa3a0564239428bfe057eb8914949) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAlignmentType.html) to [`EAlignmentType_Unordered`](../../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad3948c111a90d5cda002238ad65916e2) [<sub>.NET</sub>](../../../doxygen_cs/namespaceeVision_1_1Easy3D.html#a996aa3a0564239428bfe057eb8914949) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAlignmentType.html).
  * The [`EFeaturesAligner`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFeaturesAligner.html) is slower with unordered points but it should be fast enough until around 10 points.
* In unordered mode, you can add some constraints on the transformation to obtain. Let `(ex, ey, ez)` be the orthogonal basis (see [`E3DTransformMatrix::GetOrthoBasis`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#aa1457a49a33196ed8bfe948fea152cc3) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#ada87063c82db08864fa8bd44e674149b) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#a6b8959ddad60fd818a83459d401dd8ec)) of the transformation, you can specify the allowed range for the angle between `ex` and the x-axis with the method [`EFeaturesAligner::SetXAngleRange`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#acd56a29743642cc9f52b8babc9daa579) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#a38c6bd26b6b83b99ead2db9bd4e6f0ca) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EFeaturesAligner.html#af104ee5ef2ce3a1f3f30f98c94778689) (equivalent methods exist for y and z).
  * This allows to constrain the angle between the corresponding axes of the model and measured referentials.
  * These methods are useful when several possible transformations between the points exist because of symmetries.

With the [`EParticleDetectionParameters::Polarity`](../../../doxygen_cpp/structeVision_1_1EParticleDetectionParameters.html#ad58570994c51001447c142357a467914) [<sub>.NET</sub>](../../../doxygen_cs/structeVision_1_1EParticleDetectionParameters.html#a66ccff611c4dcf5831d3508419f987d6) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EParticleDetectionParameters.html#aa3b9295677f5de0187cfba76787021aa) parameter, you can define which transformation is returned. It can be either:

* The one that moves one point from the first list (the model) to the second list of points (the measured points) if the polarity parameter is set to [`EAlignmentPolarity_ModelToMeasured`](../../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#a5a7e3a3c7d2552cb713d791a312ec00e) [<sub>.NET</sub>](../../../doxygen_cs/namespaceeVision_1_1Easy3D.html#a569bb44c14a760ddc2b98a3205764184) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAlignmentPolarity.html) (default).
* The one that moves a point from the second list (the measured points) to the first list (the model) if the polarity parameter is set to [`EAlignmentPolarity_MeasuredToModel`](../../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#a5a7e3a3c7d2552cb713d791a312ec00e) [<sub>.NET</sub>](../../../doxygen_cs/namespaceeVision_1_1Easy3D.html#a569bb44c14a760ddc2b98a3205764184) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAlignmentPolarity.html).

The figure below illustrates the computation of the alignment transformation. In this example a model is aligned to an object using the coordinates of their corners.

![image](assets/images/easy3d/point_cloud_03.webp)

Once the transformation is computed, use the method [`E3DTransformMatrix::GetOrthoBasis`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#aa1457a49a33196ed8bfe948fea152cc3) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#ada87063c82db08864fa8bd44e674149b) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html#a6b8959ddad60fd818a83459d401dd8ec) of the [`E3DTransformMatrix`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DTransformMatrix.html) object to get the basis $(e_x, e_y, e_z)$ and the center point $t$ that defines the new basis.

#### HINT
If you need to transform a point cloud or a list of points, it is more efficient to use the [`EAffineTransformer::ApplyTransform`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EAffineTransformer.html#af2077142d051b3a44aab590a42804413) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EAffineTransformer.html#a8b170d6daf9590ed6f1348307c5e6f54) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAffineTransformer.html#ac8ab246cfbcff9adcad8553242254115) method of an [`EAffineTransformer`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1EAffineTransformer.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1EAffineTransformer.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1EAffineTransformer.html) object.

<a id="e3d-pc-spheres"></a>

## Using Spheres

<a id="e3d-pc-e3dsphere"></a>

### E3DSphere

Use an object [`E3DSphere`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DSphere.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DSphere.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DSphere.html) to represent a sphere characterized by:

* Its center as a [`E3DPoint`](../../../doxygen_cpp/structeVision_1_1Easy3D_1_1E3DPoint.html) [<sub>.NET</sub>](../../../doxygen_cs/structeVision_1_1Easy3D_1_1E3DPoint.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPoint.html).
* Its radius.

Once a sphere is defined, use the method [`E3DPoint::DistanceTo`](../../../doxygen_cpp/structeVision_1_1Easy3D_1_1E3DPoint.html#af92120de9620917814cc4f05877e215b) [<sub>.NET</sub>](../../../doxygen_cs/structeVision_1_1Easy3D_1_1E3DPoint.html#a5ac835dbb23e0b255ad12f97960a5ff7) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DPoint.html#ae1c2f6e819089f6adc80ef388a893e59) to measure the distance between the sphere and any point in the space, defined as the distance to the nearest point of the sphere.

<a id="e3d-sphere-fitter"></a>

### ESphereFitter

The operator [`ESphereFitter`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1ESphereFitter.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1ESphereFitter.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1ESphereFitter.html) computes a fit on all the points of a point cloud and returns the object [`E3DSphere`](../../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DSphere.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DSphere.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1Easy3D_1_1E3DSphere.html) that minimizes the distance between the point cloud and the sphere (calculated according to the least squares).
