如何计算2D矢量形状的中间轴? - python

我将2D形状存储为SVG中的路径元素。形状由贝塞尔曲线和线段组成。

我在生成的形状上也有一组相等的空间点
使用弧长参数化。

如何使用SVG或这些点确定形状的中轴?

我正在使用Python,但是任何形式的伪代码或算法建议将不胜感激。

以下是我正在处理的形状类型的示例,红色点是沿曲线的采样点。

参考方案

有点迟,但这里去了:

上图显示:(我已经使用在线工具将OP的图像转换为SVG
因此,参差不齐的边界是红点的假象)
1.叠加的中间和比例轴变换(MAT和SAT)。
2.仅缩放轴变换。
3.仅中间轴变换。
4.两爪之一(请参阅下文)。
5. SAT中有一个3叉(MAT中有很多)。

要找到中间轴(MA)或中间轴变换(MAT)(上图中的紫色曲线),请使用以下算法
可以使用(基于paper
Choi,Choi,Moon和Wee撰写的文章-查看演示实现here
还可以处理相交的形状和带孔的形状)。还存在其他算法。

该算法比查找二进制图像(例如位图)骨架要难得多
(又称草火或离散变换),但具有多种优势(例如,可以进行分析)。
为简化起见,下面的讨论仅处理简单情况
(不相交)无孔的形状。

一些定义

曲线-参数为t∈[0,1]的参量贝塞尔曲线。换一种说法,
矢量图形中使用的典型曲线。
形状(Ω)-(定义直接取自纸张)-“闭合的连接
ℝ²中的有界开放子集由有限数量的相互不交集界定
简单的闭合曲线。”对于此答案,可以进一步假定
形状没有孔。简单来说,它是由
多条曲线给出的边界-上图中的填充灰色部分。
边界-形状的边界。换句话说,曲线的索引序列
使得在t = 0处任何曲线的起点都对应于前一条
曲线的终点在t = 1。假定边界是正向的
(即通过沿边界沿逆时针方向走走
永远在您的左边)。
边界点-形状边界上的任何点都可以通过
标识曲线和t曲线参数的索引。
最大磁盘-形状不被任何其他磁盘遮挡的磁盘
也在形状之内。可以识别每个最大磁盘
1个或多个(通常是2个)边界点及其中心点。
n-prong-在n处切向接触形状边界的最大圆盘
点。
分支点-n> = 3的n叉最大磁盘中心。
MA上在MA平面树中形成顶点的点。
中间轴(MA)-所有n钉的中心的并集。
中间轴变换(MAT)-所有n分支的并集。换一种说法,
定义中包括最大磁盘的半径。
接触点(cp)-链接到唯一最大磁盘的边界点
已经找到。
尖角-边界点,通常在曲线参数t = 0或
t = 1,这是不可微分的(不光滑),并且形成一个内部
角度<180°。
钝角-除角度> 180°外,与尖角相同。
cp图-具有顶点的图(不一定是平面图)
因此,联系点还包含有关所有找到的信息
最大磁盘,然后整个MAT。图中的每个顶点(cp)
具有以下优势:

下一个-通过步行从当前位置找到第一个接触点
沿边界逆时针旋转。
上一个-通过步行从当前位置找到第一个接触点
沿边界顺时针旋转。
next-around-绕过
逆时针方向的最大磁盘数。
prev-around-绕过
顺时针方向上的最大磁盘数。
我们可以在返回点返回时将以上各项视为运算符(。)
通过跟随一条边缘来接触另一个接触点,例如如果a是接触点,
那么a.next是另一个联系点。

一些注意事项

中间轴变换形成无根平面树,树的叶子位于
每个1叉中心(包括尖角);如果形状有孔
那么MAT是一个平面图。
您可以通过应用
标度轴转换(SAT)(上图中的红色曲线)到MAT。的
demo实现这样的转换,
根据this
研究。但是,该演示通过确保SAT是子集来改进算法
MAT并确保拓扑保留。

算法概述

查找所有1-prongs;它们恰好是中轴树的叶子。这些
是尖角和1叉与边界接触
最大局部向内弯曲。将1-prong的接触点添加到cp-graph。
请注意,由于1-prong仅具有一个接触点,因此下一个边缘
并且prev-around只会再次返回找到的联系点。
对于一组代表性的边界点(例如OP给出的那些红点),
计算它们的最大磁盘数。它们通常是2爪子。 (请参见下面的“查找2叉”)。
通过连接适当的边,将其接触点添加到cp图中。注意
在这种情况下,因为我们发现了2叉,
(或prev-around)跟随两次,一个将回到同一接触点。
最终找到n> = 3的n个分支。从每个接触点开始:

通过接触点(例如cp1)开始遍历cp图
反复应用cp1.next.next-around,直到您回到
cp1。
如果发生上述情况的1或2次迭代,则移至下一个接触点
在边界上(使用下一个)。但是,如果需要3次或更多次迭代,
可以证明存在一个三叉分支点
并且应在每个边界块之间插入接触点
cp1和cp1.next。

在这种情况下,请插入3叉钉(有关如何查找这些钉,请参见下文)并返回
再次从cp1开始执行步骤1。
重复步骤1和2,直到找到所有3个分支。

现在已经隐式地找到了MAT的结构。
要提取MAT,需要遍历cp图。选择任何
接触点(称为cp1)作为根(为简单起见,假设它链接到2叉),
然后应用(称为cp2)。现在可以从cp1的链接最大磁盘上画一条线
以cp2为中心。这条线是MA子集的近似值。
通过点之间的插值可以获得更好的近似值
因为我们知道了接触点的边界切线。让两条线
端点是二次贝塞尔曲线的端点。由于MA的定位
我们确切地知道了两个最大的磁盘中心(通过2个边界切线的平均值)
端点处的二次贝塞尔曲线的导数也可以构造
点之间的最佳近似二次贝塞尔曲线。
如果cp2链接到3叉,则表示形成了平面树
由MA分叉(即分叉或分叉),我们需要同时遵循两个MA
边缘(例如,使用堆栈或递归)。另一方面,如果
cp2链接到1叉MA的叶子,并且
该边缘的遍历停止。实际上,由于MA形成了
平面树的遍历算法与其他算法本质上相同
树数据结构。

寻找2叉

我将在这里进行总结,但是Choi等人的论文。相当解释这部分
清晰易懂。

在边界上选择一个点,该点将是
我们的2分叉,称为bp1。绘制一个向内的法线
边界点(即2叉中的第一个“叉”)。现在迭代:

选择一个点p1(沿bp1距离d1
此射线)的d1足够大,使得一个圆(在bp1处切向绘制)
以射线为中心)将在至少一个点处切割边界。
找到最接近p1的边界点,称为bp2。最后找到
射线上与bp1和bp2等距的点,称为p2。
使用新的p1 p2返回步骤2。
重复步骤2和3,直到从p1到两个的距离
边界点在一定公差范围内。 2爪有
现在发现bp1和bp2有两个接触点
p1其中心。

寻找三爪

在这里,我们通过构造一个外接圆来偏离本文,
使用潜在功能。

选择边界点(cp1)链接的最大磁盘中心作为对磁盘的初始猜测
三爪中心。称它为p。
找到3个最接近的点,分别与3+个边界块中的每个分开。
构造这些点的外接圆并使用其中心作为新点
p。
重复步骤2和3,直到从p到3个边界块的距离
等于在一定的公差范围内。
将由此找到的3个边界点标记为3点的接触点。
点p是三叉点的中心。

请随时询问是否不清楚。

Python GPU资源利用 - python

我有一个Python脚本在某些深度学习模型上运行推理。有什么办法可以找出GPU资源的利用率水平?例如,使用着色器,float16乘法器等。我似乎在网上找不到太多有关这些GPU资源的文档。谢谢! 参考方案 您可以尝试在像Renderdoc这样的GPU分析器中运行pyxthon应用程序。它将分析您的跑步情况。您将能够获得有关已使用资源,已用缓冲区,不同渲染状态上…

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

python:ConfigParser对象,然后再阅读一次 - python

场景:我有一个配置文件,其中包含要执行的自动化测试的列表。这些测试是长期循环执行的。   配置文件的设计方式使ConfigParser可以读取它。由于有两个三个参数,因此我需要通过每个测试。现在,此配置文件由script(s1)调用,并且按照配置文件中的列表执行测试。Script(s1)第一次读取配置,并且在每次测试完成后都会执行。阅读两次的要求:由于可能会…

Python exchangelib在子文件夹中读取邮件 - python

我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…

python-docx应该在空单元格已满时返回空单元格 - python

我试图遍历文档中的所有表并从中提取文本。作为中间步骤,我只是尝试将文本打印到控制台。我在类似的帖子中已经看过scanny提供的其他代码,但是由于某种原因,它并没有提供我正在解析的文档的预期输出可以在https://www.ontario.ca/laws/regulation/140300中找到该文档from docx import Document from…