3D Elliptic Fourier Analysis#

import urllib

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px

from ktch.outline import EllipticFourierAnalysis

3D coordinate values of a leaf edge#

resp = urllib.request.urlopen("https://strata.morphometrics.jp/examples/rolling_alpha_016_nIntervals_64.csv")
arr_coord = np.loadtxt(resp)
df_coord = pd.DataFrame(arr_coord, columns=["x", "y", "z"])
fig = px.line_3d(df_coord, x="x", y="y", z="z")
fig.update_layout(
    scene=dict(
                 aspectmode='data'
         )
)
fig.show()

3D EFA#

efa3d = EllipticFourierAnalysis(n_harmonics=20, n_dim=3)
coef = efa3d.fit_transform([arr_coord], norm=False).reshape(-1, 6, 21)
print("coefficients (a, b, c, d, e, f) x (n_harmonics + 1)", coef[0].shape)
coef[0]
coefficients (a, b, c, d, e, f) x (n_harmonics + 1) (6, 21)
array([[-1.42759377e+00, -1.70650493e-02, -3.80461316e-03,
        -1.00693358e-02, -2.08141139e-03, -5.08263964e-03,
        -6.33225164e-04, -2.63599319e-03,  2.85548365e-04,
        -1.06772236e-03,  1.01756558e-03, -4.86458514e-05,
         1.42889726e-03,  2.94844097e-04,  1.26212141e-03,
         2.22682094e-04,  9.72607096e-04,  8.61224932e-05,
         6.44725055e-04, -1.67687583e-04,  2.45775150e-04],
       [ 0.00000000e+00, -2.29649671e-01, -3.71082401e-02,
        -4.35955142e-02, -1.24318140e-02, -1.47228946e-02,
        -3.21175355e-03, -5.36087097e-03,  2.70540164e-04,
        -1.41158818e-03,  1.71713692e-03,  2.49786986e-04,
         1.68927281e-03,  4.05501297e-04,  1.28422905e-03,
         3.70099425e-04,  8.01286372e-04,  8.92212858e-05,
         4.14582016e-04,  4.21990175e-05,  2.81734717e-04],
       [-2.96606005e+03, -7.11629213e-01,  1.21217768e-02,
        -6.25851238e-02,  1.19900350e-02, -1.39662239e-02,
         1.00783649e-02, -2.92613848e-03,  7.64595647e-03,
        -6.10067066e-05,  5.25242050e-03,  2.99637699e-04,
         3.20689916e-03, -8.03567357e-05,  1.66230002e-03,
        -4.70306188e-04,  6.96892054e-04, -6.45376451e-04,
         1.88346338e-04, -6.25466550e-04, -1.43585002e-05],
       [ 0.00000000e+00,  4.36254535e-02, -1.20475571e-03,
         1.14874648e-02, -2.58271554e-03,  4.13840601e-03,
        -3.56772005e-03,  9.85271940e-04, -3.87585149e-03,
        -3.28318423e-04, -3.55255299e-03, -5.87511966e-04,
        -2.80943386e-03, -3.16760514e-04, -1.93017354e-03,
         1.28722854e-04, -1.12641564e-03,  5.35104171e-04,
        -5.09622396e-04,  7.95639441e-04, -1.27512663e-04],
       [ 1.16656542e+04, -6.98049896e-01, -2.67785065e-01,
        -1.52237521e-01, -8.89077804e-02, -5.41271599e-02,
        -2.98104234e-02, -1.42123489e-02, -2.23855463e-03,
         3.68032429e-03,  8.29571207e-03,  1.02828248e-02,
         1.10620167e-02,  1.00082748e-02,  8.67763767e-03,
         7.06113978e-03,  5.40354590e-03,  3.56187209e-03,
         1.92089191e-03,  6.85781791e-04, -8.15981973e-08],
       [ 0.00000000e+00,  4.40318802e-02,  3.09147425e-02,
         2.65631557e-02,  2.09030053e-02,  1.75159130e-02,
         9.20191531e-03,  6.24554843e-03,  1.36661935e-03,
        -2.24622747e-03, -6.10452747e-03, -7.72762506e-03,
        -9.18997168e-03, -9.24920005e-03, -9.28753714e-03,
        -8.15715877e-03, -6.92609392e-03, -4.91118411e-03,
        -3.18278244e-03, -1.23983238e-03,  2.80317072e-04]])

Reconstruction of 3D coordinate values from Fourier coefficients#

arr_coord_recon = efa3d.inverse_transform(coef,t_num=600)
df_coord_recon = pd.DataFrame(arr_coord_recon[0], columns=["x", "y", "z"])
fig = px.line_3d(df_coord_recon, x="x", y="y", z="z")
fig.update_layout(
    scene=dict(
                 aspectmode='data'
         )
)
fig.show()