algorithm - 如何归一化椭圆傅里叶系数?

标签 algorithm delphi polygon normalization fourier-descriptors

我正在编写一个程序来查找闭合平面曲线的椭圆傅里叶系数 (EFC),但在系数归一化方面遇到了问题。

闭合平面折线 p 由点集 m_points 描述:m_points[i][0] 保持 xi 坐标,m_points i保持 yi 坐标。我从 0 开始到 m_num_points-1。

折线的 EFC 由该算法计算(系数在 EFD 数组中)。

// calc accumulated curve length, delta x, and delta y
dt[0] := 0;
dx[0] := 0;
dy[0] := 0;
tp[0] := 0;
T := 0;
for i:=1 to p.m_num_points-1 do begin
    va := VectorAffineSubtract(p.m_points[i], p.m_points[i-1]);
    dt[i] := VectorLength( va );
    dx[i] := p.m_points[i][0] - p.m_points[i-1][0];
    dy[i] := p.m_points[i][1] - p.m_points[i-1][1];
    tp[i] := tp[i-1] + dt[i];
    T := tp[i];
end;

Tpi := T / (2*PI*PI);
piT := PI*2 / T;

// find elliptic fourier coefficients
// first
An  := 0;
Cn  := 0;
for i:=0 to p.m_num_points-1 do begin
    An := An + (p.m_points[i][0] + p.m_points[i-1][0]) * dt[i] / 2;
    Cn  := Cn + (p.m_points[i][1] + p.m_points[i-1][1]) * dt[i] / 2;
end;
EFD[0][0] := An / T;
EFD[0][1] := 0;
EFD[0][2] := Cn / T;
EFD[0][3] := 0;
// next
for n:=1 to m_num_efd do begin
    Tn  := Tpi / (n*n);
    piTn  := piT * n;
    An  := 0;
    Bn  := 0;
    Cn  := 0;
    Dn  := 0;
    for i:=1 to p.m_num_points-1 do begin
        An := An + dx[i]/dt[i]*( cos(piTn*tp[i]) - cos(piTn*tp[i-1]) );
        Bn := Bn + dx[i]/dt[i]*( sin(piTn*tp[i]) - sin(piTn*tp[i-1]) );
        Cn := Cn + dy[i]/dt[i]*( cos(piTn*tp[i]) - cos(piTn*tp[i-1]) );
        Dn := Dn + dy[i]/dt[i]*( sin(piTn*tp[i]) - sin(piTn*tp[i-1]) );
    end;
    EFD[n][0] := An * Tn;
    EFD[n][1] := Bn * Tn;
    EFD[n][2] := Cn * Tn;
    EFD[n][3] := Dn * Tn;
end;

为了从谐波集中恢复折线,我使用了该算法。

// restore outline
resP := TYPolyline.create();
for i:=0 to p.m_num_points-1 do begin
    xn := EFD[0][0];
    yn := EFD[0][2];
    for n:=1 to m_num_efd do begin
        xn := xn + EFD[n][0] * cos(piT*n*tp[i]) + EFD[n][1] * sin(piT*n*tp[i]);
        yn := yn + EFD[n][2] * cos(piT*n*tp[i]) + EFD[n][3] * sin(piT*n*tp[i]);
    end;
    resP.add_point( xn, yn );
end;

现在我需要规范化 EFD 并将它们放在一个新数组 NEFD 中。我这样做:

// Normalize EFD
An := EFD[0][0];
Bn := EFD[0][1];
Cn := EFD[0][2];
Dn := EFD[0][3];

for n:=0 to m_num_efd do begin
    NEFD[n][0]  := EFD[n][0];
    NEFD[n][1]  := EFD[n][1];
    NEFD[n][2]  := EFD[n][2];
    NEFD[n][3]  := EFD[n][3];
end;

// rotate starting point
angl_o := 0.5 * ArcTan2( 2*(An*Bn + Cn*Dn), An*An + Cn*Cn - Bn*Bn - Dn*Dn );
for n:=1 to m_num_efd+1 do begin
    NEFD[n-1][0]:= EFD[n-1][0]*cos((n)*angl_o) + EFD[n-1][1]*sin((n)*angl_o);
    NEFD[n-1][1]:=-EFD[n-1][0]*sin((n)*angl_o) + EFD[n-1][1]*cos((n)*angl_o);

    NEFD[n-1][2]:= EFD[n-1][2]*cos((n)*angl_o) + EFD[n-1][3]*sin((n)*angl_o);
    NEFD[n-1][3]:=-EFD[n-1][3]*sin((n)*angl_o) + EFD[n-1][3]*cos((n)*angl_o);
end;

// make the semi-major axis parallel to the x-axis
angl_w := ArcTan( NEFD[1][2] / NEFD[1][0] );
cs   := cos(angl_w);
ss   := sin(angl_w);
for n:=1 to m_num_efd do begin
    NEFD[n][0]  := cs*NEFD[n][0] + ss*NEFD[n][2];
    NEFD[n][1]  := cs*NEFD[n][1] + ss*NEFD[n][3];

    NEFD[n][2]  :=-ss*NEFD[n][0] + cs*NEFD[n][2];
    NEFD[n][3]  :=-ss*NEFD[n][1] + cs*NEFD[n][3];
end;

// size invariant
R   := sqrt( NEFD[1][0]*NEFD[1][0] );
for n:=0 to m_num_efd do begin
    NEFD[n][0]  := NEFD[n][0] / R;
    NEFD[n][1]  := NEFD[n][1] / R;
    NEFD[n][2]  := NEFD[n][2] / R;
    NEFD[n][3]  := NEFD[n][3] / R;
end;

当我尝试使半长轴平行于 X 并旋转系数时,我得到了一个丑陋的结果。看起来恢复后的形状是围绕 z 轴旋转的。 (左侧为源形状,右侧为还原形状。)

enter image description here

我的代码有什么问题?

更新

@MBo 成功回答后,需要对代码进行以下修改:

// MODIFIED: change n-1 to n
// rotate starting point
angl_o := 0.5 * ArcTan2( 2*(An*Bn + Cn*Dn), An*An + Cn*Cn - Bn*Bn - Dn*Dn );
for n:=1 to m_num_efd+1 do begin
    NEFD[n][0]:= EFD[n][0]*cos((n)*angl_o) + EFD[n][1]*sin((n)*angl_o);
    NEFD[n][1]:=-EFD[n][0]*sin((n)*angl_o) + EFD[n][1]*cos((n)*angl_o);

    NEFD[n][2]:= EFD[n][2]*cos((n)*angl_o) + EFD[n][3]*sin((n)*angl_o);
    NEFD[n][3]:=-EFD[n][3]*sin((n)*angl_o) + EFD[n][3]*cos((n)*angl_o);
end;

// MODIFIED: change left NEFD to EFD
// make the semi-major axis parallel to the x-axis
angl_w := ArcTan( NEFD[1][2] / NEFD[1][0] );
cs   := cos(angl_w);
ss   := sin(angl_w);
for n:=1 to m_num_efd do begin
    EFD[n][0]  := cs*NEFD[n][0] + ss*NEFD[n][2];
    EFD[n][1]  := cs*NEFD[n][1] + ss*NEFD[n][3];

    EFD[n][2]  :=-ss*NEFD[n][0] + cs*NEFD[n][2];
    EFD[n][3]  :=-ss*NEFD[n][1] + cs*NEFD[n][3];
end;

// ADDED: place normalized EFD into NEFD
for n:=0 to m_num_efd do begin
    NEFD[n][0]  := EFD[n][0];
    NEFD[n][1]  := EFD[n][1];
    NEFD[n][2]  := EFD[n][2];
    NEFD[n][3]  := EFD[n][3];
end;

最佳答案

可能原因:
在此循环中,您使用NEFD[n][0]修改 值来计算NEFD[n][2] 的新值( NEFD[n][1] 相同)。看来您必须存储和使用未修改的值。

for n:=1 to m_num_efd do begin
    NEFD[n][0]  := cs*NEFD[n][0] + ss*NEFD[n][2];
    NEFD[n][1]  := cs*NEFD[n][1] + ss*NEFD[n][3];

                      vvvvvvvvv
    NEFD[n][2]  :=-ss*NEFD[n][0] + cs*NEFD[n][2];

                      vvvvvvvvv   
    NEFD[n][3]  :=-ss*NEFD[n][1] + cs*NEFD[n][3];
end;

关于algorithm - 如何归一化椭圆傅里叶系数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36710928/

相关文章:

css - 填充 svg 多边形形状的一半 - 星形

delphi - 无法访问的字段错误

Delphi:表单可以自行释放吗?

javascript - svg 多边形点 - Javascript - 不起作用

c++ - 将无状态 C++ 函数从里到外转换为有状态函数

delphi - 用空格初始化 char 数组(#32)而不是 null(#0)

google-maps - 谷歌地图多边形到 SVG 路径

python - 进一步简化和缩短一个简单的分组算法

c++ - Firebug 算法图解实现

algorithm - 条形图轴的正确通用计算