Skip to content

Commit 68ebcd1

Browse files
authored
Merge branch 'main' into test_suite
2 parents 62e03e7 + bcd6802 commit 68ebcd1

File tree

9 files changed

+366
-7
lines changed

9 files changed

+366
-7
lines changed

src/diffCheck/geometry/DFMesh.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ namespace diffCheck::geometry
125125
double res = (area1 + area2 + area3 - referenceTriangleArea) / referenceTriangleArea;
126126

127127
// arbitrary value to avoid false positives (points that, when projected on the triangle, are in it, but that are actually located too far from the mesh to actually belong to it)
128-
double maxProjectionDistance = std::min({(v1 - v0).norm(), (v2 - v1).norm(), (v0 - v2).norm()});
128+
double maxProjectionDistance = std::min({(v1 - v0).norm(), (v2 - v1).norm(), (v0 - v2).norm()}) / 2;
129129

130130
if (std::abs(res) < associationThreshold && (projectedPoint - point).norm() < maxProjectionDistance)
131131
{

src/diffCheck/geometry/DFPointCloud.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,13 @@ namespace diffCheck::geometry
273273
this->Normals = cloud->Normals;
274274
}
275275

276+
void DFPointCloud::AddPoints(const DFPointCloud &pointCloud)
277+
{
278+
this->Points.insert(this->Points.end(), pointCloud.Points.begin(), pointCloud.Points.end());
279+
this->Colors.insert(this->Colors.end(), pointCloud.Colors.begin(), pointCloud.Colors.end());
280+
this->Normals.insert(this->Normals.end(), pointCloud.Normals.begin(), pointCloud.Normals.end());
281+
}
282+
276283
Eigen::Vector3d DFPointCloud::GetCenterPoint()
277284
{
278285
if (this->Points.size() == 0)

src/diffCheck/geometry/DFPointCloud.hh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ namespace diffCheck::geometry
156156
*/
157157
void LoadFromPLY(const std::string &path);
158158

159+
/**
160+
* @brief adds the points, colors and normals from another point cloud
161+
*
162+
* @param pointCloud the other point cloud
163+
*/
164+
void AddPoints(const DFPointCloud &pointCloud);
165+
159166
public: ///< Getters
160167
/// @brief Number of points in the point cloud
161168
int GetNumPoints() const { return this->Points.size(); }

src/diffCheckApp.cc

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,134 @@ int main()
1515
auto initTime = std::chrono::high_resolution_clock::now();
1616

1717
std::shared_ptr<diffCheck::geometry::DFPointCloud> pcdSrc = std::make_shared<diffCheck::geometry::DFPointCloud>();
18-
std::string pathTest = diffCheck::io::GetRoofQuarterPlyPath();
19-
std::cout << "Path to the test file: " << pathTest << std::endl;
20-
21-
pcdSrc->LoadFromPLY(diffCheck::io::GetRoofQuarterPlyPath());
2218

19+
std::vector<std::vector<std::shared_ptr<diffCheck::geometry::DFMesh>>> meshSrc = std::vector<std::vector<std::shared_ptr<diffCheck::geometry::DFMesh>>>();
20+
std::vector<std::shared_ptr<diffCheck::geometry::DFPointCloud>> segments;
21+
std::vector<std::string> meshPaths;
22+
23+
std::string meshesFolderPath = R"(C:\Users\localuser\Desktop\meshes_for_diffCheck\joints\)";
24+
25+
for (int i = 1; i <= 3; i++)
26+
{
27+
std::vector<std::shared_ptr<diffCheck::geometry::DFMesh>> fullJoint;
28+
for (int j = 1; j <= 3; j++)
29+
{
30+
std::string meshPath = meshesFolderPath + std::to_string(i) + "/" + std::to_string(j) + ".ply";
31+
std::shared_ptr<diffCheck::geometry::DFMesh> mesh = std::make_shared<diffCheck::geometry::DFMesh>();
32+
mesh->LoadFromPLY(meshPath);
33+
fullJoint.push_back(mesh);
34+
}
35+
meshSrc.push_back(fullJoint);
36+
}
37+
38+
std::string pathPcdSrc = R"(C:\Users\localuser\Desktop\meshes_for_diffCheck\joints\full_beam.ply)";
39+
40+
pcdSrc->LoadFromPLY(pathPcdSrc);
41+
42+
pcdSrc->EstimateNormals(false, 100);
43+
pcdSrc->VoxelDownsample(0.007);
44+
auto intermediateTime = std::chrono::high_resolution_clock::now();
45+
segments = diffCheck::segmentation::DFSegmentation::NormalBasedSegmentation(
46+
pcdSrc,
47+
15.0f,
48+
15,
49+
true,
50+
50,
51+
0.5f,
52+
false);
53+
std::cout << "number of segments:" << segments.size()<< std::endl;
54+
55+
std::vector<std::shared_ptr<diffCheck::geometry::DFPointCloud>> unifiedSegments;
56+
for (int i = 0; i < meshSrc.size(); i++)
57+
{
58+
std::shared_ptr<diffCheck::geometry::DFPointCloud> unifiedSegment = std::make_shared<diffCheck::geometry::DFPointCloud>();
59+
unifiedSegment = diffCheck::segmentation::DFSegmentation::AssociateClustersToMeshes(
60+
meshSrc[i],
61+
segments,
62+
.2,
63+
.05);
64+
unifiedSegments.push_back(unifiedSegment);
65+
}
66+
67+
diffCheck::segmentation::DFSegmentation::CleanUnassociatedClusters(segments,
68+
unifiedSegments,
69+
meshSrc,
70+
.2,
71+
.05);
72+
73+
// Perform a registration per joint
74+
for (int i = 0; i < meshSrc.size(); i++)
75+
{
76+
std::shared_ptr<diffCheck::geometry::DFPointCloud> referencePointCloud = std::make_shared<diffCheck::geometry::DFPointCloud>();
77+
for (auto jointFace : meshSrc[i])
78+
{
79+
std::shared_ptr<diffCheck::geometry::DFPointCloud> facePointCloud = jointFace->SampleCloudUniform(1000);
80+
referencePointCloud->Points.insert(referencePointCloud->Points.end(), facePointCloud->Points.begin(), facePointCloud->Points.end());
81+
}
82+
referencePointCloud->EstimateNormals(false, 100);
83+
84+
diffCheck::transformation::DFTransformation transformation = diffCheck::registrations::DFRefinedRegistration::O3DICP(
85+
unifiedSegments[i],
86+
referencePointCloud);
87+
88+
std::cout << "Transformation matrix:" << std::endl;
89+
std::cout << transformation.TransformationMatrix << std::endl;
90+
91+
diffCheck::visualizer::Visualizer deVisu = diffCheck::visualizer::Visualizer("DiffCheckApp", 1000, 800, 50, 50, false, true, false);
92+
for (int i = 0; i < segments.size(); i++)
93+
{
94+
segments[i]->ApplyTransformation(transformation);
95+
deVisu.AddPointCloud(segments[i]);
96+
}
97+
for (auto joint : meshSrc)
98+
{
99+
for (auto face : joint)
100+
{
101+
deVisu.AddMesh(face);
102+
}
103+
}
104+
deVisu.Run();
105+
}
106+
23107
diffCheck::visualizer::Visualizer vis(std::string("DiffCheckApp"), 1000, 800, 50, 50, false, true, false);
24-
vis.AddPointCloud(pcdSrc);
108+
for (auto segment : segments)
109+
{
110+
// colorize the segments with random colors
111+
double r = static_cast<double>(rand()) / RAND_MAX;
112+
double g = static_cast<double>(rand()) / RAND_MAX;
113+
double b = static_cast<double>(rand()) / RAND_MAX;
114+
115+
segment->Colors.clear();
116+
for (int i = 0; i < segment->Points.size(); i++)
117+
{
118+
segment->Colors.push_back(Eigen::Vector3d(0, 0, 0));
119+
}
120+
vis.AddPointCloud(segment);
121+
}
122+
for(auto joint : meshSrc)
123+
{
124+
for(auto mesh : joint){vis.AddMesh(mesh);}
125+
}
126+
127+
int numSegments = unifiedSegments.size();
128+
129+
for (int i = 0; i < numSegments; i++)
130+
{
131+
for (int j = 0; j < unifiedSegments[i]->Points.size(); j++)
132+
{
133+
unifiedSegments[i]->Colors.push_back(Eigen::Vector3d((double(numSegments) - double(i))/double(numSegments), 1, double(i) / double(numSegments)));
134+
}
135+
}
136+
for (auto seg : unifiedSegments)
137+
{
138+
vis.AddPointCloud(seg);
139+
}
140+
141+
auto endTime = std::chrono::high_resolution_clock::now();
142+
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - initTime);
143+
auto segmentationTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - intermediateTime);
144+
std::cout << "Total computation time:" << duration.count() << std::endl;
145+
std::cout << "Segmentation time:" << segmentationTime.count() << std::endl;
25146

26147
vis.Run();
27148

src/diffCheckBindings.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ PYBIND11_MODULE(diffcheck_bindings, m) {
4848
py::arg("r"), py::arg("g"), py::arg("b"))
4949

5050
.def("load_from_PLY", &diffCheck::geometry::DFPointCloud::LoadFromPLY)
51+
.def("add_points", &diffCheck::geometry::DFPointCloud::AddPoints)
5152

5253
.def("get_tight_bounding_box", &diffCheck::geometry::DFPointCloud::GetTightBoundingBox)
5354

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#! python3
2+
3+
import Rhino
4+
5+
import diffCheck
6+
from diffCheck import diffcheck_bindings
7+
from diffCheck import df_cvt_bindings as df_cvt
8+
import diffCheck.df_util
9+
10+
from ghpythonlib.componentbase import executingcomponent as component
11+
12+
ABSTOL = Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance
13+
14+
class DFJointSegmentator(component):
15+
def RunScript(self,
16+
i_clusters: Rhino.Geometry.PointCloud,
17+
i_joints: Rhino.Geometry.Mesh,
18+
i_joint_ids: int,
19+
i_angle_threshold: float,
20+
i_distance_threshold: float):
21+
"""
22+
Amongst clusters, associates the clusters to the individual joints,
23+
creates a reference point cloud for each joint,
24+
and returns the joint segments, the reference point clouds, and the ICP transformations from the first to the second.
25+
26+
:param i_clusters: The clusters to be associated to the joints.
27+
:param i_joints: The joints to which the clusters will be associated. These are meshes.
28+
:param i_joint_ids: The joint ids of the joints.
29+
:param i_angle_threshold: The angle threshold for the association of the clusters to the joints.
30+
:param i_distance_threshold: The distance threshold for the association of the clusters to the joints.
31+
"""
32+
if i_angle_threshold is None : i_angle_threshold = 0.1
33+
if i_distance_threshold is None : i_distance_threshold = 0.1
34+
35+
if len(i_joints) != len(i_joint_ids):
36+
raise ValueError("The number of joints and joint ids must be the same.")
37+
38+
if len(i_clusters) == 0:
39+
raise ValueError("No clusters given.")
40+
41+
if not isinstance(i_clusters[0], Rhino.Geometry.PointCloud):
42+
raise ValueError("The input clusters must be PointClouds.")
43+
44+
if not isinstance(i_joints[0], Rhino.Geometry.Mesh):
45+
raise ValueError("The input joints must be convertible to Meshes.")
46+
47+
48+
# prepping the reference meshes
49+
n_joints = max(i_joint_ids) + 1
50+
joints = [ [] for i in range(n_joints) ]
51+
for face, id in zip(i_joints, i_joint_ids):
52+
face.Subdivide()
53+
face.Faces.ConvertQuadsToTriangles()
54+
joints[id].append(df_cvt.cvt_rhmesh_2_dfmesh(face))
55+
56+
joint_clouds = []
57+
registrations = []
58+
joint_segments = []
59+
df_clouds = [df_cvt.cvt_rhcloud_2_dfcloud(cluster) for cluster in i_clusters]
60+
61+
# for each joint, find the corresponding clusters and merge them, generate a reference point cloud, and register the merged clusters to the reference point cloud
62+
for joint in joints:
63+
64+
# create the reference point cloud
65+
joint_cloud = diffcheck_bindings.dfb_geometry.DFPointCloud()
66+
67+
for face in joint:
68+
face_cloud = face.sample_points_uniformly(1000)
69+
joint_cloud.add_points(face_cloud)
70+
71+
joint_clouds.append(df_cvt.cvt_dfcloud_2_rhcloud(joint_cloud))
72+
73+
# find the corresponding clusters and merge them
74+
segment = diffcheck_bindings.dfb_segmentation.DFSegmentation.associate_clusters(joint, df_clouds, i_angle_threshold, i_distance_threshold)
75+
diffcheck_bindings.dfb_segmentation.DFSegmentation.clean_unassociated_clusters(df_clouds, [segment], [joint], i_angle_threshold ,i_distance_threshold)
76+
77+
# register the merged clusters to the reference point cloud
78+
registration = diffcheck_bindings.dfb_registrations.DFRefinedRegistration.O3DICP(segment, joint_cloud)
79+
res = registration.transformation_matrix
80+
81+
registrations.append(df_cvt.cvt_ndarray_2_rh_transform(res))
82+
joint_segments.append(df_cvt.cvt_dfcloud_2_rhcloud(segment))
83+
84+
return joint_segments, registrations, joint_clouds
85+
86+
# if __name__ == "__main__":
87+
# o_joint_segments, o_reference_point_clouds, o_transforms = main(i_clusters, i_joints, i_joint_ids)
88+
89+
# for i in range(len(o_joint_segments)):
90+
# o_joint_segments[i].Transform(o_transforms[i])
731 Bytes
Loading
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{
2+
"name": "DFJointSegmentator",
3+
"nickname": "DFJointSegmentator",
4+
"category": "diffCheck",
5+
"subcategory": "Segmentation",
6+
"description": "Extracts the joints from a point cloud.",
7+
"exposure": 4,
8+
"instanceGuid": "fd804928-bf11-43eb-a29f-8afabefa660d",
9+
"ghpython": {
10+
"hideOutput": true,
11+
"hideInput": true,
12+
"isAdvancedMode": true,
13+
"marshalOutGuids": true,
14+
"iconDisplay": 2,
15+
"inputParameters": [
16+
17+
{
18+
"name": "i_clusters",
19+
"nickname": "i_clusters",
20+
"description": "The cluster point clouds from normal segmentation.",
21+
"optional": false,
22+
"allowTreeAccess": false,
23+
"showTypeHints": true,
24+
"scriptParamAccess": "list",
25+
"wireDisplay": "default",
26+
"sourceCount": 0,
27+
"typeHintID": "pointcloud"
28+
},
29+
{
30+
"name": "i_joint_faces",
31+
"nickname": "i_joint_faces",
32+
"description": "The joints to extract and analyze",
33+
"optional": false,
34+
"allowTreeAccess": false,
35+
"showTypeHints": true,
36+
"scriptParamAccess": "list",
37+
"wireDisplay": "default",
38+
"sourceCount": 0,
39+
"typeHintID": "mesh"
40+
},
41+
{
42+
"name": "i_joint_ids",
43+
"nickname": "i_joint_ids",
44+
"description": "The joint ids of the assembly",
45+
"optional": false,
46+
"allowTreeAccess": true,
47+
"showTypeHints": true,
48+
"scriptParamAccess": "list",
49+
"wireDisplay": "default",
50+
"sourceCount": 0,
51+
"typeHintID": "int"
52+
},
53+
{
54+
"name": "i_angle_threshold",
55+
"nickname": "i_angle_threshold",
56+
"description": "From 0 to 1, it's the sin value. By default 0.1. The closer to 0 the less permissive and viceversa to 1.",
57+
"optional": true,
58+
"allowTreeAccess": true,
59+
"showTypeHints": true,
60+
"scriptParamAccess": "item",
61+
"wireDisplay": "default",
62+
"sourceCount": 0,
63+
"typeHintID": "float"
64+
},
65+
{
66+
"name": "i_association_threshold",
67+
"nickname": "i_association_threshold",
68+
"description": "From 0 to infinite. By default 0.1. The closer to 0 the less permissive your point.",
69+
"optional": true,
70+
"allowTreeAccess": true,
71+
"showTypeHints": true,
72+
"scriptParamAccess": "item",
73+
"wireDisplay": "default",
74+
"sourceCount": 0,
75+
"typeHintID": "float"
76+
}
77+
],
78+
"outputParameters": [
79+
{
80+
"name": "o_joint_segments",
81+
"nickname": "o_joint_segments",
82+
"description": "The extracted joints.",
83+
"optional": false,
84+
"sourceCount": 0,
85+
"graft": false
86+
},
87+
{
88+
"name": "o_transforms",
89+
"nickname": "o_transforms",
90+
"description": "The transformations for each joint.",
91+
"optional": false,
92+
"sourceCount": 0,
93+
"graft": false
94+
},
95+
{
96+
"name": "o_reference_point_clouds",
97+
"nickname": "o_reference_point_clouds",
98+
"description": "The reference point clouds of the joints. They are generated from the ",
99+
"optional": false,
100+
"sourceCount": 0,
101+
"graft": false
102+
}
103+
]
104+
}
105+
}

0 commit comments

Comments
 (0)