需求分析
为解决基于特征值特征向量的柱面识别策略不鲁棒的问题,本文在上周工作的基础上提出了一种基于棱线配对的柱面识别策略。
输入输出
- 输入:局部可能包含柱面和椭球面的三维离散化网格
- 输出:柱面的参数
识别策略
既然是基于棱线配对的柱面识别策略,那么首先我们需要获取棱线。在网格生成中,棱线是一个很重要的概念,它是由两个顶点组成的线段,而顶点是由三角形面共享的。因此,我们可以通过遍历所有的三角形面,获取所有的棱线。在此之后,需要对棱线进行几何意义上的分段与拟合,获取棱线局部的曲率半径及曲率中心,选择出属于局部圆形的棱线。在此之后,需要对识别出的圆进行剪枝,这一步比较依赖于经验,因为不同模型数量级和大小不同,剪枝策略下对应的阈值参数也不同。最后需要对剪枝后的圆进行参数化,并将各个圆形两两配对,得到最终的柱面参数。
棱线获取
设置宏定义`EDGE_INDEX’,通过遍历所有的边,根据其相邻面片法向量的差是否大于’EDGE_INDEX’来判断是否为棱线。
以下是一个代码示例,并非基于MeshLib的实现,仅供参考。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #define EDGE_INDEX 0.8
for (int i = 0; i < mesh->numEdges(); i++) { Edge *edge = mesh->edge(i); if (edge->isBoundary()) continue; Face *face1 = edge->halfedge(0)->face(); Face *face2 = edge->halfedge(1)->face(); if (face1 == NULL || face2 == NULL) continue; Vector3d normal1 = face1->normal(); Vector3d normal2 = face2->normal(); if (normal1.dot(normal2) < EDGE_INDEX) { edge->isEdgeLine = true; } }
|
棱线拟合
对于识别出的棱线,我们需要对其进行拟合,获取其曲率半径及曲率中心,由于三点定圆,所以选择棱线上相互连续的三个点进行拟合,比较建议的选点策略是深度优先选择,可以尽可能的减少三点不共面的情况,三点不共面的情况会有但是实际上几乎不会影响到结果,毕竟很难找到对应的圆来与之配对。
以下是一个代码示例,并非基于MeshLib的实现,仅供参考。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| for (int i = 0; i < mesh->numEdges(); i++) { Edge *edge = mesh->edge(i); if (!edge->isEdgeLine) continue; Vertex *v1 = edge->halfedge(0)->vertex(); Vertex *v2 = edge->halfedge(1)->vertex(); Vertex *v3 = edge->halfedge(0)->next()->vertex(); Vector3d p1 = v1->position(); Vector3d p2 = v2->position(); Vector3d p3 = v3->position(); Vector3d n1 = v1->normal(); Vector3d n2 = v2->normal(); Vector3d n3 = v3->normal(); Vector3d center = getCircleCenter(p1, p2, p3); double radius = (p1 - center).norm(); edge->circleCenter = center; edge->circleRadius = radius; }
|
棱线剪枝
异常圆的剔除
剪枝主要去除的是棱线度不为2的点和半径过大的点,应该很好操作,这里不再赘述。
相近圆的合并
对于相近的圆,我们需要将其合并,这里的相近是指圆心距离小于一定阈值的圆,这个阈值是需要根据实际情况调整的,可以重载operator==
来实现。
1 2 3 4 5 6 7 8 9 10 11 12
| struct circle { double radius; CPoint center; CPoint normal; bool operator== (const circle& c) { return abs(radius - c.radius) < ZERO && ((center - c.center).norm() < ZERO) && ((normal - c.normal).norm() < ZERO); } circle(double r,CPoint c,CPoint n):radius(r),center(c),normal(n){} };
|
棱线配对
对于剪枝后的圆,我们需要将其两两配对,得到最终的柱面参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| void CirclePairRe<M>::getCirclePair() {
for(int i = 0 ; i < circlelist.size() ; i++) { for(int j = i ; j < circlelist.size() ; j++) { if(abs(circlelist[i].radius - circlelist[j].radius) <ZERO && (circlelist[i].normal ^ circlelist[j].normal).norm() < ZERO && ((circlelist[i].center - circlelist[j].center)^circlelist[i].normal).norm() < ZERO ) { auto cylinderCenter = (circlelist[i].center + circlelist[j].center) / 2; auto cylinderHeight = (circlelist[i].center - circlelist[j].center).norm(); if(cylinderHeight < ZERO) continue; auto cylinderRadius = circlelist[i].radius; auto centerline = (circlelist[i].center - circlelist[j].center); centerline /= centerline.norm(); auto rotate = new Eigen::Matrix3d(); CPoint x(-centerline[1], centerline[0], 0); x /= x.norm(); CPoint y = centerline ^ x; *rotate << x[0], x[1], x[2], y[0], y[1], y[2], centerline[0], centerline[1], centerline[2]; cylinder *cc = new cylinder(cylinderRadius,cylinderHeight,cylinderCenter,rotate); cylinderlist.push_back(*cc); } }
} }
|
总结
棱线配对可以避免线性相关的问题,提高了柱面识别的鲁棒性,但是在实际应用中,这样的方法大幅依赖棱线,但工业上很多倒角都是光滑先借的,应用场景有限,但是对于一些特殊的模型,这样的方法还是很有意义的。