Skip to content

Commit b688ec9

Browse files
UPDATE-WIP: add IsPointOnFace function, and angle threshold parameter for cluster to mesh face association
1 parent 032f428 commit b688ec9

File tree

2 files changed

+67
-75
lines changed

2 files changed

+67
-75
lines changed

src/diffCheck/segmentation/DFSegmentation.cc

Lines changed: 51 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ namespace diffCheck::segmentation
9797
std::shared_ptr<geometry::DFPointCloud> DFSegmentation::AssociateClustersToMeshes(
9898
std::vector<std::shared_ptr<geometry::DFMesh>> referenceMesh,
9999
std::vector<std::shared_ptr<geometry::DFPointCloud>> &clusters,
100+
double angleThreshold,
100101
double associationThreshold)
101102
{
102103
std::shared_ptr<geometry::DFPointCloud> unifiedPointCloud = std::make_shared<geometry::DFPointCloud>();
@@ -111,12 +112,12 @@ namespace diffCheck::segmentation
111112

112113
// Getting the center of the mesh face
113114
Eigen::Vector3d faceCenter;
114-
// open3d::geometry::OrientedBoundingBox orientedBoundingBox = o3dFace->GetMinimalOrientedBoundingBox();
115115
faceCenter = o3dFace->GetCenter();
116116

117117
// Getting the normal of the mesh face
118118

119119
Eigen::Vector3d faceNormal = face->GetFirstNormal();
120+
faceNormal.normalize();
120121

121122
// iterate through the segments to find the closest ones compared to the face center taking the normals into account
122123
Eigen::Vector3d segmentCenter;
@@ -138,54 +139,20 @@ namespace diffCheck::segmentation
138139

139140
double currentDistance = (faceCenter - segmentCenter).norm() / std::abs(segmentNormal.dot(faceNormal));
140141
// if the distance is smaller than the previous one, update the distance and the corresponding segment
141-
if (currentDistance < faceDistance)
142+
if (std::abs(faceNormal.dot(segmentNormal)) > angleThreshold && currentDistance < faceDistance)
142143
{
143144
correspondingSegment = segment;
144145
faceDistance = currentDistance;
145146
}
146147
}
147148

148149
// get the triangles of the face.
149-
std::vector<Eigen::Vector3i> faceTriangles = o3dFace->triangles_;
150+
std::vector<Eigen::Vector3i> faceTriangles = face->Faces;
150151

151152
for (Eigen::Vector3d point : correspondingSegment->Points)
152153
{
153154
bool pointInFace = false;
154-
/*
155-
To check if the point is in the face, we take into account all the triangles forming the face.
156-
We calculate the area of each triangle, then check if the sum of the areas of the tree triangles
157-
formed by two of the points of the referencr triangle and our point is equal to the reference triangle area
158-
(within a user-defined margin). If it is the case, the triangle is in the face.
159-
*/
160-
for (Eigen::Vector3i triangle : faceTriangles)
161-
{
162-
// reference triangle
163-
Eigen::Vector3d v0 = o3dFace->vertices_[triangle[0]];
164-
Eigen::Vector3d v1 = o3dFace->vertices_[triangle[1]];
165-
Eigen::Vector3d v2 = o3dFace->vertices_[triangle[2]];
166-
Eigen::Vector3d n = (v1 - v0).cross(v2 - v0);
167-
double referenceTriangleArea = n.norm()*0.5;
168-
169-
// triangle 1
170-
Eigen::Vector3d n1 = (v1 - v0).cross(point - v0);
171-
double area1 = n1.norm()*0.5;
172-
173-
// triangle 2
174-
Eigen::Vector3d n2 = (v2 - v1).cross(point - v1);
175-
double area2 = n2.norm()*0.5;
176-
177-
// triangle 3
178-
Eigen::Vector3d n3 = (v0 - v2).cross(point - v2);
179-
double area3 = n3.norm()*0.5;
180-
181-
double res = ( area1 + area2 + area3 - referenceTriangleArea) / referenceTriangleArea;
182-
if (res < associationThreshold)
183-
{
184-
pointInFace = true;
185-
break;
186-
}
187-
}
188-
if (pointInFace)
155+
if (DFSegmentation::IsPointOnFace(face, point, associationThreshold))
189156
{
190157
unifiedPointCloud->Points.push_back(point);
191158
unifiedPointCloud->Normals.push_back(
@@ -224,6 +191,7 @@ namespace diffCheck::segmentation
224191
std::vector<std::shared_ptr<geometry::DFPointCloud>> &unassociatedClusters,
225192
std::vector<std::shared_ptr<geometry::DFPointCloud>> &existingPointCloudSegments,
226193
std::vector<std::vector<std::shared_ptr<geometry::DFMesh>>> meshes,
194+
double angleThreshold,
227195
double associationThreshold)
228196
{
229197
for (std::shared_ptr<geometry::DFPointCloud> cluster : unassociatedClusters)
@@ -239,7 +207,7 @@ namespace diffCheck::segmentation
239207
{
240208
clusterNormal += normal;
241209
}
242-
clusterNormal /= cluster->GetNumPoints();
210+
clusterNormal.normalize();
243211

244212
std::shared_ptr<diffCheck::geometry::DFMesh> testMesh;
245213
int meshIndex;
@@ -254,9 +222,8 @@ namespace diffCheck::segmentation
254222

255223
std::shared_ptr<open3d::geometry::TriangleMesh> o3dFace = meshFace->Cvt2O3DTriangleMesh();
256224

257-
Eigen::Vector3d faceNormal = meshFace->GetFirstNormal();
258-
259-
// std::shared_ptr<open3d::geometry::OrientedBoundingBox> orientedBoundingBox(new open3d::geometry::OrientedBoundingBox(o3dFace->GetMinimalOrientedBoundingBox()));
225+
faceNormal = meshFace->GetFirstNormal();
226+
faceNormal.normalize();
260227
faceCenter = o3dFace->GetCenter();
261228
/*
262229
To make sure we select the right meshFace, we add another metric:
@@ -265,13 +232,13 @@ namespace diffCheck::segmentation
265232
To prevent this, we take into the account the angle between the line linking the center of the meshFace considered
266233
and the center of the point cloud cluster and the normal of the cluster. This value should be close to pi/2
267234
268-
The following two lines are not super optimized but more readable thatn the optimized version
235+
The following two lines are not super optimized but more readable than the optimized version
269236
*/
270237

271238
double clusterNormalToJunctionLineAngle = std::abs(std::acos(clusterNormal.dot((clusterCenter - faceCenter).normalized())));
272-
239+
273240
double currentDistance = (clusterCenter - faceCenter).norm() * std::pow(std::cos(clusterNormalToJunctionLineAngle), 2) / std::pow(clusterNormal.dot(faceNormal), 2);
274-
if (currentDistance < distance)
241+
if (std::abs(faceNormal.dot(clusterNormal)) > angleThreshold && currentDistance < distance)
275242
{
276243
distance = currentDistance;
277244
meshIndex = std::distance(meshes.begin(), std::find(meshes.begin(), meshes.end(), piece));
@@ -280,51 +247,26 @@ namespace diffCheck::segmentation
280247
}
281248
}
282249
}
283-
// diffCheck::visualizer::Visualizer vis;
284-
// vis.AddPointCloud(cluster);
285-
// vis.AddMesh(meshes[meshIndex][faceIndex]);
286-
// vis.Run();
250+
287251
std::shared_ptr<geometry::DFPointCloud> completed_segment = existingPointCloudSegments[meshIndex];
288252
for (Eigen::Vector3d point : cluster->Points)
289253
{
290-
bool pointInFace = false;
291-
std::shared_ptr<open3d::geometry::TriangleMesh> o3dFace = meshes[meshIndex][faceIndex]->Cvt2O3DTriangleMesh();
292-
std::vector<Eigen::Vector3i> faceTriangles = o3dFace->triangles_;
293-
for (Eigen::Vector3i triangle : faceTriangles)
294-
{
295-
Eigen::Vector3d v0 = o3dFace->vertices_[triangle[0]];
296-
Eigen::Vector3d v1 = o3dFace->vertices_[triangle[1]];
297-
Eigen::Vector3d v2 = o3dFace->vertices_[triangle[2]];
298-
Eigen::Vector3d n = (v1 - v0).cross(v2 - v0);
299-
double referenceTriangleArea = n.norm()*0.5;
300-
Eigen::Vector3d n1 = (v1 - v0).cross(point - v0);
301-
double area1 = n1.norm()*0.5;
302-
Eigen::Vector3d n2 = (v2 - v1).cross(point - v1);
303-
double area2 = n2.norm()*0.5;
304-
Eigen::Vector3d n3 = (v0 - v2).cross(point - v2);
305-
double area3 = n3.norm()*0.5;
306-
double res = ( area1 + area2 + area3 - referenceTriangleArea) / referenceTriangleArea;
307-
if (res < associationThreshold)
308-
{
309-
pointInFace = true;
310-
break;
311-
}
312-
}
313-
if (pointInFace)
254+
std::vector<Eigen::Vector3i> faceTriangles = meshes[meshIndex][faceIndex]->Faces;
255+
if (IsPointOnFace(meshes[meshIndex][faceIndex], point, associationThreshold))
314256
{
315257
completed_segment->Points.push_back(point);
316258
completed_segment->Normals.push_back(cluster->Normals[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
317259
}
318260
}
319261
std::vector<int> indicesToRemove;
262+
320263
for (int i = 0; i < cluster->Points.size(); ++i)
321264
{
322265
if (std::find(completed_segment->Points.begin(), completed_segment->Points.end(), cluster->Points[i]) != completed_segment->Points.end())
323266
{
324267
indicesToRemove.push_back(i);
325268
}
326269
}
327-
328270
for (auto it = indicesToRemove.rbegin(); it != indicesToRemove.rend(); ++it)
329271
{
330272
std::swap(cluster->Points[*it], cluster->Points.back());
@@ -334,4 +276,39 @@ namespace diffCheck::segmentation
334276
}
335277
}
336278
};
279+
280+
bool DFSegmentation::IsPointOnFace(
281+
std::shared_ptr<diffCheck::geometry::DFMesh> face,
282+
Eigen::Vector3d point,
283+
double associationThreshold)
284+
{
285+
/*
286+
To check if the point is in the face, we take into account all the triangles forming the face.
287+
We calculate the area of each triangle, then check if the sum of the areas of the tree triangles
288+
formed by two of the points of the referencr triangle and our point is equal to the reference triangle area
289+
(within a user-defined margin). If it is the case, the triangle is in the face.
290+
*/
291+
std::vector<Eigen::Vector3i> faceTriangles = face->Faces;
292+
for (Eigen::Vector3i triangle : faceTriangles)
293+
{
294+
Eigen::Vector3d v0 = face->Vertices[triangle[0]];
295+
Eigen::Vector3d v1 = face->Vertices[triangle[1]];
296+
Eigen::Vector3d v2 = face->Vertices[triangle[2]];
297+
Eigen::Vector3d n = (v1 - v0).cross(v2 - v0);
298+
double referenceTriangleArea = n.norm()*0.5;
299+
Eigen::Vector3d n1 = (v1 - v0).cross(point - v0);
300+
double area1 = n1.norm()*0.5;
301+
Eigen::Vector3d n2 = (v2 - v1).cross(point - v1);
302+
double area2 = n2.norm()*0.5;
303+
Eigen::Vector3d n3 = (v0 - v2).cross(point - v2);
304+
double area3 = n3.norm()*0.5;
305+
double res = ( area1 + area2 + area3 - referenceTriangleArea) / referenceTriangleArea;
306+
if (res < associationThreshold)
307+
{
308+
return true;
309+
break;
310+
}
311+
}
312+
return false;
313+
}
337314
} // namespace diffCheck::segmentation

src/diffCheck/segmentation/DFSegmentation.hh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,39 @@ namespace diffCheck::segmentation
2929
/** @brief Associates point cloud segments to mesh faces and merges them. It uses the center of mass of the segments and the mesh faces to find correspondances. For each mesh face it then iteratively associate the points of the segment that are actually on the mesh face.
3030
* @param referenceMesh the vector of mesh faces to associate with the segments
3131
* @param clusters the vector of clusters from cilantro to associate with the mesh faces of the reference mesh
32+
* @param angleThreshold the threshold to consider the a cluster as potential candidate for association. the value passed is the minimum cosine of the angles. A value of 0.99 requires perfect alignment, while a value of 0.8 is more permissive. A value of 0 allows any angle between the normal of the cluster and the normal of the mesh face.
3233
* @param associationThreshold the threshold to consider the points of a segment and a mesh face as associable. It is the ratio between the surface of the closest mesh triangle and the sum of the areas of the three triangles that form the rest of the pyramid described by the mesh triangle and the point we want to associate or not. The lower the number, the more strict the association will be and some poinnts on the mesh face might be wrongfully excluded.
3334
* @return std::shared_ptr<geometry::DFPointCloud> The unified segments
3435
*/
3536
static std::shared_ptr<geometry::DFPointCloud> DFSegmentation::AssociateClustersToMeshes(
3637
std::vector<std::shared_ptr<geometry::DFMesh>> referenceMesh,
3738
std::vector<std::shared_ptr<geometry::DFPointCloud>> &clusters,
38-
double associationThreshold);
39+
double angleThreshold = 0.95,
40+
double associationThreshold = 0.1);
3941

4042
/** @brief Iterated through clusters and finds the corresponding mesh face. It then associates the points of the cluster that are on the mesh face to the segment already associated with the mesh face.
4143
* @param unassociatedClusters the clusters from the normal-based segmentatinon that haven't been associated yet.
4244
* @param existingPointCloudSegments the already associated segments
4345
* @param Meshes the mesh faces for all the model. This is used to associate the clusters to the mesh faces.
46+
* * @param angleThreshold the threshold to consider the a cluster as potential candidate for association. the value passed is the minimum cosine of the angles. A value of 0.99 requires perfect alignment, while a value of 0.8 is more permissive. A value of 0 allows any angle between the normal of the cluster and the normal of the mesh face.
4447
* @param associationThreshold the threshold to consider the points of a segment and a mesh face as associable. It is the ratio between the surface of the closest mesh triangle and the sum of the areas of the three triangles that form the rest of the pyramid described by the mesh triangle and the point we want to associate or not. The lower the number, the more strict the association will be and some poinnts on the mesh face might be wrongfully excluded.
4548
*/
4649
static void DFSegmentation::CleanUnassociatedClusters(
4750
std::vector<std::shared_ptr<geometry::DFPointCloud>> &unassociatedClusters,
4851
std::vector<std::shared_ptr<geometry::DFPointCloud>> &existingPointCloudSegments,
4952
std::vector<std::vector<std::shared_ptr<geometry::DFMesh>>> Meshes,
53+
double angleThreshold = 0.95,
54+
double associationThreshold = 0.1);
55+
56+
private: ///< helper methods
57+
/** @brief private method to check if a point is on a face of a triangle mesh triangle, within a certain association threshold. This takes into account the fact that, in 3D, a point can be "above" a triangle of a triangle mesh but still considered as being on the mesh face.
58+
* @param face the triangle mesh face to check the point against
59+
* @param point the point to check
60+
* @param associationThreshold the threshold to consider the point associable to the mesh. It is the ratio between the surface of the closest mesh triangle and the sum of the areas of the three triangles that form the rest of the pyramid described by the mesh triangle and the point we want to associate or not. The lower the number, the more strict the association will be and some poinnts on the mesh face might be wrongfully excluded.
61+
*/
62+
static bool DFSegmentation::IsPointOnFace(
63+
std::shared_ptr<diffCheck::geometry::DFMesh> face,
64+
Eigen::Vector3d point,
5065
double associationThreshold);
5166
};
5267
} // namespace diffCheck::segmentation

0 commit comments

Comments
 (0)