寻求将B样条/三次样条拆分为多段Bezier曲线的方法
平滑样条转多段Bezier曲线的问题与需求
背景与现有实现
本人正在自学游戏开发,研究路径设计方向。现有一组随机点,需要构建最优拟合的平滑路径(无需经过所有点)。通过Scipy的平滑样条插值解决了常规样条必须经过所有点的问题,生成的B样条满足平滑需求,代码如下:
from scipy.interpolate import BSpline, CubicSpline from scipy.interpolate import splrep from scipy.interpolate import BPoly import numpy as np # x, y are the given points x = np.array([ 0. , 1.2, 1.9, 3.2, 5, 6.5]) y = np.array([ 0. , 2.3, 3. , 4.3, 2.9, 3.1]) t, c, k = splrep(x, y, s=0.8, k=3) # 通过设置s=0.8实现平滑 spl = BSpline(t, c, k) # 创建B样条
转换为多段Bezier曲线的尝试与问题
需要将上述B样条拆分为多段Bezier曲线,尝试两种方法均遇到问题:
方法1:参考代码转换的困惑
参考相关代码进行B样条转Bezier曲线,代码如下:
import aggdraw import numpy as np import scipy.interpolate as si from PIL import Image def scipy_bspline(cv, degree=3): """ cv: 控制点数组 degree: 曲线阶数 """ count = cv.shape[0] degree = np.clip(degree, 1, count-1) kv = np.clip(np.arange(count+degree+1)-degree, 0, count-degree) max_param = count - (degree * (1-0)) # 原代码中periodic未定义,此处修正为0 spline = si.BSpline(kv, cv, degree) return spline, max_param # 基于数学方法转换 def bspline_to_bezier(cv): cv_len = cv.shape[0] assert cv_len >= 4, "至少提供4个控制点" spline, max_param = scipy_bspline(cv, degree=3) for i in range(1, max_param): spline = si.insert(i, spline, 2) return spline.c[:3 * max_param + 1] def draw_bezier(d, bezier): path = aggdraw.Path() path.moveto(*bezier[0]) for i in range(1, len(bezier) - 1, 3): v1, v2, v = bezier[i:i+3] path.curveto(*v1, *v2, *v) d.path(path, aggdraw.Pen("black", 2)) cv = np.array([[ 40., 148.], [ 40., 48.], [244., 24.], [160., 120.], [240., 144.], [210., 260.], [110., 250.]]) im = Image.fromarray(np.ones((400, 400, 3), dtype=np.uint8) * 255) bezier = bspline_to_bezier(cv) d = aggdraw.Draw(im) draw_bezier(d, bezier) d.flush() # 显示/保存图像
无法理解代码中v1, v2, v = bezier[i:i+3]的含义,误将其当作二次Bezier曲线的控制点绘图,得到异常结果,验证代码如下:
for i in range(1, len(bezier) - 1, 3): v1, v2, v = bezier[i:i+3] cc = np.array([(v1[0], v1[1]), (v2[0], v2[1]), (v[0], v[1])]) curve = BPoly(cc[:, None, :], [0,1]) X = np.linspace(0, 1, 20) p = curve(X) plt.gca().set_aspect('equal') plt.plot(*p.T)
方法2:CubicSpline分段的问题
改用CubicSpline获取分段多项式,代码如下:
from scipy.interpolate import BSpline, CubicSpline from scipy.interpolate import splrep from scipy.interpolate import BPoly import numpy as np x = np.array([ 0. , 1.2, 1.9, 3.2, 5, 6.5]) y = np.array([ 0. , 2.3, 3. , 4.3, 2.9, 3.1]) t, c, k = splrep(x, y, s=0.8, k=3) spl = BSpline(t, c, k) import matplotlib.pyplot as plt fig, ax = plt.subplots() xx = np.linspace(0, 6, 500) ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline') ax.plot(x, y, 'rs') xx = x yy = spl(xx) cu = CubicSpline(xx, yy) plt.plot(xx, yy, 'ko') for i in range(len(cu.x)-1): xs = np.linspace(cu.x[i], cu.x[i+1], 100) plt.plot(xs, np.polyval(cu.c[:,i], xs - cu.x[i]))
该方法可生成分段曲线,但分段数量随采样点密度变化,希望以最少分段数逼近样条曲线。
求可行解决思路与指导
恳请提供将平滑B样条转换为最少分段数Bezier曲线的可行方法与指导。
内容的提问来源于stack exchange,提问作者user1285419
相关产品推荐
相关产品推荐

