<a id="e3d-match"></a>

# Easy3DMatch - 3D Alignment and Comparison

**Easy3DMatch** allows you to:

* Align a scanned object with another scan or with a reference mesh.
  * The **Easy3DMatch** tool features alignment functions to find the exact pose (position and orientation) of acquired 3D objects using a reference model.
  * You can specify this model as a reference point cloud or as a 3D mesh from CAD software (using the `stl` file format).

  ![CAD model.](assets/images/easy3d/easy3dmatch/purpose_1.webp)![Sample point cloud.](assets/images/easy3d/easy3dmatch/purpose_2.webp)![Model and sample aligned.](assets/images/easy3d/easy3dmatch/purpose_3.webp)
* Compare an aligned scan with a reference model or mesh:
  1. Compute the local distances between 3D scans and a golden sample or a reference mesh.
  2. Detect anomalies such as misplaced features, geometric distortions, gaps and bumps.

  ![Reference.](assets/images/easy3d/easy3dmatch/purpose_4.webp)![Sample.](assets/images/easy3d/easy3dmatch/purpose_5.webp)![image](assets/images/easy3d/easy3dmatch/purpose_6.webp)

<a id="e3d-match-workflow"></a>

## Workflow

1. Load a reference as:
   * A mesh (define its viewpoints)
   * A point cloud (with one viewpoint)
   * A ZMap
2. Load a sample, either as:
   * A point cloud (with one viewpoint)
   * A ZMap
3. Perform alignment and/or matching:
   * Optionally, align the sample and get its [`E3DAlignment`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAlignment.html) with respect to the reference ([`E3DAligner`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html)).
   * Optionally, compare the sample to the reference by defining ROIs as [`E3DBox`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DBox.html) ([`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html)).
   * Align the sample and compare it directly to the reference by defining ROIs as an [`ERegion`](../../doxygen_cpp/classeVision_1_1ERegion.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1ERegion.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1ERegion.html) ([`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html)).
4. Use the transformation from sample to model to locate the sample and/or process the detected [`E3DAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAnomaly.html).

<a id="e3d-photometric-stereo-alignment"></a>

## Alignment

<a id="e3d-align-base-case"></a>

### Base Case

Use the class [`E3DAligner`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html) to load a reference and a sample and to find the transformation from the sample to the reference.

1. The first step is to set the reference using the method `SetReference`.
   * In addition to the mesh or the cloud, this method also takes one or several `E3DPlane` or azimuth and elevation angles (see [Calibration](easy3d/photometric_stereo.html.md#e3d-photometric-stereo-calibration) for a definition of the azimuth and the elevation).
   * The angles are used to compute the plane and are just an easier way to specify it.
2. The goal of the plane is to specify the face(s) of the object that can be visible on the sample.
   * You must do this only once for the reference and once for the sample(s), assuming they are all taken with the same scanner.

Here is an illustration of the process:

![image](assets/images/easy3d/easy3dmatch/alignment_1.webp)![image](assets/images/easy3d/easy3dmatch/alignment_2.webp)![image](assets/images/easy3d/easy3dmatch/alignment_3.webp)
1. Call the method [`E3DAligner::Align`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html#af015977321df490091c5c3fbc2e63f42) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html#a8829392d4bff68287087509297452aec) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html#a21392309cf9ac896e8aa33b06799a90e) with a point cloud or a zmap and get an object [`E3DAlignment`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAlignment.html) that contains:
   * The pose, 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) mapping the sample to the reference.
   * The error, indicating the quality of the matching.
   * The index of the reference pose that was matched. This is useful when several poses are defined (in the example above, there are 2 poses defined).

<a id="e3d-align-reprojection"></a>

### Defining a reprojection plane to improve the results

Ideally, the sample (and the point cloud or the ZMap used as reference) should be aligned on the viewpoint. This is however not always true, for example when the scanner does not lay on top of the object. In these cases, the user may specify the plane on which the object lays, either by giving 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) or a flat scan on which only the plane is visible.

In both cases, the plane normal must have the correct orientation (pointing upwards or downwards), that is if the plane is above the normal ($z_\text{object} > z_\text{plane}$), the $z$ coordinate of the normal should be positive. This is specified either directly in the [`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) or by a boolean argument `objectAbovePlane` when giving a flat scan.

#### SEE ALSO
Code snippets:

* [Basic alignment](../../resources/code_snippets/3d_processing/easy3dmatch/e3dalignerbasic.html.md)
* [Using a reprojection plane](../../resources/code_snippets/3d_processing/easy3dmatch/e3dalignerreprojplane.html.md)
* [Align a sample on a reference object](../../resources/code_snippets/3d_processing/easy3dmatch/e3dalignmentalignsample.html.md)

<a id="e3d-photometric-stereo-comparison"></a>

## Comparison

<a id="e3d-compare-base-case"></a>

### Base Case

Use the class [`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html) to load a reference and an already aligned sample (for example by using [`E3DAligner`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html)) and to find the distance map between both as well as anomalies.

* To use the [`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html):
  * Use the method [`E3DComparer::SetMeshReference`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a23c455b2cbc8bdb65da1b2fe39b8277e) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a6f00a95e629e67de0a335a9f7b2f9d4a) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a32e119b709ac788c3e050f2641390203) or [`E3DComparer::SetPointCloudReference`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a84685cd78067a4ff97ba4e0eff17b68a) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#ae346cc81ca46a61a36068cf58059cef9) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#ae2c102a382dd19610d7799e1158063d6) to specify the reference with either an [`EMesh`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EMesh.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EMesh.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EMesh.html) or 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).
  * Set the options of the comparison (ROIs, mode and thresholds).
  * Call the method [`E3DComparer::Compare`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a9049a2fff108b75ea91342d2a7091912) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#adb56956f4a707394b813962db1de12e0) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a884dc8f945dadd20ab7e42b8e2466e90) with the sample [`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 [`E3DComparer::ComputesAnomalies`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a81c62b1217cc896fba8979d37db1d0b5) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a32a38bc891d28be751b5e19e6bb75a25) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#af2ddd84eb4c98ecfe2d8f7f0fc473da7) to retrieve the list of [`E3DAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAnomaly.html).
  * Use [`E3DComparer::GetComparisonPointCloud`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a51d2fc3bfad39d0fd3a10a80ff98bde2) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a572e5d8673e10572244f2183cd1c69df) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#ab6dbb290bc7658ebcddc3893f4240690) to retrieve the [`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) containing the distances.
* An [`E3DAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAnomaly.html) represents a specific area in which the discrepancies between the sample and the reference are important. They are represented by:
  * 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) containing all the points of the anomaly and their distance to the sample.
  * The area of the anomaly.
  * Its center of gravity.
  * A bounding box around the anomaly.

#### NOTE
The [`E3DAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAnomaly.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAnomaly.html) represents points on the reference (except on [`E3DComparer::SetNoExtraMaterial`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#af499d028986e7c93d8007fa1e692fd00) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a692e2933c09c948454d47c854ceb54f2) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#ab2fbae330926cd5ed3b0a4bff8a41f4b) regions and when the distance mode is set to [`EComparisonDistanceMode_Euclidean_Advanced`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html) or [`EComparisonDistanceMode_Normals_Advanced`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html)).

* Use [`E3DComparer::GetComparisonPointCloud`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a51d2fc3bfad39d0fd3a10a80ff98bde2) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a572e5d8673e10572244f2183cd1c69df) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#ab6dbb290bc7658ebcddc3893f4240690) to retrieve 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) containing distance and/or colors that represent the distance to the sample or the reference for each of these points.
* `SetROI` and `SetDontCare`
  * The class [`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html) can perform the comparison on only a subset of the object. This has two benefits: it is faster and it allows to ignore false-positives when detecting anomalies.
    * Use [`E3DComparer::SetROI`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a9a0b638bb7206c61477350285f0d756b) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#abd5c404289639c80c223e127cc03b433) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#aef0a48ebc96808f7534a73ae5e00519f) with a vector of [`E3DBox`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DBox.html) to define the zones on which to perform a comparison.
    * Use [`E3DComparer::SetDontCare`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#aec051a4663f22c8e359b3dff5482e334) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a7b8dffe9e8ea0d50fc57c7f1b523f78b) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a9a0d9624a602fa5393b4387ceccb01ea) to specify areas that are excluded from the comparison.
    * A point belonging to [`E3DComparer::SetROI`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a9a0b638bb7206c61477350285f0d756b) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#abd5c404289639c80c223e127cc03b433) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#aef0a48ebc96808f7534a73ae5e00519f) and [`E3DComparer::SetDontCare`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#aec051a4663f22c8e359b3dff5482e334) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a7b8dffe9e8ea0d50fc57c7f1b523f78b) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a9a0d9624a602fa5393b4387ceccb01ea) boxes is not compared. By default, all points are compared.
* `SetNoExtraMaterial`
  * The class [`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html) checks, for each point of the reference (in the ROI), the distance to its nearest neighbor in the sample.
    * This avoids false positives, where we would use points of the sample that are not part of the object (the plane on which the object lays for example).
    * This allows to detect missing points on the sample.
    * A drawback of this approach is that extra material, that is points that should not be in the sample (for example, a hole that is filled) are not detected. You can solve this problem by specifying a list of [`E3DBox`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DBox.html) containing the areas that should not contain extra material.
* `SetAnomalyThresholds`
  * An anomaly is a sufficiently large contiguous area of points whose distance to the scan is above a threshold.
    * To specify the two thresholds (distance and area), use the method [`E3DComparer::SetAnomalyThresholds`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a4dd2d46c56cf7e1030dbe68defc1efd3) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a654e33f4d50a259cb3d1a41c5163b8e3) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a491044bb01e4cd643225c7e38586ec6d).
    * By default, these two thresholds are set relatively to the model size. To set the default values back, use the method [`E3DComparer::ResetAnomalyThresholds`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a3c99c91f127c6ff09609a218b9d466e2) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#af2a517943cbf7ef09c2f68bd9a24249a) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a839eab9f70aaea50d610f26c44380444).
    * As a more advanced anomaly detection method, use [`E3DComparer::SetAnomalyHysteresis`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#ad63bf409639015391e7544ceb936187d) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#ac528ae2b382d49d29f387973aad34408) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a3426f8a00935dfaba5e896a8d422fb78).

  ![image](assets/images/easy3d/easy3dmatch/threshold_1.webp)
* `SetAnomalyHysteresis`
  * You can use the method [`E3DComparer::SetAnomalyHysteresis`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#ad63bf409639015391e7544ceb936187d) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#ac528ae2b382d49d29f387973aad34408) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a3426f8a00935dfaba5e896a8d422fb78) that is a more specific anomaly detection method.
    * With this method, a cluster of points should have a large enough subset of its points with an even larger distance to the sample to be an anomaly.
    * This may be useful if you do not want to consider as an anomaly the points with a medium distance unless they are close to points with a high distance.

  ![image](assets/images/easy3d/easy3dmatch/threshold_2.webp)
* `SetComparisonDistanceMode`
  * By default, the aligned scan and the reference comparison is based on the euclidean distance between their corresponding points.
    * Use [`E3DComparer::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#acca82f00258bc1393264a27d34f662ca) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#aa385bb02c54ccb4cb05833e461c6a522) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a00aa323eeccb240dd782fa0cf0388116) with argument set to `EComparisonDistanceMode_Euclidean` to enable it.
  * Another possibility is to base the comparison on the angular distance between the corresponding normals of the scan and of the reference.
    * This works better to detect scratches.
    * Use [`E3DComparer::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#acca82f00258bc1393264a27d34f662ca) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#aa385bb02c54ccb4cb05833e461c6a522) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a00aa323eeccb240dd782fa0cf0388116) with argument set to `EComparisonDistanceMode_Normals` to enable it.
  * There is a variant that is more robust towards false positives near the edges.
    * Use [`E3DComparer::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#acca82f00258bc1393264a27d34f662ca) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#aa385bb02c54ccb4cb05833e461c6a522) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a00aa323eeccb240dd782fa0cf0388116) with argument set to `EComparisonDistanceMode_Normals_Advanced` to enable it.

  ![image](assets/images/easy3d/easy3dmatch/comparison_1.webp)![Scratch not visible using the euclidean distance mode.](assets/images/easy3d/easy3dmatch/comparison_2.webp)![Scratch and false positives on the edges using the normals distance mode.](assets/images/easy3d/easy3dmatch/comparison_3.webp)![Only scratch detected using the advanced normals distance mode.](assets/images/easy3d/easy3dmatch/comparison_4.webp)
* `SetEnableAutomaticEdgeCropping and SetEdgeCroppingParameters`
  * Sharp edges in the reference can lead to false positives, especially when using normals (see [`E3DComparer::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#acca82f00258bc1393264a27d34f662ca) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#aa385bb02c54ccb4cb05833e461c6a522) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a00aa323eeccb240dd782fa0cf0388116)). A solution is to remove them automatically:
    * Use [`E3DComparer::SetEnableAutomaticEdgeCropping`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#af268177d0bdf4463c0d7b0ad96f29e1e) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a66080f244c390017b38df1bdea892d7e) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a87632ac567b5222ffcf5d2ca6432ed73) with argument set to `true` to enable this.
    * Use [`E3DComparer::SetEdgeCroppingParameters`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#add23dcaa8531844639e86388d4aa077f) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a881f67f4d34e8ee5b767bcf9373f77e3) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a42cf7bf2177c73c621bc478d8f58e9ed) to adjust this function.
    * By default, the edge cropping is disabled.
* `SetAutomaticCropFactor`
  * By default, the sample clouds are automatically cropped around the reference plus a margin to avoid computing distance for points that are not on the object (for example, the plane on which the object lays).
  * The margin is obtained by multiplying the anomaly distance threshold by the automatic crop factor (set with [`E3DComparer::SetAutomaticCropFactor`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html#a5eca7d16b4082959bc6c4d0227910d95) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html#a6b47de4c99cda4019e6f64accf475b52) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html#a58b6520305c58a0d357c6f6b21cd0d3e)).
    * By default, the factor is 1.

#### NOTE
If not called explicitly, the first call to `Compare` automatically computes the internal data structures.

#### SEE ALSO
Code snippets:

* [Basic comparison](../../resources/code_snippets/3d_processing/easy3dmatch/e3dcomparerbasic.html.md)
* [Advanced comparison](../../resources/code_snippets/3d_processing/easy3dmatch/e3dcompareradvanced.html.md)

<a id="e3d-matching"></a>

## Matching (Alignment and Comparison)

<a id="e3d-match-base-case"></a>

### Base Case

Use the class [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) to load a reference and a sample and to align and compare them at the same time.

[`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) inherits from [`E3DAligner`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html) (see [Alignment]()) and implements an API close to the one of [`E3DComparer`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DComparer.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DComparer.html) (see [Comparison]()) with some extra capabilities due to the usage of the reference points of view used in [`E3DAligner`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html).

To use the [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html):

1. The first step is to set the reference using the method `SetReference`.
   * In addition to the mesh or the cloud, this method also takes one or several [`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) or azimuth and elevation angles (see [Calibration](easy3d/photometric_stereo.html.md#e3d-photometric-stereo-calibration)) for a definition of the azimuth and the elevation).
   * The angles are used to compute the plane and are just an easier way to specify it.
2. Set the options of the comparison (ROIs, thresholds, mode…) and optionally a reference plane.
3. Call the `Match` method with the sample point cloud and its reference plane.
   * This returns an [`E3DMatch`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatch.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatch.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatch.html) object containing the anomalies and the [`E3DAlignment`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAlignment.html) ([`E3DMatch`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatch.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatch.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatch.html) inherits from [`E3DAlignment`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAlignment.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAlignment.html))
4. Optionally, use [`E3DMatcher::GetComparisonPointCloud`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a0a16011e8146989473fd11bc0cd7c7e9) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a39fde45e1f124155cf0c50c694eaa4e3) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a241c4865711f5a47f080f788efa23a40) to retrieve 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) that contains the distances.

* You can perform all these steps interactively in the `Easy3DMatchMatch` sample.
* The main difference between [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) and `E3DComparer` is that the ROIs are defined using [`ERegion`](../../doxygen_cpp/classeVision_1_1ERegion.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1ERegion.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1ERegion.html) on the [`EZMap`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EZMap.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EZMap.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EZMap.html) corresponding to the projection of the reference on the given [`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).

This also allows for more advanced comparisons (see [`E3DMatcher::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a2327514373ed81f659e4a4ef4bd8d130) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#af71accd626edb3c922388b642bc182a8) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a71d40e303345e61f3baf49e1df514e27) and [`E3DMatcher::SetEnableMissingPointAsAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a607fb0a603e6495557416a8476b327c8) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a7c5fae2a51bfca8f016dd5f353c2814c) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a51534dcd91aa10de9ea0911e8730fca7)).

* `SetComparisonDistanceMode`

  Use [`E3DMatcher::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a2327514373ed81f659e4a4ef4bd8d130) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#af71accd626edb3c922388b642bc182a8) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a71d40e303345e61f3baf49e1df514e27) to select one of the methods available in the class [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) to compute the distances:
  * [`EComparisonDistanceMode_Euclidean_Fast`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html): the fastest method based on the euclidean distance between points. It is less precise on the edges.
  * [`EComparisonDistanceMode_Euclidean`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html): the default method. It is based on the euclidean distance between corresponding points.
  * [`EComparisonDistanceMode_Euclidean_Advanced`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html): the slowest method based on the euclidean distances. It penalizes more the bumps.
  * [`EComparisonDistanceMode_Normals`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html): the same as [`EComparisonDistanceMode_Euclidean`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html) except that the comparison is based on the angular distance between the points normals instead of the euclidean distance between the points. It works better to detect scratches.
  * [`EComparisonDistanceMode_Normals_Advanced`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html): a variant of [`EComparisonDistanceMode_Normals`](../../doxygen_cpp/namespaceeVision_1_1Easy3D.html#ad32b3ff208b7e4ebec6130157e8adbff) [<sub>.NET</sub>](../../doxygen_cs/namespaceeVision_1_1Easy3D.html#ae5093b82b351d9fbfad561a66d6c68ff) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EComparisonDistanceMode.html) that is more robust towards false positives near the edges.
* `SetEnableAutomaticEdgeCropping and SetEdgeCroppingParameters`
  * Sharp edges in the reference can lead to false positives, especially when using normals (see [`E3DMatcher::SetComparisonDistanceMode`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a2327514373ed81f659e4a4ef4bd8d130) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#af71accd626edb3c922388b642bc182a8) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a71d40e303345e61f3baf49e1df514e27)).
  * A solution is to remove them automatically:
    * Use [`E3DMatcher::SetEnableAutomaticEdgeCropping`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a273205d35870728cd9dc2b5b10823231) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#ace1cda83c7dcb0844b413ed9710ce411) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a58836ff525a4c096a8a874cc41949b47) with parameter set to `true` to enable this.
    * Use [`E3DMatcher::SetEdgeCroppingParameters`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a639afdbfd677ea0ce03006fb16aad02b) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#ac9089e55ae0d244ca2ab225b08d010e5) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a47b07db9c2839f054d348610fe36f78e) to adjust this function.
    * By default, the edge cropping is disabled.
* `SetAllComparisonROI`
  * The class [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) can perform the comparison on only a subset of the object. This has two benefits: it is faster and it allows to ignore false-positives when detecting anomalies.
    * Use [`E3DMatcher::SetAllComparisonROI`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a8c561c55a20e06cfb4cdb132f7d2e29d) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#ab7101c48c486af277a9a93b49e567f4b) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#aa554c9511bcf1835082ba04fae2cecb2) with one on several [`ERegion`](../../doxygen_cpp/classeVision_1_1ERegion.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1ERegion.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1ERegion.html) to define the zones on which to perform a comparison.
    * These [`ERegion`](../../doxygen_cpp/classeVision_1_1ERegion.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1ERegion.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1ERegion.html) should be interpreted as a masking part of the object on a projected view. Use [`E3DAligner::RetrieveReferencePoses`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DAligner.html#afb1a6290e0e79dcd9ca67f5ac5bc7d4a) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DAligner.html#aa206461f96072cdd4f0517306f49598d) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DAligner.html#ae52e85d292d7c3f2608c5ab90ddc15f2) to retrieve the corresponding view.

  ![image](assets/images/easy3d/easy3dmatch/alignment_and_comparison_1.webp)![image](assets/images/easy3d/easy3dmatch/alignment_and_comparison_2.webp)
* `SetAllComparisonNoExtraMaterial`
  * The class [`E3DMatcher`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html) checks, for each point of the reference (in the ROI), the distance to its nearest neighbor in the sample.
    * This avoids false positives, where we would use points of the sample that are not part of the object (the plane on which the object lays for example).
    * This allows to detect missing points on the sample.
    * A drawback of this approach is that extra material, that is points that should not be in the sample (for example, a hole that is filled) are not detected. You can solve this problem by specifying a list of [`E3DBox`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DBox.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DBox.html) containing the areas that should not contain extra material.

  ![image](assets/images/easy3d/easy3dmatch/alignment_and_comparison_4.webp)![image](assets/images/easy3d/easy3dmatch/alignment_and_comparison_3.webp)
* `SetEnableMissingPointAsAnomaly`
  * By default, the points missing on the scan are considered as defaults.
    * In some case this may lead to false positives (a shadow on the sample is not necessarily a default, just the absence of information).
    * In other cases these points should be taken into account (for example, a deep hole in the object can result in the absence of points instead of the presence of misplaced points).
    * Use the method [`E3DMatcher::SetEnableMissingPointAsAnomaly`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a607fb0a603e6495557416a8476b327c8) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a7c5fae2a51bfca8f016dd5f353c2814c) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a51534dcd91aa10de9ea0911e8730fca7) to select one of these behaviors.

  ![image](assets/images/easy3d/easy3dmatch/alignment_and_comparison_5.webp)
* `SetAnomalyThresholds`
  * An anomaly is a sufficiently large contiguous area of points whose distance to the scan is above a threshold.
    * To specify the two thresholds (distance and area), use the method [`E3DMatcher::SetAnomalyThresholds`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#ab4474a17920d84be3a3df648fcbcd3ed) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#aa187df4c9f256d25f4ed0a6c8cc1edef) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a32b6a00c4748baeaf5c57c3ea3df82f8).
    * By default, these two thresholds are set relatively to the model size. To set the default values back, use the method [`E3DMatcher::ResetAnomalyThresholds`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#aca072626956a452564a706a6ad5729ee) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#aae1b15740571de40f9f5fc7a07aab539) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#aa2d269a83d7669f0f55cb5ee2589211f).
    * As a more advanced anomaly detection method, use [`E3DMatcher::SetAnomalyHysteresis`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a3ed0d99cbb3890f8f1cace6b5348d87b) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a4f2305ba100ff221d874de6aa53a8fe6) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a573d94efd558a8140c6d0621324262d6).

  ![image](assets/images/easy3d/easy3dmatch/threshold_1.webp)
* `SetAnomalyHysteresis`
  * You can use the method [`E3DMatcher::SetAnomalyHysteresis`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a3ed0d99cbb3890f8f1cace6b5348d87b) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a4f2305ba100ff221d874de6aa53a8fe6) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a573d94efd558a8140c6d0621324262d6) that is a more specific anomaly detection method.
    * With this method, a cluster of points should have a large enough subset of its points with an even larger distance to the sample to be an anomaly.
    * This may be useful if you do not want to consider as an anomaly the points with a medium distance unless they are close to points with a high distance.

  ![image](assets/images/easy3d/easy3dmatch/threshold_2.webp)
* `SetEnableAutomaticDecimation`
  * By default, the clouds are automatically decimated in such a way that the decimation error does not impact the anomalies detection.
    * This has the benefit of speeding up processing.
    * If the resolution of your point clouds is small with respect to your distance threshold, calling [`E3DMatcher::SetEnableAutomaticDecimation`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#af44c0f278bf0cfe2adc92dfd918ae7f6) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#a3cc5c38b2432b9fda25a01105788a623) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a7058aff60d1c4644c6883b723bab975a) with parameter set to `false` could improve the speed as it will avoid useless decimation.
* `SetAutomaticCropFactor`
  * By default, the sample clouds are automatically cropped around the reference plus a margin to avoid computing distance for points that are not on the object (for example, the plane on which the object lays).
  * The margin is obtained by multiplying the anomaly distance threshold by the automatic crop factor (set with [`E3DMatcher::SetAutomaticCropFactor`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1E3DMatcher.html#a0f543540d7859079731b6c7255e15de2) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1E3DMatcher.html#ae146912a4612b780bdaf40e0dd467951) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1E3DMatcher.html#a8a129d6e16e715ee17286a4aa96772fb)).
    * By default, the factor is 1.

#### NOTE
If not called explicitly, the first call to `Compare` automatically computes the internal data structures.

#### SEE ALSO
Code snippets:

* [Basic matching](../../resources/code_snippets/3d_processing/easy3dmatch/e3dmatcherbasic.html.md)
* [Advanced matching](../../resources/code_snippets/3d_processing/easy3dmatch/e3dmatcheradvanced.html.md)

<a id="e3d-sensor-fusion"></a>

## 3D Sensor Fusion

3D sensor fusion is a technique to merge the output of different 3D sensors together. In this case, these are different views of the same object.

Use the class [`EPointCloudMerger`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloudMerger.html) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloudMerger.html) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloudMerger.html):

1. Choose how many sensors to use and position them.
2. Acquire scans of the calibration cube (that must be 3D printed).
3. Use the method [`EPointCloudMerger::Calibrate`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#abdd4006ec23091f0dca9e9f5071d6960) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a83158d3740fae583e46ae4fbe7403ee8) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a4c8e05a52875151e771cc2a960b904e7) to perform the calibration.
4. For each new object:
   1. Acquire the scans of the object.
   2. Use the method [`EPointCloudMerger::Merge`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a6739ce7f766e40a5854c4c79f2b8e5a8) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a2174bb528525e571cbf727ccf893967f) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a9952f6669acc7e0d58951f7939b20112) to merge the scans together.

#### HINT
You can perform all these steps interactively in the C++ and C# `Easy3DMatchPointCloudMerger` samples.

![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_1.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_2a.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_2b.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_3a.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_3b.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_4a.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_4b.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_5a.webp)![image](assets/images/easy3d/easy3dmatch/epointcloudmerger_5b.webp)

<a id="e3d-fusion-cad-model"></a>

### 3D CAD Model

* The calibration cube model is available in the STL file format.
* Download this file from the **eVision** download area in the `Additional Resources` section ([www.euresys.com/Support](https://www.euresys.com/en/Support/Download-area?Series=f97da39d-3c25-404c-aee7-73de1d1867fc)).

![image](assets/images/easy3d/download_files.webp)
* Once downloaded, we recommend using a specialized subcontractor to print the 3D calibration cube as 3D filament printing is not really suitable to produce the cube.

For reference, we worked with a company using Selective Laser Sintering and printing a 10$\times$10$\times$10 cm cube using PA12 material costed 220 €.

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

<a id="e3d-fusion-dedup"></a>

### Removing duplicate points

A physical point seen by two sensors should not be present two times in the output cloud.

* By default, these points are removed. As this takes most of the processing time, you can disable the process if speed is an issue.
* Set the cloud resolution with the parameter [`EPointCloudMerger::SetMergedCloudResolution`](../../doxygen_cpp/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a925e3e3c9680d47434ac93e36e2d3662) [<sub>.NET</sub>](../../doxygen_cs/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#ab53a1586352140885a662086c17e7c93) [<sub>Python</sub>](../../doxygen_python/classeVision_1_1Easy3D_1_1EPointCloudMerger.html#a47c9763ed5f8f30dac3fa9f1df40a088) to control this process. The parameter value is computed automatically but you can increase it to reduce the size of the output cloud and speed-up the processing.

#### SEE ALSO
Code snippets:

* [Merge point clouds](../../resources/code_snippets/3d_processing/easy3dmatch/e3dpointcloudmerger.html.md)
