TorchEFD - Elliptic Fourier Descriptors in PyTorch

TorchEFD is a python package to compute and apply elliptic fourier descriptors with PyTorch. Elliptic Fourier descriptors (EFDs) [1] represent a closed contour and are useful for shape analysis, matching, recognition and augmentation. This post gives a short overview and documentation of the package.

The code is adapted from PyEFD.

Example

import torch
from torch_efd import (
    compute_efds,
    integrate_total_length,
    compute_curvature,
    rotate_efds,
    reconstruct_efds
)

# 2d contour of a square
contour = torch.tensor(
    [[1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1]],
    dtype=torch.float32,
)

# compute EFD coefficents
# higher order increases precision
# set normalize=True for scale/phase/rotation-invariant descriptors
efds = compute_efds(contour, order=20)

# compute length and curvature of shape
length = integrate_total_length(efds)
curvature = compute_curvature(efds, ts=100)

# transform shape
efds = rotate_efds(efds, angle=torch.pi / 4)

# back to coordinates
reconstructed = reconstruct_efds(efds, ts=100)

API

Basics

Function Description
compute_efds(
    contour: Tensor,
    order: int,
    normalize: bool = False,
) -> Tensor
Compute EFDs, see example
reconstruct_efds(
    efds: Tensor,
    ts: Tensor | int
) -> Tensor
Reconstruct contour, see example
normalize_phase(efds: Tensor) -> Tensor
Normalize the EFDs to have zero phase shift from the first major axis.
normalize_rotation(efds: Tensor) -> Tensor
Normalize the EFDs to be rotation invariant by aligning the semi-major axis with the x-axis.
normalize_scale(efds: Tensor) -> Tensor
Normalize the scale of the EFDs.
normalize_efds(efds: Tensor) -> Tensor
Normalize phase, rotation and scale of EFDs.

Geometry

Function Description
derive_tangent_efds(efds: Tensor) -> Tensor
derive_normal_efds(efds: Tensor) -> Tensor
Compute EFDs for tangent or normal function
reconstruct_tangents(
    efds: Tensor,
    ts: Tensor | int,
    normed: bool = False,
) -> Tensor

reconstruct_normals(
    efds: Tensor,
    ts: Tensor | int,
    normed: bool = False,
) -> Tensor

reconstruct_tangrads(
    efds: Tensor,
    ts: Tensor | int,
    normed: bool = False,
) -> Tensor
Compute tangent/normal/tangrad vectors
compute_curvature(
    efds: Tensor,
    ts: Tensor | int,
    signed: bool = True,
) -> Tensor
Compute (signed) curvature of a contour.
compute_speed(
    efds: Tensor,
    ts: Tensor | int
) -> Tensor

compute_arclens(
    efds: Tensor,
    ts: Tensor | int
) -> Tensor
Compute speed (magnitude of tangents) or arc lengths
integrate_total_length(
    efds: Tensor,
    ts: Tensor | int = 100
) -> Tensor
Compute total length of contour
compute_area(
    efds: Tensor,
    ts: Tensor | int = 100
) -> Tensor
Compute area of polygon bounded by contour using the shoelace method

Transform

Function Description
scale_efds(
    efds: Tensor,
    scale: float | Tensor
) -> Tensor

rotate_efds(
    efds: Tensor,
    angle: float | Tensor
) -> Tensor
Scale or rotate contour by transforming EFDs
smooth_efds(
    efds: Tensor,
    sigma: float,
    normalize: bool = False
) -> Tensor
Apply gaussian smoothing with standard deviation sigma in fourier space

Render

Function Description
draw_mask(
    efds: Tensor,
    shape: tuple[int, int],
    n_points: int
) -> Tensor

draw_perimeter(
    efds: Tensor,
    shape: tuple[int, int],
    n_points: int
) -> Tensor
Draw image of mask/perimeter of the polygon bounded by the contour. Requires scikit-image.
compute_sdf(
    efds: Tensor,
    shape: tuple[int, int],
    n_points: int
) -> Tensor
Compute signed distance function for a contour. Requires scipy.
plot_efds(
    efds: Tensor,
    ts: int | Tensor = 100,
    ax: Axes | None = None,
    *args,
    **kwargs,
)
Plot a contour. Extra args/kwargs will be passed to Axes.plot. Requires matplotlib.

References

[1] F. Kuhl and C. P. Giardina. Elliptic Fourier Features of a Closed Contour. Computer Graphics and Image Processing 18, 1982, 236-258. https://www.sci.utah.edu/~gerig/CS7960-S2010/handouts/Kuhl-Giardina-CGIP1982.pdf