Skip to content

Commit 3811b56

Browse files
feat: add ICP registration in CAD segmentation component and expose basic parameters
1 parent 00b7337 commit 3811b56

File tree

2 files changed

+78
-28
lines changed

2 files changed

+78
-28
lines changed

src/gh/components/DF_CAD_segmentator/code.py

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99

1010

1111
from diffCheck.diffcheck_bindings import dfb_segmentation
12-
from diffCheck.diffcheck_bindings import dfb_geometry, dfb_registrations
13-
# from diffCheck.diffCheck_bindings import dfb_registrations
12+
from diffCheck.diffcheck_bindings import dfb_geometry, dfb_registrations, dfb_transformation
1413

1514
from diffCheck import df_cvt_bindings
1615

@@ -22,7 +21,10 @@ def RunScript(self,
2221
i_assembly,
2322
i_angle_threshold: float,
2423
i_association_threshold: float,
25-
i_angle_association_threshold: float):
24+
i_angle_association_threshold: float,
25+
i_maximum_face_segment_distance: float,
26+
i_radius_normal_estimation: float,
27+
i_max_correspondence_distance_icp: float,):
2628

2729
if i_clouds is None or i_assembly is None:
2830
self.AddRuntimeMessage(RML.Warning, "Please provide a cloud and an assembly to segment.")
@@ -33,12 +35,17 @@ def RunScript(self,
3335
i_association_threshold = 0.1
3436
if i_angle_association_threshold is None:
3537
i_angle_association_threshold = 0.5
38+
if i_radius_normal_estimation is None:
39+
i_radius_normal_estimation = 0.01
3640
o_face_clusters = []
3741
df_clusters_temp = []
3842
df_clusters = []
3943
# we make a deepcopy of the input clouds
4044
df_clouds_copy = [df_cvt_bindings.cvt_rhcloud_2_dfcloud(cloud.Duplicate()) for cloud in i_clouds]
4145
df_clouds = [df_cvt_bindings.cvt_rhcloud_2_dfcloud(cloud.Duplicate()) for cloud in i_clouds]
46+
df_merged_cloud = dfb_geometry.DFPointCloud()
47+
for pc in df_clouds:
48+
df_merged_cloud.add_points(pc)
4249

4350
df_beams = i_assembly.beams
4451
df_asssociated_cluster_faces_per_beam = []
@@ -50,33 +57,32 @@ def RunScript(self,
5057
# different association depending on the type of beam
5158
df_asssociated_cluster_faces = dfb_segmentation.DFSegmentation.associate_clusters(
5259
is_roundwood=df_b.is_roundwood,
53-
discriminate_points=False,
60+
discriminate_points=True,
5461
reference_mesh=df_b_mesh_faces,
5562
unassociated_clusters=df_clouds_copy,
5663
angle_threshold=i_angle_threshold,
57-
association_threshold=i_association_threshold,
58-
angle_association_threshold=i_angle_association_threshold
64+
association_threshold=i_association_threshold ,
65+
angle_association_threshold=i_angle_association_threshold,
66+
maximum_face_segment_distance=i_maximum_face_segment_distance
5967
)
6068
df_asssociated_cluster_faces_per_beam.append(df_asssociated_cluster_faces)
6169

6270
for i, df_b in enumerate(df_beams):
63-
# o_face_clusters.append([])
6471
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
6572
df_b_mesh_faces = [df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_b_mesh_face) for rh_b_mesh_face in rh_b_mesh_faces]
6673

6774
dfb_segmentation.DFSegmentation.clean_unassociated_clusters(
6875
is_roundwood=df_b.is_roundwood,
69-
discriminate_points=False,
76+
discriminate_points=True,
7077
unassociated_clusters=df_clouds_copy,
7178
associated_clusters=[df_asssociated_cluster_faces_per_beam[i]],
7279
reference_mesh=[df_b_mesh_faces],
7380
angle_threshold=i_angle_threshold,
74-
association_threshold=i_association_threshold,
75-
angle_association_threshold=i_angle_association_threshold
81+
association_threshold=i_association_threshold ,
82+
angle_association_threshold=i_angle_association_threshold,
83+
maximum_face_segment_distance=i_maximum_face_segment_distance
7684
)
7785

78-
# o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces_per_beam[i]]
79-
8086
df_asssociated_cluster = dfb_geometry.DFPointCloud()
8187
for df_associated_face in df_asssociated_cluster_faces_per_beam[i]:
8288
df_asssociated_cluster.add_points(df_associated_face)
@@ -86,23 +92,36 @@ def RunScript(self,
8692
# Now with the df_clusters, we sample a point cloud on the beam of the assembly, and perform an ICP to align the beam mesh to the point cloud.
8793
# Then we re-compute the association on the scan with thigher thresholds to have a better segmentation.
8894
o_transforms = []
95+
rh_meshes = []
8996
for i, df_b in enumerate(df_beams):
9097
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
98+
for df_j_face in df_b.joint_faces:
99+
rh_b_mesh_faces.append(df_j_face.to_mesh())
91100
rh_mesh = Rhino.Geometry.Mesh()
92101
for rh_b_mesh_face in rh_b_mesh_faces:
93102
rh_mesh.Append(rh_b_mesh_face)
103+
94104
df_b_mesh = df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_mesh)
95-
df_sampled_cloud = df_b_mesh.sample_points_uniformly(10000)
96-
df_sampled_cloud.estimate_normals(use_cilantro_evaluator=True,
97-
knn = 10,
105+
df_sampled_cloud = df_b_mesh.sample_points_uniformly(1000)
106+
df_sampled_cloud.estimate_normals(use_cilantro_evaluator=False,
107+
search_radius = i_radius_normal_estimation,
98108
)
99-
print(df_clusters_temp[i].get_num_points())
100-
transform = dfb_registrations.DFRefinedRegistration.O3DGeneralizedICP(
101-
source=df_sampled_cloud,
102-
target=df_clusters_temp[i],
103-
max_correspondence_distance= 0.03
104-
109+
cluster_without_normals = df_clusters_temp[i]
110+
cluster_without_normals.estimate_normals(use_cilantro_evaluator=False,
111+
search_radius = i_radius_normal_estimation,
105112
)
113+
df_merged_cloud.remove_statistical_outliers(100, 1.5)
114+
self.AddRuntimeMessage(RML.Warning, f"size of pc: {cluster_without_normals.get_num_points()}")
115+
if cluster_without_normals.get_num_points() != 0:
116+
transform = dfb_registrations.DFRefinedRegistration.O3DICP(
117+
source=df_sampled_cloud,
118+
target=df_merged_cloud,
119+
max_correspondence_distance= i_max_correspondence_distance_icp,
120+
max_iteration = 1000
121+
122+
)
123+
else:
124+
transform = dfb_transformation.DFTransformation()
106125
df_xform = transform.transformation_matrix
107126
rh_xform = Rhino.Geometry.Transform()
108127
for i in range(4):
@@ -113,8 +132,12 @@ def RunScript(self,
113132
df_new_asssociated_cluster_faces_per_beam = []
114133
for i, df_b in enumerate(df_beams):
115134
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
116-
for rh_mesh in rh_b_mesh_faces:
117-
rh_mesh.Transform(o_transforms[i])
135+
rh_test_mesh = Rhino.Geometry.Mesh()
136+
for j in range(len(rh_b_mesh_faces)):
137+
sucess = rh_b_mesh_faces[j].Transform(o_transforms[i])
138+
if sucess:
139+
rh_test_mesh.Append(rh_b_mesh_faces[j])
140+
rh_meshes.append(rh_test_mesh)
118141
df_b_mesh_faces = [df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_b_mesh_face) for rh_b_mesh_face in rh_b_mesh_faces]
119142

120143
# different association depending on the type of beam
@@ -125,15 +148,16 @@ def RunScript(self,
125148
unassociated_clusters=df_clouds,
126149
angle_threshold=i_angle_threshold,
127150
association_threshold=i_association_threshold,
128-
angle_association_threshold=i_angle_association_threshold
151+
angle_association_threshold=i_angle_association_threshold,
152+
maximum_face_segment_distance=i_maximum_face_segment_distance
129153
)
130154
df_new_asssociated_cluster_faces_per_beam.append(df_new_asssociated_cluster_faces)
131155

132156
for i, df_b in enumerate(df_beams):
133157
o_face_clusters.append([])
134158
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
135-
for rh_mesh in rh_b_mesh_faces:
136-
rh_mesh.Transform(o_transforms[i])
159+
for j in range(len(rh_b_mesh_faces)):
160+
rh_b_mesh_faces[j].Transform(o_transforms[i])
137161
df_b_mesh_faces = [df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_b_mesh_face) for rh_b_mesh_face in rh_b_mesh_faces]
138162

139163
dfb_segmentation.DFSegmentation.clean_unassociated_clusters(
@@ -144,7 +168,8 @@ def RunScript(self,
144168
reference_mesh=[df_b_mesh_faces],
145169
angle_threshold=i_angle_threshold,
146170
association_threshold=i_association_threshold,
147-
angle_association_threshold=i_angle_association_threshold
171+
angle_association_threshold=i_angle_association_threshold,
172+
maximum_face_segment_distance=i_maximum_face_segment_distance
148173
)
149174

150175
o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_new_asssociated_cluster_faces_per_beam[i]]
@@ -155,6 +180,7 @@ def RunScript(self,
155180

156181
df_clusters.append(df_asssociated_cluster)
157182

183+
o_beam_intermediary_clouds = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_clusters_temp]
158184
o_beam_clouds = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_clusters]
159185

160186
for i, o_beam_cloud in enumerate(o_beam_clouds):
@@ -164,4 +190,4 @@ def RunScript(self,
164190

165191
o_face_clouds = th.list_to_tree(o_face_clusters)
166192

167-
return [o_beam_clouds, o_face_clouds, o_transforms]
193+
return [o_beam_clouds, o_face_clouds, o_transforms, o_beam_intermediary_clouds, rh_meshes]

src/gh/components/DF_CAD_segmentator/metadata.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@
7272
"wireDisplay": "default",
7373
"sourceCount": 0,
7474
"typeHintID": "float"
75+
},
76+
{
77+
"name": "i_radius_normal_estimation",
78+
"nickname": "i_radius_normal_estimation",
79+
"description": "The radius used for normal estimation. Default is 0.01",
80+
"optional": true,
81+
"allowTreeAccess": true,
82+
"showTypeHints": true,
83+
"scriptParamAccess": "item",
84+
"wireDisplay": "default",
85+
"sourceCount": 0,
86+
"typeHintID": "float"
87+
},
88+
{
89+
"name": "i_max_correspondence_distance_icp",
90+
"nickname": "i_max_correspondence_distance_icp",
91+
"description": "The maximum correspondence distance for the ICP algorithm. Default is 0.1",
92+
"optional": true,
93+
"allowTreeAccess": true,
94+
"showTypeHints": true,
95+
"scriptParamAccess": "item",
96+
"wireDisplay": "default",
97+
"sourceCount": 0,
98+
"typeHintID": "float"
7599
}
76100
],
77101
"outputParameters": [

0 commit comments

Comments
 (0)