Loading [MathJax]/jax/output/CommonHTML/jax.js

m개의 control point {Qi}가 주어지면  p차의 B-Spline curve는 basis 함수를 Ni,p(t)을 써서

B(t)=m1i=0Ni,p(t)Qi

로 표현할 수 있다. 이를 이용해서 일정한 순서로 샘플링된 평면상의 N개의 입력점 {Pi}을 찾아보자. B-spline 곡선을 이용하면 이 문제는 control 점을 찾는 문제가 된다. 곡선이 입력점을 잘 표현하기 위해서는 곡선과 입력점과의 차이를 최소로 하는 control 점을 찾아야 한다:

Q=argmin(L),L:=N1k=0|B(tk)Pk|2

여기서 {tk|k=0,1,...,N1}는 입력점이 얻어지는 sample time으로 0=t0t1...tN1=1로 rescale 할 수 있다. 

행렬을 이용해서 식을 좀 더 간결하게 표현할 수 있다. 

L=N1k=0|B(tk)Pk|2=N1k=0|m1i=0Ni,p(tk)QiPk|2=N1k=0|m1i=0AkiQiPk|2,Aki=Ni,p(tk)

로 쓸 수 있으므로, ˆQ=(Q0,Q1,...,Qm1)t, ˆP=(P0,P1,...,PN1)t인 벡터, 그리고 ˆA=(Aki)인 행렬로 보면

L=|ˆAˆQˆP|2=(ˆQtˆAtˆPt)(ˆAˆQˆP).

위 식의 값을 최소로 하는 최소자승해는 ˆQt에 대한 미분 값이 0인 벡터를 찾으면 된다; LˆQt=ˆAtˆAˆQˆAtˆP=0. 이 행렬 방정식의 해는

ˆQ=(ˆAtˆA)1(ˆAtˆP) 로 표현된다. ˆAtˆA가 (banded) real symmetric (m×m) 행렬이므로 Cholesky decomposion을 사용하면 쉽게 해를 구할 수 있다. ˆA가 banded matrix 형태를 가지는 이유는 basis가 local support에서만 0이 아닌 값을 취하기 때문이다. 

open b-spline(cubic)

std::vector<CfPt> BSplineFit_LS(const std::vector<CfPt>& data,
                                const int degree,    // cubic(3); 
                                const int nc)        // num of control points;
{
    // open b-spline;
    std::vector<double> knot((nc - 1) + degree + 2);
    for (int i = 0; i <= nc + degree; i++) knot[i] = i;
    
    std::vector<double> t(data.size());       // parameter;
    double scale = (knot[nc] - knot[degree]) / (data.size()-1);
    for (int i = data.size(); i-->0;) 
        t[i] = knot[degree] + scale * i;

    // design matrix;
    std::vector<double> A(ndata * nc); 
    for (int i = data.size(); i-->0;)
        for (int j = 0; j < nc; j++)
            A[i*nc + j] = Basis(j, degree, &knot[0], t[i]); //A(i,j)=N_j(t_i)

    // scattering matrix: S = A^t * A; real-symmetric matrix;
    std::vector<double> S(nc * nc); 
    for (int i = 0; i < nc; ++i)
        for (int j = i; j < nc; ++j) {
            double s = 0;
            for (int k = data.size(); k-->0;)
                s += A[k*nc + i] * A[k*nc + j];
            S[i*nc + j] = s;
        }
    // fill lower triangle;
    for (int i = 0; i < nc; ++i) 
        for (int j = 0; j < i; j++) 
            S[i*nc + j] = S[j*nc + i];
    
    std::vector<CfPt> X(nc);
    // X = A^t * P;
    for (int i = 0; i < nc; i++) {
        double sx = 0, sy = 0;
        for (int k = data.size(); k-->0;) {
            sx += A[k*nc + i] * data[k].x;
            sy += A[k*nc + i] * data[k].y;
        };
        X[i] =CfPt(sx, sy);
    };
    // psinv(A, n) = inverse of real symmetric matrix A;
    // ccmath-2.2.1 version;
    if (0 == psinv(&S[0], nc)) {
        std::vector<CfPt> Q(nc);
        for (int i = 0; i < nc; ++i) {
            souble sx = 0, sy = 0;
            for (int k = 0; k < nc; ++k) {
            	sx += S[i*nc + k] * X[k].x;
                sy += S[i*nc + k] * X[k].y;
            }
            Q[i] = CfPt(sx, sy);
        }
        return Q;  // estimated control points;
    }
    return std::vector<CfPt> ();
};

**네이버 블로그 이전;

 
728x90

'Computational Geometry' 카테고리의 다른 글

Approximate Distance Between Ellipse and Point  (0) 2024.03.08
Distance from a Point to an Ellipse  (0) 2024.03.06
Closest Pair of Points  (0) 2021.04.27
DDA Algorithm  (0) 2021.04.25
B-Spline  (1) 2021.04.25
,