Fitting a closed curve to a set of points

后端 未结 4 2010
清歌不尽
清歌不尽 2020-11-27 04:04

I have a set of points pts which form a loop and it looks like this:

\"enter

相关标签:
4条回答
  • 2020-11-27 04:47

    Actually, you were not far from the solution in your question.

    Using scipy.interpolate.splprep for parametric B-spline interpolation would be the simplest approach. It also natively supports closed curves, if you provide the per=1 parameter,

    import numpy as np
    from scipy.interpolate import splprep, splev
    import matplotlib.pyplot as plt
    
    # define pts from the question
    
    tck, u = splprep(pts.T, u=None, s=0.0, per=1) 
    u_new = np.linspace(u.min(), u.max(), 1000)
    x_new, y_new = splev(u_new, tck, der=0)
    
    plt.plot(pts[:,0], pts[:,1], 'ro')
    plt.plot(x_new, y_new, 'b--')
    plt.show()
    

    enter image description here

    Fundamentally, this approach not very different from the one in @Joe Kington's answer. Although, it will probably be a bit more robust, because the equivalent of the i vector is chosen, by default, based on the distances between points and not simply their index (see splprep documentation for the u parameter).

    0 讨论(0)
  • 2020-11-27 04:50

    To fit a smooth closed curve through N points you can use line segments with the following constraints:

    • Each line segment has to touch its two end points (2 conditions per line segment)
    • For each point the left and right line segment have to have the same derivative (2 conditions per point == 2 conditions per line segment)

    To be able to have enough freedom for in total 4 conditions per line segment the equation of each line segment should be y = ax^3 + bx^2 + cx + d. (so the derivative is y' = 3ax^2 + 2bx + c)

    Setting the conditions as suggested would give you N * 4 linear equations for N * 4 unknowns (a1..aN, b1..bN, c1..cN, d1..dN) solvable by matrix inversion (numpy).

    If the points are on the same vertical line special (but simple) handling is required since the derivative will be "infinite".

    0 讨论(0)
  • 2020-11-27 04:59

    Your problem is because you're trying to work with x and y directly. The interpolation function you're calling assumes that the x-values are in sorted order and that each x value will have a unique y-value.

    Instead, you'll need to make a parameterized coordinate system (e.g. the index of your vertices) and interpolate x and y separately using it.

    To start with, consider the following:

    import numpy as np
    from scipy.interpolate import interp1d # Different interface to the same function
    import matplotlib.pyplot as plt
    
    #pts = np.array([...]) # Your points
    
    x, y = pts.T
    i = np.arange(len(pts))
    
    # 5x the original number of points
    interp_i = np.linspace(0, i.max(), 5 * i.max())
    
    xi = interp1d(i, x, kind='cubic')(interp_i)
    yi = interp1d(i, y, kind='cubic')(interp_i)
    
    fig, ax = plt.subplots()
    ax.plot(xi, yi)
    ax.plot(x, y, 'ko')
    plt.show()
    

    enter image description here

    I didn't close the polygon. If you'd like, you can add the first point to the end of the array (e.g. pts = np.vstack([pts, pts[0]])

    If you do that, you'll notice that there's a discontinuity where the polygon closes.

    enter image description here

    This is because our parameterization doesn't take into account the closing of the polgyon. A quick fix is to pad the array with the "reflected" points:

    import numpy as np
    from scipy.interpolate import interp1d 
    import matplotlib.pyplot as plt
    
    #pts = np.array([...]) # Your points
    
    pad = 3
    pts = np.pad(pts, [(pad,pad), (0,0)], mode='wrap')
    x, y = pts.T
    i = np.arange(0, len(pts))
    
    interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad))
    
    xi = interp1d(i, x, kind='cubic')(interp_i)
    yi = interp1d(i, y, kind='cubic')(interp_i)
    
    fig, ax = plt.subplots()
    ax.plot(xi, yi)
    ax.plot(x, y, 'ko')
    plt.show()
    

    enter image description here

    Alternately, you can use a specialized curve-smoothing algorithm such as PEAK or a corner-cutting algorithm.

    0 讨论(0)
  • 2020-11-27 04:59

    Using the ROOT Framework and the pyroot interface I was able to generate the following image Drawing Using pyroot

    With the following code(I converted your data to a CSV called data.csv so reading it into ROOT would be easier and gave the columns titles of xp,yp)

    from ROOT import TTree, TGraph, TCanvas, TH2F
    
    c1 = TCanvas( 'c1', 'Drawing Example', 200, 10, 700, 500 )
    t=TTree('TP','Data Points')
    t.ReadFile('./data.csv')
    t.SetMarkerStyle(8)
    t.Draw("yp:xp","","ACP")
    c1.Print('pydraw.png')
    
    0 讨论(0)
提交回复
热议问题