PyEFD

https://github.com/hbldh/pyefd/workflows/Build%20and%20Test/badge.svg http://img.shields.io/pypi/v/pyefd.svg http://img.shields.io/pypi/dm/pyefd.svg http://img.shields.io/pypi/l/pyefd.svg https://coveralls.io/repos/github/hbldh/pyefd/badge.svg?branch=master

An Python/NumPy implementation of a method for approximating a contour with a Fourier series, as described in [1].

_images/pyefd.png

EFD representations of an MNIST [2] digit. Shows progressive improvement of approximation by order of Fourier series.

Installation

$ pip install pyefd

Usage

Given a closed contour of a shape, generated by e.g. scikit-image or OpenCV, this package can fit a Fourier series approximating the shape of the contour.

General usage examples

This section describes the general usage patterns of pyefd.

from pyefd import elliptic_fourier_descriptors
coeffs = elliptic_fourier_descriptors(contour, order=10)

The coefficients returned are the \(a_n\), \(b_n\), \(c_n\) and \(d_n\) of the following Fourier series representation of the shape.

The coefficients returned can be normalized so that they are rotation and size-invariant. This can be achieved by calling:

from pyefd import elliptic_fourier_descriptors
coeffs = elliptic_fourier_descriptors(contour, order=10, normalize=True)

Normalization can also be done afterwards:

from pyefd import normalize_efd
coeffs = normalize_efd(coeffs)

OpenCV example

If you are using OpenCV to generate contours, this example shows how to connect it to pyefd.

import cv2
import numpy
from pyefd import elliptic_fourier_descriptors

# Find the contours of a binary image using OpenCV.
contours, hierarchy = cv2.findContours(
    im, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Iterate through all contours found and store each contour's
# elliptical Fourier descriptor's coefficients.
coeffs = []
for cnt in contours:
    # Find the coefficients of all contours
    coeffs.append(elliptic_fourier_descriptors(
        numpy.squeeze(cnt), order=10))

Using EFD as features

To use EFD as features, one can write a small wrapper function:

def efd_feature(contour):
    coeffs = elliptic_fourier_descriptors(
        contour, order=10, normalize=True)
    return coeffs.flatten()[3:]

If the coefficients are normalized, then coeffs[0, 0] = 1.0, coeffs[0, 1] = 0.0 and coeffs[0, 2] = 0.0, so they can be disregarded when using the elliptic Fourier descriptors as features.

See [1] for more technical details.

Testing

Run tests with:

$ python setup.py test

or with Pytest:

$ py.test tests.py

The tests includes a single image from the MNIST dataset of handwritten digits ([2]) as a contour to use for testing.

References

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

1.4.1 (2020-09-28)

Added

  • Added CHANGELOG.md

Changed

  • Change CI from Azure Devops to Github Actions

1.4.0 (2019-07-27)

Changed

  • Merged PR #4: Vectorized contour reconstruction function

1.3.0 (2019-06-18)

Changed

  • Merged PR #2: Numpy vectorized efd

  • Moved from Travis CI to Azure Pipelines

  • Replaced rst with markdown

1.2.0 (2018-06-14)

Changed

  • Updated setup.py

  • Updated numpy requirement

Added

  • Added Pipfile

  • Ran Black on code

  • Testing on 3.6

1.1.0 (2018-06-13)

Added

  • New example for OpenCV

  • Updated documentation

1.0.0 (2016-04-19)

Changed

  • Deemed stable enough for version 1.0 release

Added

  • Created documentation.

0.1.2 (2016-02-29)

Changed

  • Testing with pytest instead of nosetests.

Added

  • Added Coveralls use.

0.1.1 (2016-02-17)

Fixed

  • Fixed MANIFEST

Added

  • Added LICENSE file that was missing.

0.1.0 (2016-02-09)

Added

  • Initial release

API

A Python implementation of the method described in [3] and [4] for calculating Fourier coefficients for characterizing closed contours.

References

Created by hbldh <henrik.blidh@nedomkull.com> on 2016-01-30.

pyefd.calculate_dc_coefficients(contour)[source]

Calculate the \(A_0\) and \(C_0\) coefficients of the elliptic Fourier series.

Parameters:

contour (numpy.ndarray) – A contour array of size [M x 2].

Returns:

The \(A_0\) and \(C_0\) coefficients.

Return type:

tuple

pyefd.elliptic_fourier_descriptors(contour, order=10, normalize=False, return_transformation=False)[source]

Calculate elliptical Fourier descriptors for a contour.

Parameters:
  • contour (numpy.ndarray) – A contour array of size [M x 2].

  • order (int) – The order of Fourier coefficients to calculate.

  • normalize (bool) – If the coefficients should be normalized; see references for details.

  • return_transformation (bool) – If the normalization parametres should be returned. Default is False.

Returns:

A [order x 4] array of Fourier coefficients and optionally the transformation parametres scale, psi_1 (rotation) and theta_1 (phase)

Return type:

:numpy.ndarray or (numpy.ndarray, (float, float, float))

pyefd.normalize_efd(coeffs, size_invariant=True, return_transformation=False)[source]

Normalizes an array of Fourier coefficients.

See [3] and [4] for details.

Parameters:
  • coeffs (numpy.ndarray) – A [n x 4] Fourier coefficient array.

  • size_invariant (bool) – If size invariance normalizing should be done as well. Default is True.

  • return_transformation (bool) – If the normalization parametres should be returned. Default is False.

Returns:

The normalized [n x 4] Fourier coefficient array and optionally the transformation parametres scale, \(psi_1\) (rotation) and \(theta_1\) (phase)

Return type:

numpy.ndarray or (numpy.ndarray, (float, float, float))

pyefd.plot_efd(coeffs, locus=(0.0, 0.0), image=None, contour=None, n=300)[source]

Plot a [2 x (N / 2)] grid of successive truncations of the series.

Note

Requires matplotlib!

Parameters:
  • coeffs (numpy.ndarray) – [N x 4] Fourier coefficient array.

  • locus (list, tuple or numpy.ndarray) – The \(A_0\) and \(C_0\) elliptic locus in [3] and [4].

  • n (int) – Number of points to use for plotting of Fourier series.

pyefd.reconstruct_contour(coeffs, locus=(0, 0), num_points=300)[source]

Returns the contour specified by the coefficients.

Parameters:
  • coeffs (numpy.ndarray) – A [n x 4] Fourier coefficient array.

  • locus (list, tuple or numpy.ndarray) – The \(A_0\) and \(C_0\) elliptic locus in [3] and [4].

  • num_points (int) – The number of sample points used for reconstructing the contour from the EFD.

Returns:

A list of x,y coordinates for the reconstructed contour.

Return type:

numpy.ndarray

Indices and tables