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)