Source code for menpofit.error.base

from functools import wraps, partial
import numpy as np

from menpo.shape import PointCloud

def pointcloud_to_points(wrapped):
@wraps(wrapped)
def wrapper(*args, **kwargs):
args = list(args)
for index, arg in enumerate(args):
if isinstance(arg, PointCloud):
args[index] = arg.points
for key in kwargs:
if isinstance(kwargs[key], PointCloud):
kwargs[key] = kwargs[key].points
return wrapped(*args, **kwargs)
return wrapper

# BOUNDING BOX NORMALISERS
[docs]def bb_area(shape):
r"""
Computes the area of the bounding box of the provided shape,
i.e.

.. math::
h w

where :math:h and :math:w are the height and width of the bounding box.

Parameters
----------
shape : menpo.shape.PointCloud or subclass
The input shape.

Returns
-------
bb_area : float
The area of the bounding box.
"""
# Area = w * h
height, width = np.max(shape, axis=0) - np.min(shape, axis=0)
return height * width

[docs]def bb_perimeter(shape):
r"""
Computes the perimeter of the bounding box of the provided shape, i.e.

.. math::
2(h + w)

where :math:h and :math:w are the height and width of the bounding box.

Parameters
----------
shape : menpo.shape.PointCloud or subclass
The input shape.

Returns
-------
bb_perimeter : float
The perimeter of the bounding box.
"""
# Area = 2(w + h)
height, width = np.max(shape, axis=0) - np.min(shape, axis=0)
return 2 * (height + width)

[docs]def bb_avg_edge_length(shape):
r"""
Computes the average edge length of the bounding box of the provided shape,
i.e.

.. math::
\frac{h + w}{2} = \frac{2h + 2w}{4}

where :math:h and :math:w are the height and width of the bounding box.

Parameters
----------
shape : menpo.shape.PointCloud or subclass
The input shape.

Returns
-------
bb_avg_edge_length : float
The average edge length of the bounding box.
"""
# 0.5(w + h) = (2w + 2h) / 4
height, width = np.max(shape, axis=0) - np.min(shape, axis=0)
return 0.5 * (height + width)

[docs]def bb_diagonal(shape):
r"""
Computes the diagonal of the bounding box of the provided shape, i.e.

.. math::
\sqrt{h^2 + w^2}

where :math:h and :math:w are the height and width of the bounding box.

Parameters
----------
shape : menpo.shape.PointCloud or subclass
The input shape.

Returns
-------
bb_diagonal : float
The diagonal of the bounding box.
"""
# sqrt(w**2 + h**2)
height, width = np.max(shape, axis=0) - np.min(shape, axis=0)
return np.sqrt(width ** 2 + height ** 2)

bb_norm_types = {
'avg_edge_length': bb_avg_edge_length,
'perimeter': bb_perimeter,
'diagonal': bb_diagonal,
'area': bb_area
}

# EUCLIDEAN AND ROOT MEAN SQUARE ERRORS
[docs]@pointcloud_to_points
def root_mean_square_error(shape, gt_shape):
r"""
Computes the root mean square error between two shapes, i.e.

.. math::
\sqrt{\frac{1}{N}\sum_{i=1}^N(s_i-s^*_i)^2}

where :math:s_i and :math:s^*_i are the coordinates of the :math:i'th
point of the final and ground truth shapes, and :math:N is the total
number of points.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.

Returns
-------
root_mean_square_error : float
The root mean square error.
"""
return np.sqrt(np.mean((shape.ravel() - gt_shape.ravel()) ** 2))

[docs]@pointcloud_to_points
def euclidean_error(shape, gt_shape):
r"""
Computes the Euclidean error between two shapes, i.e.

.. math::
\frac{1}{N}\sum_{i=1}^N\sqrt{(s_{i,x}-s^*_{i,x})^2 + (s_{i,y}-s^*_{i,y})^2}

where :math:(s_{i,x}, s_{i,y}) are the x and y coordinates of the
:math:i'th point of the final shape, :math:(s^*_{i,x}, s^*_{i,y})
are the x and y coordinates of the :math:i'th point of the ground
truth shape and :math:N is the total number of points.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.

Returns
-------
root_mean_square_error : float
The Euclidean error.
"""
return np.mean(np.sqrt(np.sum((shape - gt_shape) ** 2, axis=-1)))

# DISTANCE NORMALISER
[docs]def distance_two_indices(index1, index2, shape):
r"""
Computes the Euclidean distance between two points of a shape, i.e.

.. math::
\sqrt{(s_{i,x}-s_{j,x})^2 + (s_{i,y}-s_{j,y})^2}

where :math:s_{i,x}, :math:s_{i,y} are the x and y coordinates of
the :math:i'th point (index1) and :math:s_{j,x}, :math:s_{j,y} are
the x and y coordinates of the :math:j'th point (index2).

Parameters
----------
index1 : int
The index of the first point.
index2 : int
The index of the second point.
shape : menpo.shape.PointCloud
The input shape.

Returns
-------
distance_two_indices : float
The Euclidean distance between the points.
"""
return euclidean_error(shape[index1], shape[index2])

# GENERIC NORMALISED ERROR FUNCTIONS
@pointcloud_to_points
def bb_normalised_error(shape_error_f, shape, gt_shape,
norm_shape=None, norm_type='avg_edge_length'):
r"""
Computes an error normalised by a measure based on the shape's bounding
box, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where :math:\mathcal{F}(s,s^*) is an error metric function between the
final shape :math:s and the ground truth shape :math:s^* and
:math:\mathcal{N}(s^*) is a normalising function that returns a measure
based on the ground truth shape's bounding box.

Parameters
----------
shape_error_f : callable
The function to be used for computing the error.
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
norm_shape : menpo.shape.PointCloud or None, optional
The shape to be used to compute the normaliser. If None, then the
ground truth shape is used.
norm_type : {'area', 'perimeter', 'avg_edge_length', 'diagonal'}, optional
The type of the normaliser. Possible options are:

========================= ==========================================
Method                    Description
========================= ==========================================
:map:bb_area            Area of norm_shape's bounding box
:map:bb_perimeter       Perimeter of norm_shape's bounding box
:map:bb_avg_edge_length Average edge length of norm_shape's bbox
:map:bb_diagonal        Diagonal of norm_shape's bounding box
========================= ==========================================

Returns
-------
normalised_error : float
The computed normalised error.
"""
if norm_type not in bb_norm_types:
raise ValueError('norm_type must be one of '
'{avg_edge_length, perimeter, diagonal, area}.')
if norm_shape is None:
norm_shape = gt_shape
return (shape_error_f(shape, gt_shape) /
bb_norm_types[norm_type](norm_shape))

@pointcloud_to_points
def distance_normalised_error(shape_error_f, distance_norm_f, shape, gt_shape):
r"""
Computes an error normalised by a distance measure between two shapes, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s,s^*)}

where :math:\mathcal{F}(s,s^*) is an error metric function between the
final shape :math:s and the ground truth shape :math:s^* and
:math:\mathcal{N}(s,s^*) is a normalising function based on a distance
metric between the two shapes.

Parameters
----------
shape_error_f : callable
The function to be used for computing the error.
distance_norm_f : callable
The function to be used for computing the normalisation distance metric.
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.

Returns
-------
normalised_error : float
The computed normalised error.
"""
return shape_error_f(shape, gt_shape) / distance_norm_f(shape, gt_shape)

@pointcloud_to_points
def distance_indexed_normalised_error(shape_error_f, index1, index2, shape,
gt_shape):
r"""
Computes an error normalised by the distance measure between two points
of the ground truth shape, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where :math:\mathcal{F}(s,s^*) is an error metric function between the
final shape :math:s and the ground truth shape :math:s^* and
:math:\mathcal{N}(s^*) is a normalising function that returns the
distance between two points of the ground truth shape.

Parameters
----------
shape_error_f : callable
The function to be used for computing the error.
index1 : int
The index of the first point.
index2 : int
The index of the second point.
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.

Returns
-------
normalised_error : float
The computed normalised error.
"""
return shape_error_f(shape, gt_shape) / distance_two_indices(index1, index2,
gt_shape)

# EUCLIDEAN AND ROOT MEAN SQUARE NORMALISED ERRORS
[docs]def root_mean_square_bb_normalised_error(shape, gt_shape, norm_shape=None,
norm_type='avg_edge_length'):
r"""
Computes the root mean square error between two shapes normalised by a
measure based on the ground truth shape's bounding box, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \sqrt{\frac{1}{N}\sum_{i=1}^N(s_i-s^*_i)^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:s_i and :math:s^*_i are the coordinates of the
:math:i'th point of the final and ground truth shapes, and :math:N is
the total number of points. Finally, :math:\mathcal{N}(s^*) is a
normalising function that returns a measure based on the ground truth
shape's bounding box.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
norm_shape : menpo.shape.PointCloud or None, optional
The shape to be used to compute the normaliser. If None, then the
ground truth shape is used.
norm_type : {'area', 'perimeter', 'avg_edge_length', 'diagonal'}, optional
The type of the normaliser. Possible options are:

========================= ==========================================
Method                    Description
========================= ==========================================
:map:bb_area            Area of norm_shape's bounding box
:map:bb_perimeter       Perimeter of norm_shape's bounding box
:map:bb_avg_edge_length Average edge length of norm_shape's bbox
:map:bb_diagonal        Diagonal of norm_shape's bounding box
========================= ==========================================

Returns
-------
error : float
The computed root mean square normalised error.
"""
return bb_normalised_error(shape_error_f=root_mean_square_error,
shape=shape, gt_shape=gt_shape,
norm_shape=norm_shape, norm_type=norm_type)

[docs]def root_mean_square_distance_normalised_error(shape, gt_shape,
distance_norm_f):
r"""
Computes the root mean square error between two shapes normalised by a
distance measure between two shapes, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s,s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \sqrt{\frac{1}{N}\sum_{i=1}^N(s_i-s^*_i)^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:s_i and :math:s^*_i are the coordinates of the
:math:i'th point of the final and ground truth shapes, and :math:N is
the total number of points. Finally, :math:\mathcal{N}(s,s^*) is a
normalising function based on a distance metric between the two shapes.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
distance_norm_f : callable
The function to be used for computing the normalisation distance metric.

Returns
-------
error : float
The computed root mean square normalised error.
"""
return distance_normalised_error(shape_error_f=root_mean_square_error,
distance_norm_f=distance_norm_f,
shape=shape, gt_shape=gt_shape)

[docs]def root_mean_square_distance_indexed_normalised_error(shape, gt_shape,
index1, index2):
r"""
Computes the root mean square error between two shapes normalised by the
distance measure between two points of the ground truth shape, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \sqrt{\frac{1}{N}\sum_{i=1}^N(s_i-s^*_i)^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:s_i and :math:s^*_i are the coordinates of the
:math:i'th point of the final and ground truth shapes, and :math:N is
the total number of points. Finally, :math:\mathcal{N}(s^*) is a
normalising function that returns the distance between two points of the
ground truth shape.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
index1 : int
The index of the first point.
index2 : int
The index of the second point.

Returns
-------
error : float
The computed root mean square normalised error.
"""
return distance_indexed_normalised_error(
shape_error_f=root_mean_square_error, index1=index1, index2=index2,
shape=shape, gt_shape=gt_shape)

[docs]def euclidean_bb_normalised_error(shape, gt_shape, norm_shape=None,
norm_type='avg_edge_length'):
r"""
Computes the Euclidean error between two shapes normalised by a measure
based on the ground truth shape's bounding box, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \frac{1}{N}\sum_{i=1}^N\sqrt{(s_{i,x}-s^*_{i,x})^2 + (s_{i,y}-s^*_{i,y})^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:(s_{i,x}, s_{i,y}) are the x and y coordinates of
the :math:i'th point of the final shape, :math:(s^*_{i,x}, s^*_{i,y})
are the x and y coordinates of the :math:i'th point of the ground
truth shape and :math:N is the total number of points. Finally,
:math:\mathcal{N}(s^*) is a normalising function that returns a measure
based on the ground truth shape's bounding box.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
norm_shape : menpo.shape.PointCloud or None, optional
The shape to be used to compute the normaliser. If None, then the
ground truth shape is used.
norm_type : {'area', 'perimeter', 'avg_edge_length', 'diagonal'}, optional
The type of the normaliser. Possible options are:

========================= ==========================================
Method                    Description
========================= ==========================================
:map:bb_area            Area of norm_shape's bounding box
:map:bb_perimeter       Perimeter of norm_shape's bounding box
:map:bb_avg_edge_length Average edge length of norm_shape's bbox
:map:bb_diagonal        Diagonal of norm_shape's bounding box
========================= ==========================================

Returns
-------
error : float
The computed Euclidean normalised error.
"""
return bb_normalised_error(shape_error_f=euclidean_error,
shape=shape, gt_shape=gt_shape,
norm_shape=norm_shape, norm_type=norm_type)

[docs]def euclidean_distance_normalised_error(shape, gt_shape, distance_norm_f):
r"""
Computes the Euclidean error between two shapes normalised by a distance
measure between two shapes, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s,s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \frac{1}{N}\sum_{i=1}^N\sqrt{(s_{i,x}-s^*_{i,x})^2 + (s_{i,y}-s^*_{i,y})^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:(s_{i,x}, s_{i,y}) are the x and y coordinates of
the :math:i'th point of the final shape, :math:(s^*_{i,x}, s^*_{i,y})
are the x and y coordinates of the :math:i'th point of the ground
truth shape and :math:N is the total number of points. Finally,
:math:\mathcal{N}(s,s^*) is a normalising function based on a distance
metric between the two shapes.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
distance_norm_f : callable
The function to be used for computing the normalisation distance metric.

Returns
-------
error : float
The computed Euclidean normalised error.
"""
return distance_normalised_error(shape_error_f=euclidean_error,
distance_norm_f=distance_norm_f,
shape=shape, gt_shape=gt_shape)

[docs]def euclidean_distance_indexed_normalised_error(shape, gt_shape, index1,
index2):
r"""
Computes the Euclidean error between two shapes normalised by the
distance measure between two points of the ground truth shape, i.e.

.. math::
\frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)}

where

.. math::
\mathcal{F}(s,s^*) = \frac{1}{N}\sum_{i=1}^N\sqrt{(s_{i,x}-s^*_{i,x})^2 + (s_{i,y}-s^*_{i,y})^2}

where :math:s and :math:s^* are the final and ground truth shapes,
respectively. :math:(s_{i,x}, s_{i,y}) are the x and y coordinates of
the :math:i'th point of the final shape, :math:(s^*_{i,x}, s^*_{i,y})
are the x and y coordinates of the :math:i'th point of the ground
truth shape and :math:N is the total number of points. Finally,
:math:\mathcal{N}(s^*) is a normalising function that returns the
distance between two points of the ground truth shape.

Parameters
----------
shape : menpo.shape.PointCloud
The input shape (e.g. the final shape of a fitting procedure).
gt_shape : menpo.shape.PointCloud
The ground truth shape.
index1 : int
The index of the first point.
index2 : int
The index of the second point.

Returns
-------
error : float
The computed Euclidean normalised error.
"""
return distance_indexed_normalised_error(
shape_error_f=euclidean_error, index1=index1, index2=index2,
shape=shape, gt_shape=gt_shape)