规则化之前(红色)和之后(绿色)的闭合轮廓。
这个 CGAL包能够规范2D中的一组线段和开闭轮廓以及3D中的一组平面,以便所有输入对象根据用户指定的条件进行旋转和对齐。此外,我们提供了一个全局规范框架,可以根据用户需求和任何类型的几何对象进行调整。该包还可以与形状检测包结合使用。
给定一组无序的二维片段,用户可以强化这些片段中的三种规律:平行度:被检测为接近平行的线段被精确地平行。正交性:被检测为接近正交的片段被精确地正交。共线:平行线段被检测为近共线,并精确地形成共线。
该算法的典型使用包括以下步骤:使用二维线段创建输入范围;定义应一起规范化的分段组;用适当的参数实例化邻居查询和正则化类型的概念模型;调用函数regularize_segments()。
一旦用户定义了具有二维分段的输入范围,他可以将它们全部提供给正则化算法,这是默认选项,或者可以将它们重新组织成具有上下文相似性的分段组。例如,所有相同长度的分段可以组成一个组。在正则化时,只考虑组内的分段,即一个组中的分段不会朝向和/或对齐另一个组中的分段。
要应用该算法,用户必须定义两个模型:一个是概念NeighborQuery,它提供对分段最接近邻居的访问;另一个是概念RegularizationType,它提供一种可用的规律,应该对其进行调整。
这个CGAL组件提供了NeighborQuery概念的模型:
Segments::Delaunay_neighbor_query_2 - 通过构建Delaunay三角剖分来查找每个分段的局部邻居。更多细节请参见此处。
RegularizationType 概念的两个模型:
Segments::Angle_regularization_2 - 定向分段以加强它们之间的平行性和正交性。
Segments::Offset_regularization_2 - 对齐平行段以加强它们之间的共线性。
在CGAL中,Segments::Angle_regularization_2和Segments::Offset_regularization_2是两个用于对2D线段进行正则化的概念模型。
Segments::Angle_regularization_2:这个模型用于对线段进行角度正则化。它通过定向线段来加强平行性和正交性。在实践中,它可以使一组线段更加规整,例如,将一个组内的线段尽可能地平行或正交。这在几何形状分析和处理中非常有用,特别是在需要规范化线段方向以进行进一步分析或处理的场景中
Segments::Offset_regularization_2:这个模型用于对平行线段进行偏移正则化。它通过调整线段的偏移量来加强线段之间的共线性。共线性是指一组线段在同一直线上。通过偏移正则化,可以确保一组平行线段真正共线。在几何形状分析中,这对于确保数据的一致性和规整性非常有用,特别是在处理需要精确共线性的几何对象时。
对一组输入段进行正则化的标准方法是首先应用角度正则化,然后应用偏移正则化,但是该算法可以灵活处理其他情况。
该算法的核心是QP正则化框架。
下面的示例显示了该算法最直接的入口点,其中我们在所有输入段的组中应用两种类型的规则:并行性和正交性。该算法是通过函数regularized_segments()调用的。
角度和偏移正则化之前(红色)和之后(绿色)的一组2D段。
从该示例中可以看出,该算法不优先考虑任何方向,如垂直或水平方向,而是返回输入段的最优正则化配置。
该类通过在输入段的中心点上构建一个Delaunay三角剖分,使用类 CGAL::Delaunay_triangulation_2,找到每个段的局部邻居。因此,段的局部邻居由三角剖分中相应的单环邻居定义。只能为一组至少两个段构建Delaunay三角剖分。
Delaunay 邻居查询类从一组线段构造 Delaunay 三角剖分,线段必须由用户通过 Segments::Delaunay_neighbor_query_2::add_group() 方法提供,并仅在该组内查找每个线段的本地邻居。如果从未调用此方法,则所有输入线段都被视为一个组。
请注意,组的分段数量可以小于输入范围中的分段数量。例如,如果您的输入范围包含多个分段,这些分段在上下文中形成三个不同的对象组,例如三个不同建筑物的边界,并且您不想使这些建筑物彼此之间规则化,而是在每个建筑物边界内规则化,在这种情况下,您应该调用三次 add_group 方法。此类组的示例可以在此图中看到,您可以看到三组上下文相似的分段:外边界、内部顶部菱形和内部底部菱形,或在下图中。
在这个图中,有两个正方形,一个外部和一个内部。在左边,红色的部分显示了所有输入段之间的连接,这些连接是基于所有这些段构建的Delaunay三角剖分,而在右边,绿色的部分只显示了外部正方形段之间的连接,蓝色的部分只显示了内部正方形段之间的连接。
Delaunay三角剖分(红色)用于所有输入段(黑色,左侧)和两个上下文不同的组,其中绿色Delaunay用于外部段,蓝色Delaunay用作内部段(右侧)。
此类对2D片段进行定向,以增强它们之间的平行性和正交性。要在一组2D片段上应用角度正则化,用户必须:
指定线段与其初始方向的最大角度偏差,该偏差必须在[0,90]度的范围内。如果未提供边界,则将25度的边界设置为默认值;
通过segments::Angle_rregularization_2::add_group()方法添加段组(如果有)。
在优化之后,每个线段相对于其中点进行旋转。
在角度正则化之前(红色)和之后(绿色)生成的一组2D片段。
此类对齐二维平行线段,以增强它们之间的共线。要在一组2D片段上应用偏移正则化,用户必须:
指定两个平行线段之间的最大距离,该距离必须在区间[0,+inf)内。如果未提供边界,则将0.5单位长度的边界设置为默认值。
通过segments::Offset_regulation_2::add_group()方法添加平行线段组。如果用户没有这些组,则可以通过定向原始线段从“角度正则化”获得,也可以通过实用函数segments::parallel_groups()获得。点击此处查看更多详细信息。
在优化之后,每个片段沿着其正交方向平移。
注意,如果同一组内的输入段不完全平行,则定义为一个段的中点与该点在另一段的支撑线上的投影之间的距离,不是优化分段位置的好度量,这可能导致结果偏离用户在完全平行分段的情况下的期望。偏移正则化不会在内部定向线段以使其完全平行。这就是角度正则化类的作用。实用函数Segments::parallel_groups()也不确定线段的方向,只返回近似平行的线段组。
在偏移正则化之前(红色)和之后(绿色)生成的一组2D片段。
以下示例演示了形状正则化算法在一组二维片段上依次对角度和偏移的使用。
第一个例子包含 15 个分段。使用最大边界 10 度,长度 0.1 单位,分别对这些分段顺序执行角度和偏移正则化。我们在这里还展示了如何创建和使用上下文相似的分段组,并对每个组进行单独正则化。所定义组是外边界、顶部和底部菱形。由于分段形状正则化算法基于QP正则化框架,因此该例子还展示了如何直接使用该框架,而不是调用函数regularize_segments()。
正则化之前(红色)、角度之后(黄色)和偏移(绿色)的一组2D线段。
using Neighbor_query =
CGAL::Shape_regularization::Segments::Delaunay_neighbor_query_2;
using Angle_regularization =
CGAL::Shape_regularization::Segments::Angle_regularization_2;
using Offset_regularization =
CGAL::Shape_regularization::Segments::Offset_regularization_2;
using Quadratic_program =
CGAL::OSQP_quadratic_program_traits;
using Quadratic_angle_regularizer =
CGAL::Shape_regularization::QP_regularization<
Kernel, Segments, Neighbor_query, Angle_regularization, Quadratic_program>;
using Quadratic_offset_regularizer =
CGAL::Shape_regularization::QP_regularization<
Kernel, Segments, Neighbor_query, Offset_regularization, Quadratic_program>;
第二个示例包含65个线段,这些线段是由一组输入点构成的。所有点都被组织成组,使得每组代表近似的2D线。可以通过形状检测包来实现将点组织成这样的组。我们使用主成分分析软件包为每组点拟合一个线段。分别使用80度和2个单位长度的边界对这些片段依次执行角度和偏移正则化。
正则化之前(红色)、角度之后(黄色)和偏移(绿色)的一组2D线段。
除了主要的算法,我们还提供了几个实用函数,这些函数经常与算法结合使用。
CGAL组件还提供了三种对细分市场进行分组的方式:
Segments::parallel_groups()-将一组无序的2D段组织成一组平行段。
Segments::thogonal_groups()-将一组无序的2D段组织为正交段组。
Segments::colline_groups()-将一组无序的二维线段组织为共线线段组。
近似平行(左)、近似正交(中)和近似共线(右)线段的组。红色、绿色和蓝色表示每组2D线段内的组。
Segments::parallel_groups()函数使用户能够形成平行分段组。例如,如果您知道所有分段在一定容差范围内已经相互平行,并且您不想通过应用角度正则化算法来对它们进行定向,但您仍然需要通过使用偏移正则化算法最小化平行分段之间的偏移来使它们共线,您可以使用此函数创建平行分段组,并将其作为偏移正则化算法的输入,正如我们在这里所做的那样。
其他两个函数服务于类似的目标。一个函数 Segments::orthogonal_groups() 首先创建平行线段组,然后将它们合并成组,其中所有线段要么平行,要么相互正交。另一个函数 Segments::collinear_groups() 首先创建平行线段组,然后将每个组拆分为共线线段组(如果有的话)。
请注意,这些函数都没有对输入段进行正则化。它们只返回具有相似方向和/或位置的段索引组。
在正则化角度和偏移后,简化具有相似特性的线段是一项常见的后处理任务。该CGAL组件提供了一个实用函数Segments::unique_Segments(),该函数获取一组输入线段,根据共线特性对它们进行分组,然后为每组共线线段返回最适合该组的线段(见下图)。
即使线段彼此相距很远,但相对于它们之间的正交距离很近,即它们几乎共线,它们也将合并为图中的蓝色线段。
具有多个共线线段的输入线段(左)简化为唯一线段(右)。
形状正则化算法的性能主要取决于所使用的QP求解器。当使用CGAL::OSQP_quadratic_program_traits模型时,我们利用并有效地使用了相关QP问题的稀疏性,这在实际中导致了快速性能。
下面的图表(实线)显示了计算时间如何依赖于输入段的数量。我们首先观察到最具挑战性的步骤是角度正则化,而偏移正则化要快得多。这是通过将问题分割成偏移正则化组来降低复杂性的效果。由于每组平行段远小于原始输入段集,因此总计算时间也较小。同样的想法也可以应用于加速角度正则化。从一开始就将输入段分割成具有上下文相似属性的组将获得更好的性能,如图表(虚线)所示。但是,请注意,并非每个数据集都可以有意义地分割成这样的组。
为了对算法进行基准测试,我们使用了MacBook Pro 2018,具有2.2 GHz Intel Core i7处理器(6核)和32 GB 2400 MHz DDR4内存。安装的操作系统是OS X 10.15 Catalina。我们首先在正方形中创建一组随机段,使得所有段要么平行于X轴,要么平行于Y轴。然后我们在[-15, 15]度区间内通过随机角度稍微扰动所有段,并在相同的输入上应用正则化算法10次。返回的时间是所有迭代的平均时间。在预分组版本中,我们将所有段重新分组为10个段的组,并将正则化算法应用于每个组。例如,在50个输入段的情况下,我们将有5个输入组。由于组非常小,角度和偏移正则化之间的时间没有太大差异。结果如下图所示。
正则化角度(红色实线)和偏移(绿色实线)的时间(以秒为单位),无需重新组合输入段,并且角度(红色虚线)和偏移量(绿色虚线)由10个段组成。
给定一组由线段连接的二维有序点,这些线段形成一个闭合或开放的轮廓,用户可以强化该轮廓连续边缘之间的三种规律性:
平行度:检测为近乎平行的轮廓边缘被精确地平行。正交性:轮廓边缘被检测为近似正交,因此被精确地正交。共线:平行轮廓边缘,被检测为近共线,被精确地设置为共线。
该算法的典型使用包括以下步骤:
指定轮廓的类型,打开或关闭;使用适当的参数创建类ContourDirections的实例;调用函数regularize_closed_contour()或regularize_open_contour()。
我们假设每个轮廓至少有一个主方向,该方向是轮廓边缘旋转的参考方向。给定一组估计或用户指定的方向,每个边缘要么平行于这些方向,要么垂直于这些方向。
为了估计轮廓的主要方向,该组件提供了三个概念ContourDirections的模型:
Contours::Longest_direction_2 - 将最长的轮廓边设置为唯一的主方向。
Contours::Multiple_directions_2 - 根据用户指定的参数尝试估计轮廓中的多个主方向(见下图)。
Contours::User_defined_directions_2 - 将用户指定的主方向设置为轮廓方向。
在轮廓正则化之前(红色)和之后(绿色)的闭合轮廓。找到的主要方向标记为黄色。
设置方向后,算法在轮廓边的数量上是线性的。它首先遍历每个轮廓边,并将其朝向最佳拟合方向。在第二步中,如果所有平行的连续边在用户指定的最大容差距离内,则将它们合并。这里的距离被定义为第一条边的中点与该点在下一条边的支撑线上的投影之间的距离。合并段的位置相对于其邻居进行了优化。在最后几步中,所有段被重新连接到轮廓中,如下图所示。由于合并步骤,轮廓中的输出边数量不一定与输入边数量相同。
轮廓正则化算法的步骤(从左到右):正则化前的闭合轮廓;边缘朝着找到的主方向旋转的断开的轮廓,这里我们只有一个方向;对优化后的边、蓝边进行合并,并对它们的位置进行优化;以及最终重新连接的轮廓。
如果用户希望将每个轮廓边缘单独朝最佳拟合方向旋转,而不在将其重新连接成闭合/开放轮廓后,她可以使用分段正则化算法,或者通过调用ContourDirections::orient()方法来定位每个分段。
下面的例子展示了算法最直接的入口点,我们在这里对一个简单的闭合轮廓进行正则化。该算法通过函数regularize_closed_contour()调用。
正则化之前(红色)和之后(绿色)的闭合轮廓。
CGAL::Shape_regularization::Contours::
regularize_closed_contour
在下面的例子中,我们正则化一个闭合的轮廓。我们使用多方向估计器,它只返回一个方向,因为轮廓是直线的。事实上,在这种情况下,返回的方向与最长的边缘方向一致。
在轮廓正则化之前(红色)和之后(绿色)的闭合轮廓。
CGAL::Shape_regularization::Contours::regularize_closed_contour
开放轮廓是指轮廓的头部和尾部没有连接的轮廓。这种情况需要特殊处理,但算法的核心是相同的。在下面的例子中,我们根据其最长边对开放轮廓进行正则化。这个例子还展示了如何向算法提供属性映射,以便算法访问轮廓顶点的坐标。
在轮廓正则化之前(红色)和之后(绿色)的开放轮廓。
CGAL::Shape_regularization::Contours::regularize_open_contour
实际上,封闭和开放的轮廓正则化算法相对于轮廓顶点的数量具有线性时间行为。事实上,由于合并连续共线边的第二步,时间不是线性的,如下图所示。对于一些多边形,这样的边数量相当高,在将它们合并到一个分段之前,我们将它们全部收集到一个组中,以便找到放置最终分段的最佳位置,这可能会导致在某些情况下性能变慢。
为了对算法进行基准测试,我们使用了一台 MacBook Pro 2018,它配有 2.2 GHz Intel Core i7 处理器(6 核)和 32 GB 2400 MHz DDR4 内存。安装的操作系统是 OS X 10.15 Catalina。我们首先创建一个具有所需边数的直线多边形。然后,我们以[-15,15]度之间的随机角度略微扰动所有边,并在同一输入上应用正则化算法 10 次。返回的时间是所有迭代的平均时间。结果如下图所示。
正则化闭合(红色)和打开(绿色)轮廓的时间(以秒为单位)。
给定一组三维平面及其相应的内层集,用户可以使用 regularize_planes() 函数来强化这些平面之间的四种规律性:
平行度:被检测为接近平行的平面被精确地平行。正交性:被检测为接近正交的平面被精确地正交。共面性:检测为近共面的平行平面被精确地设置为共面。轴对称:对于用户指定的轴,检测到几乎对称的平面,则使它们完全对称。
用户可以选择只规整这四个属性中的一个或几个。该过程是贪婪的,基于层次分解(共面簇是平行簇的子集,平行簇是轴对称和正交簇的子集)。
以下示例说明了如何使用平面正则化函数。
CGAL::Shape_regularization::Planes::regularize_planes
形状正则化组件是一个基于Bauchet等人提出的二次规划(QP)全局正则化算法的通用框架。只有当你想了解形状正则化框架的内部组织方式的细节,或者你想通过实现自己的正则化类型来扩展该框架时,才应该参考本节。
我们之前介绍的角度正则化和偏移正则化是该算法的两个特定实例。用户可以添加其他实例,如本文所述。
该框架遵循[1]的第3节,但该论文中的算法得到了扩展和推广。主要算法背后的思想是使能量最小化
U(x)=(1-λ)D(x)+λV(x),其中x = (x1,…,xn)是对n个输入项进行扰动的配置,D(x)和V(x)分别表示数据项和成对势能,λ∈[0,1]是加权这两个项的参数。通过设置正确类型的D(x)和V(x),可以将问题重新表述为具有(n+m)个变量和2(n+m)个线性约束的二次优化问题,其中m是通过将一个项目与其最接近的邻居之一连接而形成的唯一对的数量。让我们解释一下,当输入项是分段时,我们想要规范它们的方向,以加强它们之间的平行性和正交性,它是如何工作的。
为了建立框架,我们首先需要为每个片段找到最近的邻居。这些邻居是通过邻居查询的概念提供的。在内部,我们根据这些邻居创建一个图。每当i 当我们有了图,我们通过RegularizationType的概念填写术语D(x)和V(x)。首先,我们通过RegularizationType::bound()方法获得每个分段的最大扰动范围。由于我们想旋转分段,我们在这里返回每个分段相对于其原始方向的最大允许角度偏差,假设为25度。 接下来,对于图中的每条边 {i,j},我们通过 RegularizationType::target() 方法计算两个段 i 和 j 之间的扰动差。例如,这可能是段方向相对于 90 或 180 度的差。假设两个段之间的角度为 85 度,那么我们返回 90-85=5 度,因为这是我们应该最小化的值,以使两个段相互正交。 然后,我们建立了二次规划问题,该问题通过 QuadraticProgram Traits 概念来解决。返回的结果存储为长度为 (n+m) 的向量,其中更新后的扰动值,前 n 个值是应添加到输入段的原始方向的值,以对其进行更新,后 m 个值被最小化为零。更新是通过 RegularizationType::update() 方法实现的。 总体而言,类QP_regularization由以下参数表示:提供访问项目的本地邻居的方法的NeighborQuery,RegularizationType,确定要应用的正则化类型,以及用于解决相应QP问题的QuadraticProgram Traits。 在这个通用框架内,用户可以规范任何一组输入项,只要他们有自己的邻居搜索、规范化类型和QP求解器。 NeighborQuery 概念提供了访问项目本地邻居的方法。要创建一个遵循此概念的模型,用户必须提供运算符的重载: NeighborQuery::operator()(),它必须用所有项目的索引填充一个向量,这些项目是查询项目的邻居。 例如,给定一个分段,此运算符可能会返回一个向量,其中包含与此分段有一定距离的其他输入分段的索引,但此距离是测量的。有关更多详细信息,请参见上文和本节。 RegularizationType的概念决定了要应用的正则化类型。要创建一个尊重这一概念的模型,必须定义三个函数: RegularizationType::target() 函数,用于估计两个相邻值之间的规律性类型, 例如,如果我们想使段方向正则化,即使段彼此平行和正交,第一个函数应该返回查询段与其每个邻居之间的角度扰动;第二个函数应该返回一个最大角度,在该角度内接受查询段的旋转;第三个函数应该更新输入段的原始方向。有关更多详细信息,请参见上文和本节。 为了解决上述算法的相关QP问题,CGAL 库提供了一个外部OSQP求解器的包装器CGAL::OSQP_quadratic_program_traits,它是二次程序特征概念的一个模型。 或者,可以使用来自线性与二次规划求解器包的内部CGAL求解器,但我们不建议使用它。形状正则化必须解决的内部二次规划是稀疏的。CGAL版本将在内部将此问题转换为密集问题,需要花费大量精力来解决,而OSQP版本则特别关注问题的稀疏性,从而获得更好的性能。由于QP_regularization类由通用概念QuadraticProgram Traits参数化,因此也欢迎用户提供他们自己的求解器版本。 如果你想实现自己的正则化方法,遵循相同的框架,例如强化一种不同于已经提供的正则化类型,你必须实现自己的 RegularizationType 概念模型,可能还需要实现 NeighborQuery 概念模型。这些概念用于参数化主要的 QP_regularization 算法: 使用 NeighborQuery 查找每个输入项的本地邻居;使用RegularizationType来估计这些邻居之间的当前规律;使用RegularizationType设置允许的规则性变化的最大范围;使用QuadraticProgram Traits解决二次规划问题;使用RegularizationType根据修改后的规则更新输入项。 此外,如果用户知道如何针对特定类型的输入数据优化QP求解器,他可能也想更改QP求解器。为此,用户必须实现QuadraticProgram Traits概念的模型。5.2、临近
5.3、正则化
RegularizationType::bound() 函数,返回允许规则性变化的最大界限,以及
RegularizationType::update() 函数,根据修改后的规则更新输入项。5.4、求解
5.5、实施
6、其他