Source code for menpofit.error.human.face

import numpy as np

from menpo.shape import PointCloud
from menpo.landmark import (face_ibug_68_to_face_ibug_49,
                            face_ibug_68_to_face_ibug_68,
                            face_ibug_49_to_face_ibug_49)

from menpofit.error import euclidean_error
from menpofit.error.base import (distance_normalised_error,
                                 distance_indexed_normalised_error,
                                 bb_normalised_error)


def _convert_68_to_51(shape):
    return PointCloud(shape.points[17:])


def _convert_68_to_49(shape):
    sp = shape.points.copy()
    sp = np.delete(sp, 64, 0)
    sp = np.delete(sp, 60, 0)
    sp = sp[17:]
    return PointCloud(sp)


def _convert_66_to_49(shape):
    return PointCloud(shape.points[17:])


def _convert_51_to_49(shape):
    sp = shape.points.copy()
    sp = np.delete(sp, 47, 0)
    sp = np.delete(sp, 43, 0)
    return PointCloud(sp)


[docs]def mean_pupil_68_error(shape, gt_shape): r""" Computes the Euclidean error based on 68 points normalised with the distance between the mean eye points (pupils), i.e. .. math:: \frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s)} where .. math:: \mathcal{F}(s,s^*) = \frac{1}{68}\sum_{i=1}^{68}\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. Finally, :math:`\mathcal{N}(s)` is the distance between the mean eye points (pupils). Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must have 68 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 points. Returns ------- normalised_error : `float` The computed normalised Euclidean error. Raises ------ ValueError Final shape must have 68 points ValueError Ground truth shape must have 68 points """ if shape.n_points != 68: raise ValueError('Final shape must have 68 points') if gt_shape.n_points != 68: raise ValueError('Ground truth shape must have 68 points') def pupil_dist(_, s): _, mapping = face_ibug_68_to_face_ibug_68(s, include_mapping=True) return euclidean_error(np.mean(s[mapping['left_eye']], axis=0), np.mean(s[mapping['right_eye']], axis=0)) return distance_normalised_error(euclidean_error, pupil_dist, shape, gt_shape)
[docs]def mean_pupil_49_error(shape, gt_shape): r""" Computes the euclidean error based on 49 points normalised with the distance between the mean eye points (pupils), i.e. .. math:: \frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s)} where .. math:: \mathcal{F}(s,s^*) = \frac{1}{49}\sum_{i=1}^{49}\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. Finally, :math:`\mathcal{N}(s)` is the distance between the mean eye points (pupils). Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must have either 68 or 66 or 51 or 49 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have either 68 or 66 or 51 or 49 points. Returns ------- normalised_error : `float` The computed normalised Euclidean error. Raises ------ ValueError Final shape must have 68 or 66 or 51 or 49 points ValueError Ground truth shape must have 68 or 66 or 51 or 49 points """ if shape.n_points not in [68, 66, 51, 49]: raise ValueError('Final shape must have 68 or 66 or 51 or 49 points') if gt_shape.n_points not in [68, 66, 51, 49]: raise ValueError('Ground truth shape must have 68 or 66 or 51 or 49 ' 'points') def pupil_dist(_, s): _, mapping = face_ibug_49_to_face_ibug_49(s, include_mapping=True) return euclidean_error(np.mean(s[mapping['left_eye']], axis=0), np.mean(s[mapping['right_eye']], axis=0)) if shape.n_points == 68: shape = _convert_68_to_49(shape) elif shape.n_points == 66: shape = _convert_66_to_49(shape) elif shape.n_points == 51: shape = _convert_51_to_49(shape) if gt_shape.n_points == 68: gt_shape = _convert_68_to_49(gt_shape) elif gt_shape.n_points == 66: gt_shape = _convert_66_to_49(gt_shape) elif gt_shape.n_points == 51: gt_shape = _convert_51_to_49(gt_shape) return distance_normalised_error(euclidean_error, pupil_dist, shape, gt_shape)
[docs]def outer_eye_corner_68_euclidean_error(shape, gt_shape): r""" Computes the Euclidean error based on 68 points normalised with the distance between the mean eye points (pupils), i.e. .. math:: \frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)} where .. math:: \mathcal{F}(s,s^*) = \frac{1}{68}\sum_{i=1}^{68}\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. Finally, :math:`\mathcal{N}(s^*)` is the distance between the ``36``-th and ``45``-th points. Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must have 68 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 points. Returns ------- normalised_error : `float` The computed normalised Euclidean error. Raises ------ ValueError Final shape must have 68 points ValueError Ground truth shape must have 68 points """ if shape.n_points != 68: raise ValueError('Final shape must have 68 points') if gt_shape.n_points != 68: raise ValueError('Ground truth shape must have 68 points') return distance_indexed_normalised_error(euclidean_error, 36, 45, shape, gt_shape)
[docs]def outer_eye_corner_51_euclidean_error(shape, gt_shape): r""" Computes the Euclidean error based on 51 points normalised with the distance between the mean eye points (pupils), i.e. .. math:: \frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)} where .. math:: \mathcal{F}(s,s^*) = \frac{1}{51}\sum_{i=1}^{51}\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. Finally, :math:`\mathcal{N}(s^*)` is the distance between the ``19``-th and ``28``-th points. Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must 68 or 51 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 or 51 points. Returns ------- normalised_error : `float` The computed normalised Euclidean error. Raises ------ ValueError Final shape must have 68 or 51 points ValueError Ground truth shape must have 68 or 51 points """ if shape.n_points not in [68, 51]: raise ValueError('Final shape must have 68 or 51 points') if gt_shape.n_points not in [68, 51]: raise ValueError('Ground truth shape must have 68 or 51 points') if shape.n_points == 68: shape = _convert_68_to_51(shape) if gt_shape.n_points == 68: gt_shape = _convert_68_to_51(gt_shape) return distance_indexed_normalised_error(euclidean_error, 19, 28, shape, gt_shape)
[docs]def outer_eye_corner_49_euclidean_error(shape, gt_shape): r""" Computes the Euclidean error based on 49 points normalised with the distance between the mean eye points (pupils), i.e. .. math:: \frac{\mathcal{F}(s,s^*)}{\mathcal{N}(s^*)} where .. math:: \mathcal{F}(s,s^*) = \frac{1}{49}\sum_{i=1}^{49}\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. Finally, :math:`\mathcal{N}(s^*)` is the distance between the ``19``-th and ``28``-th points. Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must 68 or 66 or 51 or 49 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 or 66 or 51 or 49 points. Returns ------- normalised_error : `float` The computed normalised Euclidean error. Raises ------ ValueError Final shape must have 68 or 66 or 51 or 49 points ValueError Ground truth shape must have 68 or 66 or 51 or 49 points """ if shape.n_points not in [68, 66, 51, 49]: raise ValueError('Final shape must have 68 or 66 or 51 or 49 points') if gt_shape.n_points not in [68, 66, 51, 49]: raise ValueError('Ground truth shape must have 68 or 66 or 51 or 49 ' 'points') if shape.n_points == 68: shape = _convert_68_to_49(shape) elif shape.n_points == 66: shape = _convert_66_to_49(shape) elif shape.n_points == 51: shape = _convert_51_to_49(shape) if gt_shape.n_points == 68: gt_shape = _convert_68_to_49(gt_shape) elif gt_shape.n_points == 66: gt_shape = _convert_66_to_49(gt_shape) elif gt_shape.n_points == 51: gt_shape = _convert_51_to_49(gt_shape) return distance_indexed_normalised_error(euclidean_error, 19, 28, shape, gt_shape)
[docs]def bb_avg_edge_length_68_euclidean_error(shape, gt_shape): r""" Computes the Euclidean error based on 68 points normalised by the average edge length of the 68-point 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}{68}\sum_{i=1}^{68}\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. Finally, :math:`\mathcal{N}(s^*)` is a normalising function that returns the average edge length of the bounding box of the 68-point ground truth shape (:map:`bb_avg_edge_length`). Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must have 68 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 points. Returns ------- normalised_error : `float` The computed Euclidean normalised error. Raises ------ ValueError Final shape must have 68 points ValueError Ground truth shape must have 68 points """ if shape.n_points != 68: raise ValueError('Final shape must have 68 points') if gt_shape.n_points != 68: raise ValueError('Ground truth shape must have 68 points') return bb_normalised_error(euclidean_error, shape, gt_shape, norm_type='avg_edge_length', norm_shape=gt_shape)
[docs]def bb_avg_edge_length_49_euclidean_error(shape, gt_shape): r""" Computes the Euclidean error based on 49 points normalised by the average edge length of the 68-point 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}{49}\sum_{i=1}^{49}\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. Finally, :math:`\mathcal{N}(s^*)` is a normalising function that returns the average edge length of the bounding box of the 68-point ground truth shape (:map:`bb_avg_edge_length`). Parameters ---------- shape : `menpo.shape.PointCloud` The input shape (e.g. the final shape of a fitting procedure). It must have 68 or 66 or 51 or 49 points. gt_shape : `menpo.shape.PointCloud` The ground truth shape. It must have 68 points. Returns ------- normalised_error : `float` The computed Euclidean normalised error. Raises ------ ValueError Final shape must have 68 or 51 or 49 points ValueError Ground truth shape must have 68 points """ if shape.n_points not in [68, 66, 51, 49]: raise ValueError('Final shape must have 68 or 66 or 51 or 49 points') if gt_shape.n_points != 68: raise ValueError('Ground truth shape must have 68 points') if shape.n_points == 68: shape = _convert_68_to_49(shape) elif shape.n_points == 66: shape = _convert_66_to_49(shape) elif shape.n_points == 51: shape = _convert_51_to_49(shape) gt_shape_68 = gt_shape.copy() gt_shape = _convert_68_to_49(gt_shape) return bb_normalised_error(euclidean_error, shape, gt_shape, norm_type='avg_edge_length', norm_shape=gt_shape_68)