복붙노트

[PYTHON] SciPy를 사용한 베 지어 커브 피팅

PYTHON

SciPy를 사용한 베 지어 커브 피팅

나는 2D 곡선을 근사하는 점들의 집합을 가지고있다. numpoint와 scipy를 사용하여 Python을 사용하여 점에 거의 맞는 입방 베 지어 경로를 찾고 싶습니다. 여기서 두 끝점의 정확한 좌표를 지정하고 다른 두 제어점의 좌표를 반환합니다.

처음에 scipy.interpolate.splprep ()가 내가 원하는 것을 할 수 있다고 생각했지만, 보간을 원한다고 가정 할 때 데이터 포인트 각각을 통과하도록 곡선을 강제하는 것처럼 보입니다. 나는 내가 잘못된 길을 가고 있다고 생각할 것이다.

내 질문은이 질문과 유사합니다 : 베 지어 커브를 데이터 세트에 맞추려면 어떻게해야합니까? 단 numpy를 사용하고 싶지 않다고 말한 것을 제외하고는. 필자가 선호하는 것은 scipy 나 numpy로 이미 구현 된 것을 찾아내는 것입니다. 그렇지 않으면 numpy를 사용하여 해당 질문에 대한 대답 중 하나에서 링크 된 알고리즘을 구현할 계획입니다. 디지털화 된 곡선을 자동으로 맞추는 알고리즘 (pdf. 페이지 622).

어떤 제안을 해주셔서 감사합니다!

편집 : 큐빅 베 지어 곡선 모든 지점을 통과하는 보장되지 않습니다 이해; 두 개의 주어진 끝점을 통과하고 지정된 내부 지점에 가능한 한 근접한 끝점을 원합니다.

해결법

  1. ==============================

    1.다음은 피팅 포인트를위한 파이썬 코드입니다.

    다음은 피팅 포인트를위한 파이썬 코드입니다.

    '''least square qbezier fit using penrose pseudoinverse
        >>> V=array
        >>> E,  W,  N,  S =  V((1,0)), V((-1,0)), V((0,1)), V((0,-1))
        >>> cw = 100
        >>> ch = 300
        >>> cpb = V((0, 0))
        >>> cpe = V((cw, 0))
        >>> xys=[cpb,cpb+ch*N+E*cw/8,cpe+ch*N+E*cw/8, cpe]            
        >>> 
        >>> ts = V(range(11), dtype='float')/10
        >>> M = bezierM (ts)
        >>> points = M*xys #produces the points on the bezier curve at t in ts
        >>> 
        >>> control_points=lsqfit(points, M)
        >>> linalg.norm(control_points-xys)<10e-5
        True
        >>> control_points.tolist()[1]
        [12.500000000000037, 300.00000000000017]
    
    '''
    from numpy import array, linalg, matrix
    from scipy.misc import comb as nOk
    Mtk = lambda n, t, k: t**(k)*(1-t)**(n-k)*nOk(n,k)
    bezierM = lambda ts: matrix([[Mtk(3,t,k) for k in range(4)] for t in ts])
    def lsqfit(points,M):
        M_ = linalg.pinv(M)
        return M_ * points
    

    일반적으로 베 지어 곡선 체크 아웃 애니메이션 베지 및 베 지어 인포

  2. ==============================

    2.numpy로 베 지어 곡선을 그리는 방법은 다음과 같습니다.

    numpy로 베 지어 곡선을 그리는 방법은 다음과 같습니다.

    import numpy as np
    from scipy.misc import comb
    
    def bernstein_poly(i, n, t):
        """
         The Bernstein polynomial of n, i as a function of t
        """
    
        return comb(n, i) * ( t**(n-i) ) * (1 - t)**i
    
    
    def bezier_curve(points, nTimes=1000):
        """
           Given a set of control points, return the
           bezier curve defined by the control points.
    
           points should be a list of lists, or list of tuples
           such as [ [1,1], 
                     [2,3], 
                     [4,5], ..[Xn, Yn] ]
            nTimes is the number of time steps, defaults to 1000
    
            See http://processingjs.nihongoresources.com/bezierinfo/
        """
    
        nPoints = len(points)
        xPoints = np.array([p[0] for p in points])
        yPoints = np.array([p[1] for p in points])
    
        t = np.linspace(0.0, 1.0, nTimes)
    
        polynomial_array = np.array([ bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints)   ])
    
        xvals = np.dot(xPoints, polynomial_array)
        yvals = np.dot(yPoints, polynomial_array)
    
        return xvals, yvals
    
    
    if __name__ == "__main__":
        from matplotlib import pyplot as plt
    
        nPoints = 4
        points = np.random.rand(nPoints,2)*200
        xpoints = [p[0] for p in points]
        ypoints = [p[1] for p in points]
    
        xvals, yvals = bezier_curve(points, nTimes=1000)
        plt.plot(xvals, yvals)
        plt.plot(xpoints, ypoints, "ro")
        for nr in range(len(points)):
            plt.text(points[nr][0], points[nr][1], nr)
    
        plt.show()
    
  3. ==============================

    3.베 지어 곡선은 사용자가 지정한 모든 점을 통과하지는 않습니다. 제어점은 임의적입니다 (특정 알고리즘을 찾을 수있는 특정 알고리즘이 없으므로 사용자가 직접 선택할 수 있음). 방향 만 커브를 당깁니다.

    베 지어 곡선은 사용자가 지정한 모든 점을 통과하지는 않습니다. 제어점은 임의적입니다 (특정 알고리즘을 찾을 수있는 특정 알고리즘이 없으므로 사용자가 직접 선택할 수 있음). 방향 만 커브를 당깁니다.

    여러분이 제공하는 모든 점을 통과하는 커브를 원한다면, 자연적인 입체 스플라인과 같은 것이 필요합니다. 그리고 그것들의 한계 때문에 (x 좌표를 증가 시키거나 무한대로 움직여야합니다) 아마도 파라 메트릭 자연 입방 스플라인을 원할 것입니다.

    다음은 멋진 자습서입니다.

    큐빅 스플라인

    파라 메트릭 큐빅 스플라인

  4. ==============================

    4.짧은 대답 : 베 지어 커브가 작동하지 않기 때문에 그렇지 않습니다. 더 긴 대답 : 대신 Catmull-Rom 스플라인을보십시오. 그것들은 매우 쉽게 형성 할 수 있습니다 (시작점과 끝점을 제외한 모든 점 P의 접선 벡터는 {P-1, P + 1} 선과 평행하므로 프로그램하기 쉽습니다) 항상 통과합니다 모든 제어점에 의해 설정된 볼록 선체 내부의 "어딘가"를 보간하는 베 지어 곡선과는 달리,이를 정의하는 점입니다.

    짧은 대답 : 베 지어 커브가 작동하지 않기 때문에 그렇지 않습니다. 더 긴 대답 : 대신 Catmull-Rom 스플라인을보십시오. 그것들은 매우 쉽게 형성 할 수 있습니다 (시작점과 끝점을 제외한 모든 점 P의 접선 벡터는 {P-1, P + 1} 선과 평행하므로 프로그램하기 쉽습니다) 항상 통과합니다 모든 제어점에 의해 설정된 볼록 선체 내부의 "어딘가"를 보간하는 베 지어 곡선과는 달리,이를 정의하는 점입니다.

  5. ==============================

    5.Mike Kamermans가 말한 것은 사실이지만, 알고있는 한 catmull-rom 스플라인은 큐빅 베 지어로 정의 할 수 있습니다. 따라서 입방체로 작동하는 라이브러리 만있는 경우에도 catmull-rom 스플라인을 수행 할 수 있어야합니다.

    Mike Kamermans가 말한 것은 사실이지만, 알고있는 한 catmull-rom 스플라인은 큐빅 베 지어로 정의 할 수 있습니다. 따라서 입방체로 작동하는 라이브러리 만있는 경우에도 catmull-rom 스플라인을 수행 할 수 있어야합니다.

  6. ==============================

    6.@keynesiancross는 "변수가 무엇인지에 대한 [Roland] 코드의 주석"을 요구하고 다른 사람들은 명시된 문제를 완전히 놓쳤습니다. Roland는 베 지어 곡선을 입력으로 사용하여 완벽한 일치를 얻었으므로 문제와 (적어도 나에게는) 솔루션을 이해하는 것이 더 어려워졌습니다. 보간과의 차이점은 잔차를 남기는 입력에 대해보기가 쉽습니다. 바꿔 말하면 코드 및 비 베 지어 입력이 모두 발생하며 예기치 않은 결과가 발생합니다.

    @keynesiancross는 "변수가 무엇인지에 대한 [Roland] 코드의 주석"을 요구하고 다른 사람들은 명시된 문제를 완전히 놓쳤습니다. Roland는 베 지어 곡선을 입력으로 사용하여 완벽한 일치를 얻었으므로 문제와 (적어도 나에게는) 솔루션을 이해하는 것이 더 어려워졌습니다. 보간과의 차이점은 잔차를 남기는 입력에 대해보기가 쉽습니다. 바꿔 말하면 코드 및 비 베 지어 입력이 모두 발생하며 예기치 않은 결과가 발생합니다.

    import matplotlib.pyplot as plt
    import numpy as np
    from scipy.special import comb as n_over_k
    Mtk = lambda n, t, k: t**k * (1-t)**(n-k) * n_over_k(n,k)
    BézierCoeff = lambda ts: [[Mtk(3,t,k) for k in range(4)] for t in ts]
    
    fcn = np.log
    tPlot = np.linspace(0. ,1. , 81)
    xPlot = np.linspace(0.1,2.5, 81)
    tData = tPlot[0:81:10]
    xData = xPlot[0:81:10]
    data = np.column_stack((xData, fcn(xData))) # shapes (9,2)
    
    Pseudoinverse = np.linalg.pinv(BézierCoeff(tData)) # (9,4) -> (4,9)
    control_points = Pseudoinverse.dot(data)     # (4,9)*(9,2) -> (4,2)
    Bézier = np.array(BézierCoeff(tPlot)).dot(control_points)
    residuum = fcn(Bézier[:,0]) - Bézier[:,1]
    
    fig, ax = plt.subplots()
    ax.plot(xPlot, fcn(xPlot),   'r-')
    ax.plot(xData, data[:,1],    'ro', label='input')
    ax.plot(Bézier[:,0],
            Bézier[:,1],         'k-', label='fit')
    ax.plot(xPlot, 10.*residuum, 'b-', label='10*residuum')
    ax.plot(control_points[:,0],
            control_points[:,1], 'ko:', fillstyle='none')
    ax.legend()
    fig.show()
    

    이는 fcn = np.cos에서는 잘 작동하지만 로그에서는 제대로 작동하지 않습니다. 필자는 제어점을 드래그하여 수행 할 때와 마찬가지로 자유도가 추가 자유도로 제어점의 t 구성 요소를 사용한다고 예상했습니다.

    manual_points = np.array([[0.1,np.log(.1)],[.27,-.6],[.82,.23],[2.5,np.log(2.5)]])
    Bézier = np.array(BézierCoeff(tPlot)).dot(manual_points)
    residuum = fcn(Bézier[:,0]) - Bézier[:,1]
    
    fig, ax = plt.subplots()
    ax.plot(xPlot, fcn(xPlot),   'r-')
    ax.plot(xData, data[:,1],    'ro', label='input')
    ax.plot(Bézier[:,0],
            Bézier[:,1],         'k-', label='fit')
    ax.plot(xPlot, 10.*residuum, 'b-', label='10*residuum')
    ax.plot(manual_points[:,0],
            manual_points[:,1],  'ko:', fillstyle='none')
    ax.legend()
    fig.show()
    

    실패의 원인은 하나의 커브상의 점과 다른 커브상의 가장 가까운 점 사이의 거리 대신 커브상의 점 사이의 거리를 규범이 측정한다는 것입니다.

  7. from https://stackoverflow.com/questions/12643079/b%c3%a9zier-curve-fitting-with-scipy by cc-by-sa and MIT license