三次样条/曲线拟合

时间:2015-07-10 15:07:40

标签: computer-vision curve-fitting spline cubic-spline

我需要确定Illumintaion变化的参数,这是由这个连续的分段多项式C(t)定义的,其中f(t)是由两个边界点(t1,c)定义的三次曲线和( t2,0),f'(t1)= 0且f'(t2)= 0。 Model Binding To A List

enter image description here

enter image description here

强度曲线从阴影边界的法线上采样,它看起来像这样:

每行都是样本,显示照度变化。所以X是列数,Y是像素强度。

enter image description here

我有这样的真实数据(从所有样本中抽取一个样本): enter image description here

根本我有N个样本,我需要确定参数(c,t1,t2)

我该怎么做?

我尝试通过在Matlab中求解线性方程来做到这一点:

avr_curve是平均曲线,通过对所有样本求平均得到。

f(x)= x ^ 3 + a2 * x ^ 2 + a1 * x1 + a0是立方函数

%t1,t2 selected by hand
t1= 10;
t2= 15;

offset=10;
avr_curve= [41, 40, 40, 41, 41, 42, 42, 43, 43, 43, 51, 76, 98, 104, 104, 103, 104, 105, 105, 107, 105];
%gradx= convn(avr_curve,[-1 1],'same');

A= zeros(2*offset+1,3);
%b= zeros(2*offset+1,1);
b= avr_curve';
%for i= 1:2*offset+1
for i=t1:t2
  i
  x= i-offset-1
  A(i,1)=  x^2; %a2
  A(i,2)=  x; %a1
  A(i,3)=  1; %a0 
  b(i,1)= b(i,1)-x^3;
end

u= A\b;

figure,plot(avr_curve(t1:t2))


%estimated cubic curve
for i= 1:2*offset+1 
  x= i-offset-1;
  fx(i)=x^3+u(1)*x^2+u(2)*x+u(3);
end

figure,plot(fx(t1:t2))

[t1 t2]上avr_curve的一部分 enter image description here

我得到的立方曲线(看起来不像avr_curve) enter image description here

所以我做错了什么?

更新 似乎我的错误是因为我使用3个变量模拟三次多项式:

f(x)= x^3+a2*x^2+a1*x1+a0 - 3 variables

然后我使用4个变量似乎一切正常:

f(x)= a3*x^3+a2*x^2+a1*x1+a0 - 4 variables 

以下是Matlab中的代码:

%defined by hand
t1= 10;
t2= 14;

avr_curve= [41, 40, 40, 41, 41, 42, 42, 43, 43, 43, 51, 76, 98, 104, 104, 103, 104, 105, 105, 107, 105];
x=         [1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,  15,  16,  17,  18,  19,  20,  21];
%x=        [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; %real x axis

%%%model 1
%%f(x)= x^3+a2*x^2+a1*x1+a0 - 3 variables
%A= zeros(4,3);
%b= [43  104]';
%%cubic equation at t1 
%A(1,1)= t1^2; %a2
%A(1,2)= t1; %a1
%A(1,3)= 1; %a0
%b(1,1)= b(1,1)-t1^3;
%%cubic equation at t2
%A(2,1)= t2^2; %a2
%A(2,2)= t2; %a1
%A(2,3)= 1; %a0
%b(2,1)= b(2,1)-t1^3;
%%1st derivative at t1
%A(3,1)= 2*t1; %a2
%A(3,2)= 1; %a1
%A(3,3)= 0; %a0
%b(3,1)= -3*t1^2;
%%1st derivative at t2
%A(4,1)= 2*t2; %a2
%A(4,2)= 1; %a1
%A(4,3)= 0; %a0
%b(4,1)= -3*t2^2;

%model 2
%f(x)= a3*x^3+a2*x^2+a1*x1+a0 - 4 variables
A= zeros(4,4);
b= [43  104]';
%cubic equation at t1 
A(1,1)= t1^3; %a3
A(1,2)= t1^2; %a2
A(1,3)= t1; %a1
A(1,4)= 1; %a0
b(1,1)= b(1,1);
%cubic equation at t2
A(2,1)= t2^3; %a3
A(2,2)= t2^2; %a2
A(2,3)= t2; %a1
A(2,4)= 1; %a0
b(2,1)= b(2,1);
%1st derivative at t1
A(3,1)= 3*t1^2; %a3
A(3,2)= 2*t1; %a2
A(3,3)= 1; %a1
A(3,4)= 0; %a0
b(3,1)= 0;
%1st derivative at t2
A(4,1)= 3*t2^2; %a3
A(4,2)= 2*t2; %a2
A(4,3)= 1; %a1
A(4,4)= 0; %a0
b(4,1)= 0;

size(A)
size(b)
u= A\b;
u

%estimated cubic curve
%dx=[1:21]; % global view
dx=t1-1:t2+1; % local view in [t1 t2]
for x= dx
  %fx(x)=x^3+u(1)*x^2+u(2)*x+u(3); % model 1
  fx(x)= u(1)*x^3+u(2)*x^2+u(3)*x+u(4); % model 2
end

err= 0;
for x= dx
  err= err+(fx(x)-avr_curve(x))^2;
end

err

figure,plot(dx,avr_curve(dx),dx,fx(dx))

间隔[t1-1 t2 + 1]的样条 enter image description here

并且在完整的时间间隔

enter image description here

1 个答案:

答案 0 :(得分:2)

声明

我无法保证下面给出的代码或方法的正确性,在使用任何代码或方法之前始终使用您的批评意义。

0。定义问题

你有这个分段定义的功能

Piecewise function to recover

其中 f(t)是一个三次函数,为了唯一识别它,给出了以下附加条件

Additional condition on cubic piece

您希望恢复参数 t1 t2 sigma 的最佳值,以便使用给定的点集最小化错误。

这基本上是符合least squares意义的曲线。

1参数化 f(t)三次函数

为了计算候选 Cl(t)函数与我们需要计算 f(t)的点集之间的误差,它的一般形式(正在一个立方体是

Gerenal cubic

所以我们似乎还有四个额外的参数需要考虑。实际上,这些参数完全由 free 三个参数 t1 t2 sigma 定义。
重要的是不要将 free 参数与依赖参数混淆。

考虑到 f(t)的附加条件,我们可以设置这个线性系统

Solving linear system for cubic

给出一个解决方案(按预期)

Parameters of the cubic

这告诉我们如何在给定三个自由参数的情况下计算三次参数 这样 Cl(t)就完全确定了,现在是找到最佳候选人的时候了。

2最小化错误

我现在通常会选择最小的方格 由于这不是线性函数,因此没有用于计算 sigma t1 t2 的封闭形式。
然而,有一些数值方法,如Gauss-Newton

然而,无论如何,都需要根据三个参数计算偏导数 我不知道如何根据 t1 之类的分离参数来计算导数。

我搜索过MathSE并找到了解决同一问题的this问题,但没有人回答。

没有偏导数,最小二乘法就结束了。

所以我采取了更实际的方法并在C中实现了一个强力函数,它可以尝试每个可能的参数三元组并返回最佳匹配。

3蛮力功能

鉴于问题的性质,样本数量的结果为 O (n ^ 2)。

算法如下:将样本集分为三部分,第一部分是 t1 之前的一部分, t1 之间的第二部分和 t2 以及 t2 之后的最后一个点。

第一部分仅用于计算 sigma sigma 只是第1部分中各点的算术平均值。

t1 t2 通过一个循环计算, t1 设置为原始点集中的每个可能点,从第二个开始继续前进 对于 t1 的每个选项, t2 设置为 t1 之后的每个可能点。

在每次迭代时计算错误,如果它是有史以来的最小值,则保存使用的参数。

错误是计算机作为残差的绝对值,因为绝对值应该快(肯定比平方快)并且它符合度量的目的。

4代码

#include <stdio.h>
#include <math.h>

float point_on_curve(float sigma, float t1, float t2, float t)
{
    float a,b,c,d, K;

    if (t <= t1)
        return sigma;

    if (t >= t2)
        return 0;

    K = (t1-t2)*(t1-t2)*(t1-t2);
    a = -2*sigma/K;
    b = 3*sigma*(t1+t2)/K;
    c = -6*sigma*t1*t2/K;
    d = sigma*t2*t2*(3*t1-t2)/K;

    return a*t*t*t + b*t*t + c*t + d;
}

float compute_error(float sigma, float t1, float t2, int s, int dx, int* data, int len)
{
    float error=0;
    unsigned int i;

    for (i = 0; i < len; i++)
        error += fabs(point_on_curve(sigma, t1, t2, s+i*dx)- data[i]);

    return error;
}

/* 
 * s is the starting time of the samples set
 * dx is the separation in time between two sample (a.k.a. sampling period)
 * data is the array of samples
 * len  is the number of samples
 * sigma, t1, t2 are pointers to output parameters computed
 *
 * return 1 if not enough (3) samples, 0 if everything went ok.
 */
int curve_fit(int s, int dx, int* data, unsigned int len, float* sigma, float* t1, float* t2)
{
    float l_sigma = 0;
    float l_t1, l_t2;
    float sum = 0;

    float min_error, cur_error;
    char error_valid = 0;

    unsigned int i, j;

    if (len < 3)
        return 1;

    for (i = 0; i < len; i++)
    {
        /* Compute sigma as the average of points <= i */
        sum += data[i];
        l_sigma = sum/(i+1);

        /* Set t1 as the point i+1 */
        l_t1 = s+(i+1)*dx;

        for (j = i+2; j < len; j++)
        {
            /* Set t2 as the points i+2, i+3, i+4, ... */
            l_t2 = s+j*dx;

            /* Compute the error */
            cur_error = compute_error(l_sigma, l_t1, l_t2, s, dx, data, len);

            if (cur_error < min_error || !error_valid)
            {
                error_valid = 1;
                min_error = cur_error;

                *sigma = l_sigma;
                *t1 = l_t1;
                *t2 = l_t2;
            }
        }
    }

    return 0;
}

int main()
{
    float sigma, t1, t2;
    int data[]={41, 40, 40, 41, 41, 42, 42, 43, 43, 43, 51, 76, 98, 104, 104, 103, 104, 105, 105, 107, 105};
    unsigned int len = sizeof(data)/sizeof(int);
    unsigned int i;


    for (i = 0; i < len; i++)
        data[i] -= 107;             /* Subtract the max */


    if (curve_fit(1,1,data, len, &sigma, &t1, &t2))
        printf("Not enough data!\n");
    else 
        printf("Parameters: sigma = %.3f, t1 = %.3f, t2 = %.3f\n", sigma, t1, t2);

    return 0;


}

请注意 Cl(t)被定义为0作为其右限制,因此代码假设是这种情况。

这就是为什么从每个样本中减去最大值(107),我使用了开头给出的 Cl(t)的定义注意到样本有偏见。

到目前为止,我不打算调整代码,您可以轻松地在问题中添加另一个参数,并在需要时从1重做步骤,或者只是使用最大值翻译样本。

代码的输出是

 Parameters: sigma = -65.556, t1 = 10.000, t2 = 14.000

与给定的点数相匹配,考虑到它被-107垂直平移。