将scipy.interpolate.splprep的输出转换为NURBS格式以进行IGES显示

问题描述 投票:2回答:1

我正在寻求将描述任意曲线的一系列有序(非常密集)的2D点转换为NURBS表示形式,可以将其写入IGES文件中。

我正在使用scipy.interpolate的splprep获取给定系列点的B样条表示,然后我假定NURBS的定义本质上就是这样,并说所有权重都等于1。但是我认为我是根本上会误解splprep的输出,尤其是“ B样条系数”与在某些CAD软件包中手动重新创建样条所需的控制点之间的关系(我使用的是Siemens NX11)。

我尝试了一个简单的例子,从一组稀疏点中逼近函数y = x ^ 3:

import scipy.interpolate as si
import numpy as np
import matplotlib.pyplot as plt

# Sparse points defining cubic
x = np.linspace(-1,1,7)
y = x**3

# Get B-spline representation
tck, u = si.splprep([x,y],s=0.0)

# Get (x,y) coordinates of control points
c_x = tck[1][0]
c_y = tck[1][1]

# Plotting
u_fine = np.linspace(0,1,1000)
x_fine, y_fine = si.splev(u_fine, tck)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'o', x_fine, y_fine)
ax.axis('equal')
plt.show()

哪个提供以下参数:

>>> t
array([ 0.        ,  0.        ,  0.        ,  0.        ,  0.39084883,
        0.5       ,  0.60915117,  1.        ,  1.        ,  1.        ,  1.        ])
>>> c_x
array([ -1.00000000e+00,  -9.17992269e-01,  -6.42403598e-01,
        -2.57934892e-16,   6.42403598e-01,   9.17992269e-01,
         1.00000000e+00])
>>> c_y
array([ -1.00000000e+00,  -7.12577481e-01,  -6.82922469e-03,
        -1.00363771e-18,   6.82922469e-03,   7.12577481e-01,
         1.00000000e+00])
>>> k
3
>>> u
array([ 0.        ,  0.25341516,  0.39084883,  0.5       ,  0.60915117,
        0.74658484,  1.        ])
>>> 

我假设两组系数(c_x,c_y)描述了构造样条曲线所需极点的(x,y)坐标。在NX中手动尝试可得到相似的样条曲线,尽管不尽相同,但区间中的其他点的评估与Python中的不同。当我将此手动样条线导出为IGES格式时,NX将结线更改为下面的值(同时显然保持相同的控制点/极点,并将所有权重设置为1)。

t_nx = np.array([0.0, 0.0, 0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0, 1.0, 1.0])

以另一种方式,将splprep结(t)写入IGES定义(具有所述“控制点”和权重= 1)似乎没有给出有效的样条。 NX和至少一个其他软件包无法对其进行评估,理由是“ B样条曲线的修剪或参数值无效”。

我认为至少有三种可能性:

  1. 非平凡的转换对于从非有理B样条到有理B样条是必要的>
  2. [IGES样条有特定于应用程序的解释(即我对splprep输出的解释是正确的,但是当在IGES转换例程中手动绘制/绘制时,这被NX简化/近似了)。似乎不太可能。
  3. splprep的系数不能以我所描述的方式解释为控制点
  4. 我通过比较所有权重= 1的scipy B样条(link)和IGES NURBS样条的方程式来注销第一种可能性(link,第14页)。它们看起来完全相同,正是这使我相信splprep系数=控制点。

Any

帮助澄清以上几点,将不胜感激!

注意,我想表示闭合曲线的可能性,因此,如果可能,请坚持使用splprep。


编辑:

我认为先使用splrep尝试此过程会更简单,因为输出对我而言似乎更直观。我假设返回的系数是控制点的y值,但不知道它们对应的x位置。因此,我尝试使用this矩阵方法根据样条曲线定义和输入数据来计算它们。 C矩阵只是输入数据。 N矩阵是每个x值的每个基函数的评估,我使用here所示的(略微修改的)递归函数进行了此操作。然后剩下的就是将N取反,并将C乘以C得到控制点。代码和结果如下:
import numpy as np
import scipy.interpolate as si

# Functions to evaluate B-spline basis functions
def B(x, k, i, t):
   if k == 0:
      return 1.0 if t[i] <= x < t[i+1] else 0.0
   if t[i+k] == t[i]:
      c1 = 0.0
   else:
      c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t)
   if t[i+k+1] == t[i+1]:
      c2 = 0.0
   else:
      c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t)
   return c1 + c2

def bspline(x, t, c, k):
   n = len(t) - k - 1
   assert (n >= k+1) and (len(c) >= n)
   cont = []
   for i in range(n):
       res = B(x, k, i, t)
       cont.append(res)
   return cont

# Input data
x = np.linspace(-1,1,7)
y = x**3

# B-spline definition
t, c, k = si.splrep(x,y)

# Number of knots = m + 1 = n + k + 2
m = len(t) - 1

# Number of kth degree basis fcns
n = m - k - 1

# Define C and initialise N matrix
C_mat = np.column_stack((x,y))
N_mat = np.zeros(((n+1),(n+1)))

# Calculate basis functions for each x, store in matrix
for i, xs in enumerate(x):
    row = bspline(xs, t, c, k)
    N_mat[i,:] = row

# Last value must be one...
N_mat[-1,-1] = 1.0

# Invert the matrix
N_inv = np.linalg.inv(N_mat)

# Now calculate control points
P = np.dot(N_inv, C_mat)

结果:

>>> P
array([[ -1.00000000e+00,  -1.00000000e+00],
       [ -7.77777778e-01,  -3.33333333e-01],
       [ -4.44444444e-01,  -3.29597460e-17],
       [ -3.12250226e-17,   8.67361738e-18],
       [  4.44444444e-01,  -2.77555756e-17],
       [  7.77777778e-01,   3.33333333e-01],
       [  1.00000000e+00,   1.00000000e+00]])

我认为是正确的,因为P的y值与splrep,c的系数匹配。有趣的是,x值似乎是结平均值(可以按以下方式单独计算)。对于熟悉数学的人来说,这个结果也许是显而易见的,但对我而言肯定不是。

def knot_average(knots, degree):
    """
    Determines knot average vector from knot vector.

    :knots: A 1D numpy array describing knots of B-spline.
        (NB expected from scipy.interpolate.splrep)
    :degree: Integer describing degree of B-spline basis fcns
    """
    # Chop first and last vals off
    knots_to_average = knots[1:-1]
    num_averaged_knots = len(knots_to_average) - degree + 1
    knot_averages = np.zeros((num_averaged_knots,))
    for i in range(num_averaged_knots):
        avg = np.average(knots_to_average[i: i + degree])
        knot_averages[i] = avg
    return(knot_averages)

现在,要将它们转换为IGES NURBS,我认为是定义标准化结矢量,将权重都设置为等于1并从上方包括P个控制点的情况。我将其标准化如下,并在其下面包括了IGES文件。

但是,当我尝试将文件导入NX时,它再次在定义中指出无效的修整参数而失败。谁能告诉我这是否是有效的NURBS定义?

或者也许这是NX的一些限制?例如,我注意到在交互式绘制Studio花键时,结矢量被迫(夹紧)统一(如fang所指)。必须具有此约束(且权重全部= 1)才能唯一定义曲线。有趣的是,如果我强迫splrep使用统一的结点矢量(即钳制但否则统一)返回样条曲线表示,则将IGES读入。尽管从NX的角度来看,我也不认为这是必要的-这样做违背了目的首先拥有NURBS。所以这似乎不太可能,我回头想知道我对splrep输出的解释是否正确...有人可以指出我错了吗?

# Original knot vector
>>> t
array([-1.        , -1.        , -1.        , -1.        , -0.33333333,
        0.        ,  0.33333333,  1.        ,  1.        ,  1.        ,  1.        ])

mini = min(t)
maxi = max(t)
r = maxi - mini
norm_t = (t-mini)/r

# Giving:
>>> norm_t
array([ 0.        ,  0.        ,  0.        ,  0.        ,  0.33333333,
        0.5       ,  0.66666667,  1.        ,  1.        ,  1.        ,  1.        ])

IGES定义:

                                                                        S      1
,,11Hspline_test,13Hsome_path.igs,19HSpline to iges v1.0,4H 0.1,,,,,,,  G      1
1.0, 2,2HMM,,,8H 8:58:19,,,,;                                           G      2
     126       1               1       1       0       0               0D      1
     126      27               4       0                 Spline1       1D      2
126,6,3,0,0,1,0,0.0,0.0,0.0,0.0,0.33333,0.5,0.6666666,1.0,1.0,1.0,1.0, 1P      1
1.0,1.0,1.0,1.0,1.0,1.0,1.0,-1.0,-1.0,0.0,-0.7777,-0.33333,0.0,        1P      2
-0.444444,0.0,0.0,0.0,0.0,0.0,0.4444444,0.0,0.0,0.777777777,0.33333,   1P      3
0.0,1.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0;                                 1P      4
S      1G      2D      2P      4                                        T      1

我正在尝试将描述任意曲线的一系列有序(非常密集)的2D点转换为NURBS表示形式,可以将其写入IGES文件中。我正在使用scipy.interpolate的...

python scipy curve-fitting bspline nurbs
1个回答
0
投票

这个小众查询偶尔会帮助其他人,结果证明问题出在IGES中参数数据部分的格式不正确。描述样条线的数据每行不能超过64个字符。 splprep输出的解释是正确的,(c_x,c_y)数组描述了连续极点的(x,y)坐标。等效的NURBS定义只需要指定所有权重=1。

© www.soinside.com 2019 - 2024. All rights reserved.