Skip to content

Commit 1938abd

Browse files
committed
WIP-ADD signed toggle for cloud2cloudDistance
1 parent 7d26e50 commit 1938abd

File tree

4 files changed

+70
-122
lines changed

4 files changed

+70
-122
lines changed

src/gh/components/DF_cloud_to_cloud_distance/code.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,47 @@
2020
class CloudToCloudDistance(component):
2121
def RunScript(self,
2222
i_cloud_source: rg.PointCloud,
23-
i_cloud_target: rg.PointCloud):
23+
i_cloud_target: rg.PointCloud,
24+
i_signed_flag: bool):
2425
"""
2526
The cloud-to-cloud component computes the distance between each point in the source point cloud and its nearest neighbour in thr target point cloud.
2627
2728
:param i_cloud_source: source point cloud
2829
:param i_cloud_target: target point cloud to align to
30+
:param i_signed_flag: whether to take normals into account
2931
3032
:return o_distances : list of calculated distances for each point
3133
:return o_mse: the average squared difference between corresponding points of source and target
3234
:return o_max_deviation: the max deviation between source and target (Hausdorff Distance)
3335
:return o_min_deviation: the min deviation between source and target
36+
:return o_std_deviation: the standard deviation between source and target
3437
"""
3538
if i_cloud_source is None or i_cloud_target is None:
3639
ghenv.Component.AddRuntimeMessage(RML.Warning, "Please provide both objects of type point clouds to compare")
3740
return None
41+
42+
if i_signed_flag and not i_cloud_target.ContainsNormals:
43+
ghenv.Component.AddRuntimeMessage(RML.Warning, "Please provide a target point cloud with normals or assign false to i_signed_flag")
44+
return None
3845

3946
# conversion
4047
df_cloud_source = df_cvt_bindings.cvt_rhcloud_2_dfcloud(i_cloud_source)
4148
df_cloud_target = df_cvt_bindings.cvt_rhcloud_2_dfcloud(i_cloud_target)
4249

4350
# calculate distances
44-
o_distances = df_error_estimation.cloud_2_cloud_distance(df_cloud_source, df_cloud_target)
51+
o_distances = df_error_estimation.cloud_2_cloud_distance(df_cloud_source, df_cloud_target, i_signed_flag)
4552
o_mse = df_error_estimation.compute_mse(o_distances)
4653
o_max_deviation = df_error_estimation.compute_max_deviation(o_distances)
4754
o_min_deviation = df_error_estimation.compute_min_deviation(o_distances)
55+
o_std_deviation = df_error_estimation.compute_standard_deviation(o_distances)
4856

49-
return o_distances.tolist(), o_mse, o_max_deviation, o_min_deviation
57+
return o_distances.tolist(), o_mse, o_max_deviation, o_min_deviation, o_std_deviation
5058

5159

5260
if __name__ == "__main__":
5361
com = CloudToCloudDistance()
54-
o_distances, o_mse, o_max_deviation, o_min_deviation = com.RunScript(
62+
o_distances, o_mse, o_max_deviation, o_min_deviation, o_std_deviation = com.RunScript(
5563
i_cloud_source,
56-
i_cloud_target
64+
i_cloud_target,
65+
i_signed_flag
5766
)

src/gh/components/DF_cloud_to_cloud_distance/metadata.json

Lines changed: 30 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"name": "RANSACGlobalRegistration",
3-
"nickname": "RANSACGReg",
2+
"name": "CloudToCloudDistance",
3+
"nickname": "C2CDistance",
44
"category": "diffCheck",
5-
"subcategory": "Registrations",
6-
"description": "Register two point clouds together with a feature matching based on RANSAC.",
5+
"subcategory": "Analysis",
6+
"description": "Computes the distance between each point in the source point cloud and its nearest neighbour in thr target point cloud.",
77
"exposure": 4,
88
"instanceGuid": "805911b9-7ea9-4bbf-be34-00f9f225b9b3",
99
"ghpython": {
@@ -13,18 +13,6 @@
1313
"marshalOutGuids": true,
1414
"iconDisplay": 2,
1515
"inputParameters": [
16-
{
17-
"name": "i_recompute",
18-
"nickname": "i_recompute",
19-
"description": "Connect a button to recompute the registration.",
20-
"optional": true,
21-
"allowTreeAccess": true,
22-
"showTypeHints": true,
23-
"scriptParamAccess": "item",
24-
"wireDisplay": "default",
25-
"sourceCount": 0,
26-
"typeHintID": "bool"
27-
},
2816
{
2917
"name": "i_cloud_source",
3018
"nickname": "i_cloud_source",
@@ -50,119 +38,55 @@
5038
"typeHintID": "pointcloud"
5139
},
5240
{
53-
"name": "i_radius_kd_search",
54-
"nickname": "i_radius_kd_search",
55-
"description": "The radius used to search for neighbors in the KDTree.it is expressed relative to the point cloud size (0.01 means radiusKDTreeSearch = 1% of maxSize(pointCloud). It is used for the calculation of FPFHFeatures.",
56-
"optional": false,
57-
"allowTreeAccess": true,
58-
"showTypeHints": true,
59-
"scriptParamAccess": "item",
60-
"wireDisplay": "default",
61-
"sourceCount": 0,
62-
"typeHintID": "float"
63-
},
64-
{
65-
"name": "i_neighbours_kd_search",
66-
"nickname": "i_neighbours_kd_search",
67-
"description": "The maximum number of neighbors to search for in the KDTree. It is used for the calculation of FPFHFeatures. A higher value will result in heavier computation but potentially more precise.",
68-
"optional": false,
69-
"allowTreeAccess": true,
70-
"showTypeHints": true,
71-
"scriptParamAccess": "item",
72-
"wireDisplay": "default",
73-
"sourceCount": 0,
74-
"typeHintID": "int"
75-
},
76-
{
77-
"name": "i_max_corrspondence_dist",
78-
"nickname": "i_max_corrspondence_dist",
79-
"description": "The maximum distance between correspondences. A higher value will result in more correspondences, but potentially include wrong ones.",
41+
"name": "i_signed_flag",
42+
"nickname": "i_signed_flag",
43+
"description": "whether to take normals into account",
8044
"optional": false,
81-
"allowTreeAccess": true,
45+
"allowTreeAccess": false,
8246
"showTypeHints": true,
8347
"scriptParamAccess": "item",
8448
"wireDisplay": "default",
8549
"sourceCount": 0,
8650
"typeHintID": "float"
87-
},
88-
{
89-
"name": "is_t_estimate_pt2pt",
90-
"nickname": "is_t_estimate_pt2pt",
91-
"description": "If true it deforms the cloud to match. The transformation estimation method to use. By default, it uses a point to point transformation estimation. If true it will scale and deform the cloud.",
92-
"optional": false,
93-
"allowTreeAccess": true,
94-
"showTypeHints": true,
95-
"scriptParamAccess": "item",
96-
"wireDisplay": "default",
97-
"sourceCount": 0,
98-
"typeHintID": "bool"
99-
},
51+
}
52+
],
53+
"outputParameters": [
10054
{
101-
"name": "i_ransac_n",
102-
"nickname": "i_ransac_n",
103-
"description": "The number of points to sample in the source point cloud. A higher value can result in a more precise transformation, but will take more time to compute.",
55+
"name": "o_distance",
56+
"nickname": "o_distance",
57+
"description": "list of calculated distances for each point of the source.",
10458
"optional": false,
105-
"allowTreeAccess": true,
106-
"showTypeHints": true,
107-
"scriptParamAccess": "item",
108-
"wireDisplay": "default",
10959
"sourceCount": 0,
110-
"typeHintID": "int"
60+
"graft": false
11161
},
11262
{
113-
"name": "i_checker_dist",
114-
"nickname": "i_checker_dist",
115-
"description": "The maximum distance between correspondances in the FPFH space before testing a RanSaC model. It is exprimed in relative values (it is scaled by the size of the bounding box of the poinnt cloud).",
63+
"name": "o_mse",
64+
"nickname": "o_mse",
65+
"description": "average squared difference between source and target.",
11666
"optional": false,
117-
"allowTreeAccess": true,
118-
"showTypeHints": true,
119-
"scriptParamAccess": "item",
120-
"wireDisplay": "default",
12167
"sourceCount": 0,
122-
"typeHintID": "float"
68+
"graft": false
12369
},
12470
{
125-
"name": "i_similarity_threshold",
126-
"nickname": "i_similarity_threshold",
127-
"description": "The threshold for the ransac check based on edge length to consider a model as inlier. A higher value will be stricter, discarding more ransac models.",
71+
"name": "o_max_deviation",
72+
"nickname": "o_max_deviation",
73+
"description": "max deviation between source and target",
12874
"optional": false,
129-
"allowTreeAccess": true,
130-
"showTypeHints": true,
131-
"scriptParamAccess": "item",
132-
"wireDisplay": "default",
13375
"sourceCount": 0,
134-
"typeHintID": "float"
76+
"graft": false
13577
},
13678
{
137-
"name": "i_max_iterations",
138-
"nickname": "i_max_iterations",
139-
"description": "The maximum number of iterations to run the Ransac algorithm. A higher value will take more time to compute but increases the chances of finding a good transformation.",
79+
"name": "o_min_deviation",
80+
"nickname": "o_min_deviation",
81+
"description": "min deviation between source and target",
14082
"optional": false,
141-
"allowTreeAccess": true,
142-
"showTypeHints": true,
143-
"scriptParamAccess": "item",
144-
"wireDisplay": "default",
14583
"sourceCount": 0,
146-
"typeHintID": "int"
84+
"graft": false
14785
},
14886
{
149-
"name": "i_confidence_threshold",
150-
"nickname": "i_confidence_threshold",
151-
"description": "The threshold for the convergence criteria of the ransac models. A higher value will be stricter, discarding more ransac models.",
152-
"optional": false,
153-
"allowTreeAccess": true,
154-
"showTypeHints": true,
155-
"scriptParamAccess": "item",
156-
"wireDisplay": "default",
157-
"sourceCount": 0,
158-
"typeHintID": "float"
159-
}
160-
],
161-
"outputParameters": [
162-
{
163-
"name": "o_x_form",
164-
"nickname": "o_x_form",
165-
"description": "The computed transformation.",
87+
"name": "o_std_deviation",
88+
"nickname": "o_std_deviation",
89+
"description": "standard deviation between source and target.",
16690
"optional": false,
16791
"sourceCount": 0,
16892
"graft": false

src/gh/components/DF_cloud_to_mesh_distance/code.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
class CloudToMeshDistance(component):
1919
def RunScript(self,
2020
i_cloud_source: rg.PointCloud,
21-
i_mesh_target: rg.PointCloud):
21+
i_mesh_target: rg.PointCloud,
22+
i_signed_flag: bool):
2223
"""
2324
The cloud-to-cloud component computes the distance between each point in the source point cloud and its nearest neighbour in thr target point cloud.
2425
@@ -39,7 +40,7 @@ def RunScript(self,
3940
df_mesh_target = df_cvt_bindings.cvt_rhmesh_2_dfmesh(i_mesh_target)
4041

4142
# calculate distances
42-
o_distances = df_error_estimation.cloud_2_mesh_distance(df_cloud_source, df_mesh_target)
43+
o_distances = df_error_estimation.cloud_2_mesh_distance(df_cloud_source, df_mesh_target, i_signed_flag)
4344
o_mse = df_error_estimation.compute_mse(o_distances)
4445
o_max_deviation = df_error_estimation.compute_max_deviation(o_distances)
4546
o_min_deviation = df_error_estimation.compute_min_deviation(o_distances)
Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,57 @@
11
#! python3
22
"""
3-
This module contains the utility functions to convert the data between the
4-
Rhino, the basic diffCheck data structures and the diffCheck bindings.
3+
This module contains the utility functions to compute the difference between source and target
54
"""
65

76
import numpy as np
8-
from diffCheck import diffcheck_bindings
97

10-
def cloud_2_cloud_distance(source, target):
118

12-
# Convert pts to np array and calculate distances
13-
distances = np.linalg.norm(np.asarray(source.points) - np.asarray(target.points), axis=1)
9+
def cloud_2_cloud_distance(source, target, signed=False):
10+
"""
11+
Compute the Euclidean distance for every point of a source pcd to its closest point on a target pointcloud
12+
"""
13+
distances = np.full(len(source.points), np.inf)
14+
15+
for i in range(len(source.points)):
16+
17+
dists = np.linalg.norm(np.asarray(target.points) - np.asarray(source.points)[i], axis=1)
18+
distances[i] = np.min(dists)
19+
20+
# determine whether the point on the source cloud is in the same direction as the normal of the corresponding point on the target pcd
21+
if signed:
22+
closest_idx = np.argmin(dists)
23+
# direction from target to source
24+
direction = source.points[i] - target.points[closest_idx]
25+
distances[i] *= np.sign(np.dot(direction, target.normals[closest_idx]))
1426

1527
return distances
1628

29+
1730
def cloud_2_mesh_distance(source, target):
1831

1932
distances = np.ones(len(source.points), dtype=float)
2033

2134
return distances
2235

36+
2337
def compute_mse(distances):
2438
"""
2539
Calculate mean squared distance
2640
"""
27-
2841
mse = np.sqrt(np.mean(distances ** 2))
2942

3043
return mse
3144

45+
3246
def compute_max_deviation(distances):
3347
"""
3448
Calculate max deviation of distances
3549
"""
36-
3750
max_deviation = np.max(distances)
3851

3952
return max_deviation
4053

54+
4155
def compute_min_deviation(distances):
4256
"""
4357
Calculate min deviation of distances
@@ -47,11 +61,11 @@ def compute_min_deviation(distances):
4761

4862
return min_deviation
4963

64+
5065
def compute_standard_deviation(distances):
5166
"""
5267
Calculate standard deviation of distances
5368
"""
54-
5569
standard_deviation = np.std(distances)
5670

5771
return standard_deviation

0 commit comments

Comments
 (0)