Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在样条曲线上采样到给定的最大弦偏差_Python_Numpy_Scipy - Fatal编程技术网

Python 在样条曲线上采样到给定的最大弦偏差

Python 在样条曲线上采样到给定的最大弦偏差,python,numpy,scipy,Python,Numpy,Scipy,我正在寻找一种在样条曲线上采样点的快速方法,这样通过这些点的多边形或线串不会超过原始样条曲线的给定弦误差。我有一个我不久前写的算法,可以在图片中产生结果(如果感兴趣,请参阅下面的代码;我不希望有人仔细研究它)。它工作正常,但速度不是很快(在我的计算机上生成该图形大约需要2秒钟)。是否有更简单的东西,可能内置在numpy或scipy中,可以实现这一点 谢谢 import numpy as np from scipy.optimize import brentq from scipy.interpo

我正在寻找一种在样条曲线上采样点的快速方法,这样通过这些点的多边形或线串不会超过原始样条曲线的给定弦误差。我有一个我不久前写的算法,可以在图片中产生结果(如果感兴趣,请参阅下面的代码;我不希望有人仔细研究它)。它工作正常,但速度不是很快(在我的计算机上生成该图形大约需要2秒钟)。是否有更简单的东西,可能内置在numpy或scipy中,可以实现这一点

谢谢

import numpy as np
from scipy.optimize import brentq
from scipy.interpolate import splev

def get_rhos(ts, tck):
    """Get (signed) rhos (1/rad of curvature) for a given
    set of t values.
    """
    tanvs = np.array(splev(ts, tck, der=1)).T
    accvs = np.array(splev(ts, tck, der=2)).T
    if tanvs.ndim == 1:
        tanvs = tanvs.reshape(1, -1)
        accvs = accvs.reshape(1, -1)

    crossp = np.cross(accvs, tanvs, axis=1)
    tanvms = np.array([np.sqrt(np.dot(v, v)) for v in tanvs])
    rhos = crossp / tanvms**3
    return rhos

def calc_rad(pt0, pt1, pt2, calcdrop=False):
    """Calculate a radius from three points on the arc.
    Lifted from http://www.physicsforums.com/showthread.php?t=173847
    """
    pt0 = np.array(pt0)
    pt1 = np.array(pt1)
    pt2 = np.array(pt2)

    v0 = pt1 - pt0
    v1 = pt2 - pt0
    v2 = pt2 - pt1
    a = np.sqrt(np.dot(v0, v0))
    b = np.sqrt(np.dot(v1, v1))
    c = np.sqrt(np.dot(v2, v2))
    R = (a*b*c) / np.sqrt(  2 * a**2 * b**2
                          + 2 * b**2 * c**2
                          + 2 * c**2 * a**2
                          - a**4 - b**4 - c**4)
    if calcdrop:
        # Calculate arc drop
        drop = R - np.sqrt(R**2 - (b/2.)**2)
        return R, drop
    else:
        return R

def chordal_sample(tck, chordaltol, oversample=10):
    """Given a spline definition and a chordal tolerance
    (intol/outol), get the t-values for the spline such
    that, when adjacent points are connected, the chordal
    tolerance is not violated.

    Accomplishes this by bracketing a solution, then using
    the brentq solver to find the point where the chordal
    error equals the chordal tolerance.

    Note that a few extra points may be inserted where there
    are inflections in the cubic; these are sometimes missed
    by the arc-radius-calculating portion of the code.
    """

    # This is the function we'll need when we have to
    # go searching for the answer via brentq
    def makeerrfunc(st, spt, tck, chordaltol):
        def errfunc(et):
            mt = (st + et) / 2.0
            mpt = np.array(splev(mt, tck))
            ept = np.array(splev(et, tck))
            _, arcdrop = calc_rad(spt, mpt, ept, calcdrop=True)
            diff = arcdrop - chordaltol
            return diff
        return errfunc

    # Make sure we're sampling enough points
    # TODO: How can we be sure?
    ts = np.linspace(0, 1, oversample * len(tck[1][0]))
    newts = [0]
    # Loop through the time values
    for nt in ts:
        st = newts[-1]
        rts = ts[ts > st] # Only consider remaining time values
        # Step through adjacent pairs of time values and find
        # ones that bracket the solution.
        for et0, et1 in zip(rts[0:-1], rts[1:]):
            # Get a 'middle time' that we can use to calc
            # a 'middle point' for our arc calculations
            mt0 = (st + et0) / 2.
            mt1 = (st + et1) / 2.

            # Interpolate points at the critical t values
            ipts = np.array(splev([st, mt0, et0, mt1, et1], tck))
            spt, mpt0, ept0, mpt1, ept1 = ipts.T

            _, arcdrop0 = calc_rad(spt, mpt0, ept0, calcdrop=True)
            _, arcdrop1 = calc_rad(spt, mpt1, ept1, calcdrop=True)

            # Have we bracketed the solution yet? If so, use
            # brentq to find a better one within the bracketed
            # range, then move on to a new start t.
            if arcdrop0 > chordaltol: # Check the initial pair
                errfunc = makeerrfunc(st, spt, tck, chordaltol)
                mdt = brentq(errfunc, st, et0)
                newts.append(mdt)
                break
            if arcdrop0 <= chordaltol and arcdrop1 > chordaltol:
                errfunc = makeerrfunc(st, spt, tck, chordaltol)
                mdt = brentq(errfunc, et0, et1)
                newts.append(mdt)
                break
            # Check for the existence of an inflection point
            # in the bracketed range by checking the signs
            # of the two calculated curvatures and looking for
            # a reversal.
            if get_rhos(et0, tck)[0] * get_rhos(et1, tck)[0] < 0:
                newts.append((et0 + et1) / 2.0)
                break
        if et1 == 1.0: # No more points to try
            newts.append(1.0)
            break
    return newts

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from scipy.interpolate import splprep

    # Create a hi-res sample spline. Start with some
    # low-res points and then resample at a higher
    # res.
    XY = np.array([[0.0,  1.0,  2.0, 3.0, 2.0, 1.0, 0.0],
                   [0.0, -1.0, -0.5, 0.0, 2.5, 1.2, 2.0]])
    tck, u = splprep(XY, s=0)
    XY = splev(np.linspace(0, 1, 400), tck)
    tck, u = splprep(XY, s=0)

    # Get a set of t values that will plot out
    # a linestring with no more than 0.1 chordal
    # error to the original.
    ts = chordal_sample(tck, 0.1)

    fig, ax = plt.subplots()
    # Plot the hi-res spline
    ax.plot(*XY)
    # Plot the approximated spline
    ax.plot(*np.array(splev(ts, tck)), marker='o')

    ax.axis('equal')
    ax.grid()
    plt.show()

将numpy导入为np
从scipy.optimize导入brentq
从scipy.interpolate导入splev
def get_rhos(ts、tck):
“”“获取给定曲线的(有符号)ρ(曲率的1/rad)。”
t值的集合。
"""
tanvs=np.array(splev(ts,tck,der=1)).T
accvs=np.array(splev(ts,tck,der=2)).T
如果tanvs.ndim==1:
tanvs=tanvs.重塑(1,-1)
accvs=accvs.重塑(1,-1)
交叉点=np.交叉点(ACCV,tanvs,轴=1)
tanvms=np.array([np.sqrt(np.dot(v,v))表示tanvs中的v])
rhos=交叉点/tanvms**3
返回菱形
def calc_rad(pt0、pt1、pt2、calcdrop=False):
“”“从圆弧上的三个点计算半径。
从http://www.physicsforums.com/showthread.php?t=173847
"""
pt0=np.数组(pt0)
pt1=np.数组(pt1)
pt2=np.数组(pt2)
v0=pt1-pt0
v1=pt2-pt0
v2=pt2-pt1
a=np.sqrt(np.dot(v0,v0))
b=np.sqrt(np.dot(v1,v1))
c=np.sqrt(np.dot(v2,v2))
R=(a*b*c)/np.sqrt(2*a**2*b**2)
+2*b**2*c**2
+2*c**2*a**2
-a**4-b**4-c**4)
如果calcdrop:
#计算弧降
下降=R-np.sqrt(R**2-(b/2.)**2)
返回R,下降
其他:
返回R
def chordal_样本(tck、chordaltol、过样本=10):
“”“给定样条曲线定义和弦公差
(intol/outol),获取样条曲线的t值,如
当相邻点相连时,弦
宽容不受侵犯。
通过将解决方案括起来,然后使用
使用brentq解算器查找弦
误差等于弦公差。
请注意,可能会在存在的位置插入一些额外的点
是立方体中的屈折变化;这些变化有时会被忽略
由圆弧半径计算部分的代码实现。
"""
#这是我们在必要时需要的功能
#通过brentq搜索答案
def MakeerFunc(st、spt、tck、CHRDALTOL):
def errfunc(et):
mt=(st+et)/2.0
mpt=np.数组(splev(mt,tck))
ept=np.阵列(splev(et,tck))
_,arcdrop=calc_rad(标准贯入度、最大贯入度、最大贯入度、最大贯入度、最大贯入度、最大贯入度)
diff=电弧降-氯度醇
回差
返回errfunc
#确保我们采样的点足够多
#托多:我们怎么能确定?
ts=np.linspace(0,1,过采样*len(tck[1][0]))
牛顿=[0]
#循环遍历时间值
对于ts中的nt:
st=牛顿[-1]
RTS= TS[TS> ST]α=只考虑剩余时间值
#单步遍历相邻的时间值对并查找
#那些支持解决方案的人。
对于et0,邮政编码中的et1(rts[0:-1],rts[1:]):
#得到一个“中间时间”,我们可以用它来计算
#圆弧计算的“中间点”
mt0=(st+et0)/2。
mt1=(st+et1)/2。
#在临界t值处插值点
ipts=np.阵列(splev([st,mt0,et0,mt1,et1],tck))
spt,mpt0,ept0,mpt1,ept1=ipts.T
_,arcdrop0=calc_rad(spt、mpt0、ept0、calcdrop=True)
_,arcdrop1=calc_rad(spt、mpt1、ept1、calcdrop=True)
#我们把解决方案括起来了吗?如果是,请使用
#brentq想在括号内找到一个更好的
#范围,然后转到新的起点t。
如果arcdrop0>chordaltol:#检查初始对
errfunc=makeerrfunc(st、spt、tck、chordaltol)
mdt=brentq(errfunc,st,et0)
newts.append(mdt)
打破
如果arcdrop0 chordaltol:
errfunc=makeerrfunc(st、spt、tck、chordaltol)
mdt=brentq(errfunc,et0,et1)
newts.append(mdt)
打破
#检查是否存在拐点
#通过检查标志在括号内的范围内
#在两个计算曲率的
#逆转。
如果get_rhos(et0,tck)[0]*get_rhos(et1,tck)[0]<0:
牛顿附加((et0+et1)/2.0)
打破
如果et1==1.0:#没有更多的分数可以尝试
newts.append(1.0)
打破
返回蝾螈
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
将matplotlib.pyplot作为plt导入
从scipy.interpolate导入splprep
#创建高分辨率采样样条线。从一些开始
#低分辨率点,然后以更高的分辨率重新采样
#res。
XY=np.数组([[0.0,1.0,2.0,3.0,2.0,1.0,0.0],
[0.0, -1.0, -0.5, 0.0, 2.5, 1.2, 2.0]])
tck,u=splprep(XY,s=0)
XY=splev(np.linspace(0,1400),tck)
tck,u=splprep(XY,s=0)
#获取一组将绘制出来的t值
#不超过0.1和弦的线串
#原稿有误。
ts=弦_样本(tck,0.1)
图,ax=plt.子批次()
#绘制高分辨率样条曲线
轴图(*XY)
#绘制近似样条曲线
ax.plot(*np.array(splev(ts,tck)),marker='o')
ax.轴(“相等”)
ax.grid()
plt.show()

我在我最喜欢的模块之一Shapely中找到了一个很好的解决方案。对于形状优美的几何对象,有一种
simplify()
方法,它采用公差,并为相同的0.1值生成公差:

在我看来更好,只需要1.65毫秒(约1200倍的加速)