使用 numpy/scipy 的快速 B 样条算法

2024-02-29

我需要在 python 中计算 bspline 曲线。我研究了 scipy.interpolate.splprep 和其他一些 scipy 模块,但找不到任何可以轻松满足我需要的东西。所以我在下面编写了自己的模块。代码运行良好,但速度很慢(测试函数运行时间为 0.03 秒,考虑到我只要求 100 个带有 6 个控制顶点的样本,这似乎很多)。

有没有办法通过一些 scipy 模块调用来简化下面的代码,这可能会加快速度?如果没有,我可以对我的代码做些什么来提高其性能?

import numpy as np

# cv = np.array of 3d control vertices
# n = number of samples (default: 100)
# d = curve degree (default: cubic)
# closed = is the curve closed (periodic) or open? (default: open)
def bspline(cv, n=100, d=3, closed=False):

    # Create a range of u values
    count = len(cv)
    knots = None
    u = None
    if not closed:
        u = np.arange(0,n,dtype='float')/(n-1) * (count-d)
        knots = np.array([0]*d + range(count-d+1) + [count-d]*d,dtype='int')
    else:
        u = ((np.arange(0,n,dtype='float')/(n-1) * count) - (0.5 * (d-1))) % count # keep u=0 relative to 1st cv
        knots = np.arange(0-d,count+d+d-1,dtype='int')


    # Simple Cox - DeBoor recursion
    def coxDeBoor(u, k, d):

        # Test for end conditions
        if (d == 0):
            if (knots[k] <= u and u < knots[k+1]):
                return 1
            return 0

        Den1 = knots[k+d] - knots[k]
        Den2 = knots[k+d+1] - knots[k+1]
        Eq1  = 0;
        Eq2  = 0;

        if Den1 > 0:
            Eq1 = ((u-knots[k]) / Den1) * coxDeBoor(u,k,(d-1))
        if Den2 > 0:
            Eq2 = ((knots[k+d+1]-u) / Den2) * coxDeBoor(u,(k+1),(d-1))

        return Eq1 + Eq2


    # Sample the curve at each u value
    samples = np.zeros((n,3))
    for i in xrange(n):
        if not closed:
            if u[i] == count-d:
                samples[i] = np.array(cv[-1])
            else:
                for k in xrange(count):
                    samples[i] += coxDeBoor(u[i],k,d) * cv[k]

        else:
            for k in xrange(count+d):
                samples[i] += coxDeBoor(u[i],k,d) * cv[k%count]


    return samples




if __name__ == "__main__":
    import matplotlib.pyplot as plt
    def test(closed):
        cv = np.array([[ 50.,  25.,  -0.],
               [ 59.,  12.,  -0.],
               [ 50.,  10.,   0.],
               [ 57.,   2.,   0.],
               [ 40.,   4.,   0.],
               [ 40.,   14.,  -0.]])

        p = bspline(cv,closed=closed)
        x,y,z = p.T
        cv = cv.T
        plt.plot(cv[0],cv[1], 'o-', label='Control Points')
        plt.plot(x,y,'k-',label='Curve')
        plt.minorticks_on()
        plt.legend()
        plt.xlabel('x')
        plt.ylabel('y')
        plt.xlim(35, 70)
        plt.ylim(0, 30)
        plt.gca().set_aspect('equal', adjustable='box')
        plt.show()

    test(False)

The two images below shows what my code returns with both closed conditions: Open curve Closed curve


因此,在对我的问题进行了很多思考和大量研究之后,我终于有了答案。一切都可以在 scipy 中找到,我将我的代码放在这里,希望其他人能发现它有用。

该函数接受 N d 点数组、曲线度数、周期状态(打开或关闭),并将沿该曲线返回 n 个样本。有多种方法可以确保曲线样本等距,但目前我将重点关注这个问题,因为这一切都与速度有关。

值得注意的是:我似乎无法超越 20 度的曲线。当然,这已经有些过分了,但我认为值得一提。

另外值得注意的是:在我的机器上,下面的代码可以在 0.017 秒内计算 100,000 个样本

import numpy as np
import scipy.interpolate as si


def bspline(cv, n=100, degree=3, periodic=False):
    """ Calculate n samples on a bspline

        cv :      Array ov control vertices
        n  :      Number of samples to return
        degree:   Curve degree
        periodic: True - Curve is closed
                  False - Curve is open
    """

    # If periodic, extend the point array by count+degree+1
    cv = np.asarray(cv)
    count = len(cv)

    if periodic:
        factor, fraction = divmod(count+degree+1, count)
        cv = np.concatenate((cv,) * factor + (cv[:fraction],))
        count = len(cv)
        degree = np.clip(degree,1,degree)

    # If opened, prevent degree from exceeding count-1
    else:
        degree = np.clip(degree,1,count-1)


    # Calculate knot vector
    kv = None
    if periodic:
        kv = np.arange(0-degree,count+degree+degree-1)
    else:
        kv = np.clip(np.arange(count+degree+1)-degree,0,count-degree)

    # Calculate query range
    u = np.linspace(periodic,(count-degree),n)


    # Calculate result
    return np.array(si.splev(u, (kv,cv.T,degree))).T

要测试它:

import matplotlib.pyplot as plt
colors = ('b', 'g', 'r', 'c', 'm', 'y', 'k')

cv = np.array([[ 50.,  25.],
   [ 59.,  12.],
   [ 50.,  10.],
   [ 57.,   2.],
   [ 40.,   4.],
   [ 40.,   14.]])

plt.plot(cv[:,0],cv[:,1], 'o-', label='Control Points')

for d in range(1,21):
    p = bspline(cv,n=100,degree=d,periodic=True)
    x,y = p.T
    plt.plot(x,y,'k-',label='Degree %s'%d,color=colors[d%len(colors)])

plt.minorticks_on()
plt.legend()
plt.xlabel('x')
plt.ylabel('y')
plt.xlim(35, 70)
plt.ylim(0, 30)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()

开放曲线或周期曲线的结果:

ADDENDUM

从 scipy-0.19.0 开始,有一个新的scipy.interpolate.BSpline https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.interpolate.BSpline.html#scipy.interpolate.BSpline可以使用的功能。

import numpy as np
import scipy.interpolate as si
def scipy_bspline(cv, n=100, degree=3, periodic=False):
    """ Calculate n samples on a bspline

        cv :      Array ov control vertices
        n  :      Number of samples to return
        degree:   Curve degree
        periodic: True - Curve is closed
    """
    cv = np.asarray(cv)
    count = cv.shape[0]

    # Closed curve
    if periodic:
        kv = np.arange(-degree,count+degree+1)
        factor, fraction = divmod(count+degree+1, count)
        cv = np.roll(np.concatenate((cv,) * factor + (cv[:fraction],)),-1,axis=0)
        degree = np.clip(degree,1,degree)

    # Opened curve
    else:
        degree = np.clip(degree,1,count-1)
        kv = np.clip(np.arange(count+degree+1)-degree,0,count-degree)

    # Return samples
    max_param = count - (degree * (1-periodic))
    spl = si.BSpline(kv, cv, degree)
    return spl(np.linspace(0,max_param,n))

等效性测试:

p1 = bspline(cv,n=10**6,degree=3,periodic=True) # 1 million samples: 0.0882 sec
p2 = scipy_bspline(cv,n=10**6,degree=3,periodic=True) # 1 million samples: 0.0789 sec
print np.allclose(p1,p2) # returns True
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 numpy/scipy 的快速 B 样条算法 的相关文章

随机推荐

  • 评估 Python 日志记录 YAML 配置文件中的语句

    考虑以下 Python 代码片段loggingYAML 配置文件 version 1 formatters simple format asctime s name s levelname s message s handlers logf
  • 在我的项目中使用 android.support.v7.widget.CardView (Eclipse)

    我想使用新的 CardView 小部件 该小部件是随新的 Android L 开发人员预览支持库一起引入的 如上所述here https developer android com preview material compatibilit
  • while循环中的Python套接字recv数据没有停止

    当我尝试用 a 接收数据时while loop即使没有数据 循环也不会停止 import socket class Connect connect socket socket socket AF INET socket SOCK STREA
  • acts_as_votable 按赞成票排序

    到目前为止 我还没有找到任何可以使用以下方法按点赞数排序问题的方法 acts as votable gem https github com ryanto acts as votable 这是我的点赞和索引方法 def upvote que
  • Asp net core Blazor Auth0 异常:OpenIdConnectAuthenticationHandler:message.State 为 null 或为空

    我正在使用 Blazor 服务器端和 Auth0 来处理我的应用程序的登录 我可以正常进入登录屏幕 但是当我单击 确定 使用正确的凭据登录时 我得到以下信息 Exception OpenIdConnectAuthenticationHand
  • mongo 哪个更好?将子项放入数组或多个字段中

    我有一个 MongoDB 作为多个独立 SQL 数据库和 API 的整合中间层运行 这些 SQL 数据库共享相似的 Article 表 但结构不同 鉴于一篇文章将属于多个类别和子类别 最多不超过4层 结果我们有两种模式设计 categori
  • MS SQL 中文排序规则

    我在我们的亚洲市场生产数据库中发现了奇怪的行为 在中文字母的情况下 条件并不像人们所期望的那样工作 create table Temp TextContent nvarchar 20 ChineseType varchar 10 inser
  • 相同的命令在不同的设备上输出不同的值

    我有两个树莓派 3 当我做 tfenv pi raspberrypi pip install opencv python 在第一个 Raspberry Pi 上 我得到 Collecting opencv python Could not
  • 增加空指针的定义是否明确?

    在进行指针算术时 有很多未定义 未指定行为的示例 指针必须指向同一数组内部 或超出末尾的一个 或同一对象内部 限制何时可以基于上述内容进行比较 操作 ETC 以下操作定义明确吗 int p 0 p 5 2 6 1 通过添加来修改操作数对象的
  • 切换到 VS 2010 后,编译的程序运行速度变慢了

    我们的关键公司应用程序 C 处理高分辨率图像 我们付出了巨大的努力来优化它 它在约 2 5 秒内执行超过 250 万次操作 我们已经使用 VS 2005 多年 上周 我们将所有内容都转移到了 VS 2010 完全相同的项目代码 现在 当我构
  • 如何在 WP7 中访问播客?

    如何访问手机上的播客列表并在 WP7 中播放它们 Thanks 目前无法查询播客 流派不会告诉您正在使用的内容是否是播客 MediaLibrary 将仅访问常规歌曲 而不是其他内容 希望 Mango 更新能够为媒体访问 API 带来一些扩展
  • 匹配第一个和最后一个字符的正则表达式

    我正在尝试使用正则表达式来检查字符串中的第一个和最后一个字符是否是 a z 之间的字母字符 我知道这与第一个字符匹配 a z i 但是我如何检查最后一个字符呢 This a z a z i 不起作用 我怀疑这两个子句之间应该有一些东西 但我
  • MIPS汇编将整数转换为二进制并读取1的数量?

    我正在开发一个程序 它从用户那里获取一个整数 然后输出它的二进制等价物中有多少个 1 所以首先我认为我需要将其转换为二进制 然后使用循环检查所有 32 位以找出有多少个 1 我已经浏览了几个小时并尝试不同的方法来首先将整数转换为二进制 最好
  • 如何使用 React DatePicker 选择时间

    我在用着反应日期选择器 https github com Hacker0x01 react datepicker我还需要包括选择日期的时间 我没有从文档中找到任何如何实现这一点的示例 DatePicker 是否提供了任何开箱即用的功能 还是
  • 从批处理文件执行存储过程

    如何从批处理文件执行 SQL Server 中的存储过程 使用 Windows 身份验证 如果您使用的是 Sql Server 2005 则可以使用sqlcmd http msdn microsoft com en us library m
  • SwiftUI 简化许多文本字段的 .onChange 修饰符

    我正在寻找一种方法来简化 重构 SwiftUI 中添加 onChange of 具有许多文本字段的视图 如果解决方案简洁 我也会移动修饰符 更接近适当的字段 而不是位于 ScrollView 的末尾 在这个 在这种情况下 所有 onChan
  • NuxtJS 上的 ESLint 和 Prettier 冲突

    当我创建一个新的 Nuxt js 项目时 我遇到了 ESLint 和 Prettier 的一个非常令人筋疲力尽的问题 如果我节省这个 vue文件中 Prettier 尝试修复它 但 ESLint 阻止它这样做 所以 我无法删除这方面的错误
  • 拖动进入时突出显示按钮

    刚刚开始探索iOS SDK 我有一些按钮 需要突出显示它们 触摸一次然后拖动 据我了解 当您单击按钮然后拖动到外部然后再次拖动到内部时 会触发 TouchDragEnter 事件 当您单击按钮外部然后拖动到内部时 是否会触发任何事件 亚历山
  • 数据注释、IDataErrorInfo 和 MVVM

    我正在尝试找到验证 MVVM 中数据的最佳方法 目前 我正在尝试使用 MVVM 模式将 IDataErrorInfo 与数据注释结合使用 然而 似乎没有任何作用 我不确定我可能做错了什么 我有这样的东西 Model public class
  • 使用 numpy/scipy 的快速 B 样条算法

    我需要在 python 中计算 bspline 曲线 我研究了 scipy interpolate splprep 和其他一些 scipy 模块 但找不到任何可以轻松满足我需要的东西 所以我在下面编写了自己的模块 代码运行良好 但速度很慢