Source code for menpofit.result

import numpy as np

try:
    import collections.abc as collections_abc
except ImportError:
    import collections as collections_abc
from menpo.image import Image

from menpofit.visualize import view_image_multiple_landmarks
from menpofit.error import euclidean_bb_normalised_error


def _rescale_shapes_to_reference(shapes, affine_transform, scale_transform):
    rescaled_shapes = []
    for shape in shapes:
        shape = scale_transform.apply(shape)
        rescaled_shapes.append(affine_transform.apply(shape))
    return rescaled_shapes


def _parse_iters(iters, n_shapes):
    if not (iters is None or isinstance(iters, int) or isinstance(iters, list)):
        raise ValueError("iters must be either int or list or None")
    if iters is None:
        iters = list(range(n_shapes))
    if isinstance(iters, int):
        iters = [iters]
    return iters


def _get_scale_of_iter(iter_i, reconstruction_indices):
    ids = np.array(reconstruction_indices)
    return np.nonzero(iter_i >= ids)[0][-1]


[docs]class Result(object): r""" Class for defining a basic fitting result. It holds the final shape of a fitting process and, optionally, the initial shape, ground truth shape and the image object. Parameters ---------- final_shape : `menpo.shape.PointCloud` The final shape of the fitting process. image : `menpo.image.Image` or `subclass` or ``None``, optional The image on which the fitting process was applied. Note that a copy of the image will be assigned as an attribute. If ``None``, then no image is assigned. initial_shape : `menpo.shape.PointCloud` or ``None``, optional The initial shape that was provided to the fitting method to initialise the fitting process. If ``None``, then no initial shape is assigned. gt_shape : `menpo.shape.PointCloud` or ``None``, optional The ground truth shape associated with the image. If ``None``, then no ground truth shape is assigned. """ def __init__(self, final_shape, image=None, initial_shape=None, gt_shape=None): self._final_shape = final_shape self._initial_shape = initial_shape self._gt_shape = gt_shape # If image is provided, create a copy self._image = None if image is not None: self._image = Image(image.pixels) @property def is_iterative(self): r""" Flag whether the object is an iterative fitting result. :type: `bool` """ return False @property def final_shape(self): r""" Returns the final shape of the fitting process. :type: `menpo.shape.PointCloud` """ return self._final_shape @property def initial_shape(self): r""" Returns the initial shape that was provided to the fitting method to initialise the fitting process. In case the initial shape does not exist, then ``None`` is returned. :type: `menpo.shape.PointCloud` or ``None`` """ return self._initial_shape @property def gt_shape(self): r""" Returns the ground truth shape associated with the image. In case there is not an attached ground truth shape, then ``None`` is returned. :type: `menpo.shape.PointCloud` or ``None`` """ return self._gt_shape @property def image(self): r""" Returns the image that the fitting was applied on, if it was provided. Otherwise, it returns ``None``. :type: `menpo.shape.Image` or `subclass` or ``None`` """ return self._image
[docs] def final_error(self, compute_error=None): r""" Returns the final error of the fitting process, if the ground truth shape exists. This is the error computed based on the `final_shape`. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the fitted and ground truth shapes. Returns ------- final_error : `float` The final error at the end of the fitting process. Raises ------ ValueError Ground truth shape has not been set, so the final error cannot be computed """ if compute_error is None: compute_error = euclidean_bb_normalised_error if self.gt_shape is not None: return compute_error(self.final_shape, self.gt_shape) else: raise ValueError( "Ground truth shape has not been set, so the " "final error cannot be computed" )
[docs] def initial_error(self, compute_error=None): r""" Returns the initial error of the fitting process, if the ground truth shape and initial shape exist. This is the error computed based on the `initial_shape`. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the initial and ground truth shapes. Returns ------- initial_error : `float` The initial error at the beginning of the fitting process. Raises ------ ValueError Initial shape has not been set, so the initial error cannot be computed ValueError Ground truth shape has not been set, so the initial error cannot be computed """ if compute_error is None: compute_error = euclidean_bb_normalised_error if self.initial_shape is None: raise ValueError( "Initial shape has not been set, so the initial " "error cannot be computed" ) elif self.gt_shape is None: raise ValueError( "Ground truth shape has not been set, so the " "initial error cannot be computed" ) else: return compute_error(self.initial_shape, self.gt_shape)
[docs] def view( self, figure_id=None, new_figure=False, render_image=True, render_final_shape=True, render_initial_shape=False, render_gt_shape=False, subplots_enabled=True, channels=None, interpolation="bilinear", cmap_name=None, alpha=1.0, masked=True, final_marker_face_colour="r", final_marker_edge_colour="k", final_line_colour="r", initial_marker_face_colour="b", initial_marker_edge_colour="k", initial_line_colour="b", gt_marker_face_colour="y", gt_marker_edge_colour="k", gt_line_colour="y", render_lines=True, line_style="-", line_width=2, render_markers=True, marker_style="o", marker_size=4, marker_edge_width=1.0, render_numbering=False, numbers_horizontal_align="center", numbers_vertical_align="bottom", numbers_font_name="sans-serif", numbers_font_size=10, numbers_font_style="normal", numbers_font_weight="normal", numbers_font_colour="k", render_legend=True, legend_title="", legend_font_name="sans-serif", legend_font_style="normal", legend_font_size=10, legend_font_weight="normal", legend_marker_scale=None, legend_location=2, legend_bbox_to_anchor=(1.05, 1.0), legend_border_axes_pad=None, legend_n_columns=1, legend_horizontal_spacing=None, legend_vertical_spacing=None, legend_border=True, legend_border_padding=None, legend_shadow=False, legend_rounded_corners=False, render_axes=False, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=None, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(7, 7), ): """ Visualize the fitting result. The method renders the final fitted shape and optionally the initial shape, ground truth shape and the image, id they were provided. Parameters ---------- figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. render_image : `bool`, optional If ``True`` and the image exists, then it gets rendered. render_final_shape : `bool`, optional If ``True``, then the final fitting shape gets rendered. render_initial_shape : `bool`, optional If ``True`` and the initial fitting shape exists, then it gets rendered. render_gt_shape : `bool`, optional If ``True`` and the ground truth shape exists, then it gets rendered. subplots_enabled : `bool`, optional If ``True``, then the requested final, initial and ground truth shapes get rendered on separate subplots. channels : `int` or `list` of `int` or ``all`` or ``None`` If `int` or `list` of `int`, the specified channel(s) will be rendered. If ``all``, all the channels will be rendered in subplots. If ``None`` and the image is RGB, it will be rendered in RGB mode. If ``None`` and the image is not RGB, it is equivalent to ``all``. interpolation : See Below, optional The interpolation used to render the image. For example, if ``bilinear``, the image will be smooth and if ``nearest``, the image will be pixelated. Example options :: {none, nearest, bilinear, bicubic, spline16, spline36, hanning, hamming, hermite, kaiser, quadric, catrom, gaussian, bessel, mitchell, sinc, lanczos} cmap_name: `str`, optional, If ``None``, single channel and three channel images default to greyscale and rgb colormaps respectively. alpha : `float`, optional The alpha blending value, between 0 (transparent) and 1 (opaque). masked : `bool`, optional If ``True``, then the image is rendered as masked. final_marker_face_colour : See Below, optional The face (filling) colour of the markers of the final fitting shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray final_marker_edge_colour : See Below, optional The edge colour of the markers of the final fitting shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray final_line_colour : See Below, optional The line colour of the final fitting shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray initial_marker_face_colour : See Below, optional The face (filling) colour of the markers of the initial shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray initial_marker_edge_colour : See Below, optional The edge colour of the markers of the initial shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray initial_line_colour : See Below, optional The line colour of the initial shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray gt_marker_face_colour : See Below, optional The face (filling) colour of the markers of the ground truth shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray gt_marker_edge_colour : See Below, optional The edge colour of the markers of the ground truth shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray gt_line_colour : See Below, optional The line colour of the ground truth shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_lines : `bool` or `list` of `bool`, optional If ``True``, the lines will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. line_style : `str` or `list` of `str`, optional The style of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. Example options:: {'-', '--', '-.', ':'} line_width : `float` or `list` of `float`, optional The width of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. render_markers : `bool` or `list` of `bool`, optional If ``True``, the markers will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. marker_style : `str` or `list` of `str`, optional The style of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. Example options:: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int` or `list` of `int`, optional The size of the markers in points. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. marker_edge_width : `float` or `list` of `float`, optional The width of the markers' edge. You can either provide a single value that will be used for all shapes or a list with a different value per shape in (`final`, `initial`, `groundtruth`) order. render_numbering : `bool`, optional If ``True``, the landmarks will be numbered. numbers_horizontal_align : ``{center, right, left}``, optional The horizontal alignment of the numbers' texts. numbers_vertical_align : ``{center, top, bottom, baseline}``, optional The vertical alignment of the numbers' texts. numbers_font_name : See Below, optional The font of the numbers. Example options :: {serif, sans-serif, cursive, fantasy, monospace} numbers_font_size : `int`, optional The font size of the numbers. numbers_font_style : ``{normal, italic, oblique}``, optional The font style of the numbers. numbers_font_weight : See Below, optional The font weight of the numbers. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} numbers_font_colour : See Below, optional The font colour of the numbers. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_legend : `bool`, optional If ``True``, the legend will be rendered. legend_title : `str`, optional The title of the legend. legend_font_name : See below, optional The font of the legend. Example options :: {serif, sans-serif, cursive, fantasy, monospace} legend_font_style : ``{normal, italic, oblique}``, optional The font style of the legend. legend_font_size : `int`, optional The font size of the legend. legend_font_weight : See Below, optional The font weight of the legend. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} legend_marker_scale : `float`, optional The relative size of the legend markers with respect to the original legend_location : `int`, optional The location of the legend. The predefined values are: =============== == 'best' 0 'upper right' 1 'upper left' 2 'lower left' 3 'lower right' 4 'right' 5 'center left' 6 'center right' 7 'lower center' 8 'upper center' 9 'center' 10 =============== == legend_bbox_to_anchor : (`float`, `float`) `tuple`, optional The bbox that the legend will be anchored. legend_border_axes_pad : `float`, optional The pad between the axes and legend border. legend_n_columns : `int`, optional The number of the legend's columns. legend_horizontal_spacing : `float`, optional The spacing between the columns. legend_vertical_spacing : `float`, optional The vertical space between the legend entries. legend_border : `bool`, optional If ``True``, a frame will be drawn around the legend. legend_border_padding : `float`, optional The fractional whitespace inside the legend border. legend_shadow : `bool`, optional If ``True``, a shadow will be drawn behind legend. legend_rounded_corners : `bool`, optional If ``True``, the frame's corners will be rounded (fancybox). render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : See Below, optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : ``{normal, italic, oblique}``, optional The font style of the axes. axes_font_weight : See Below, optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the Image as a percentage of the Image's width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the Image as a percentage of the Image's height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) `tuple` or ``None`` optional The size of the figure in inches. Returns ------- renderer : `class` The renderer object. """ # Create image instance if self.image is None: image = Image(np.zeros((10, 10))) render_image = False else: image = Image(self.image.pixels) # Assign pointclouds to image groups = [] face_colours = [] edge_colours = [] line_colours = [] subplots_titles = {} if render_final_shape: image.landmarks["final"] = self.final_shape groups.append("final") face_colours.append(final_marker_face_colour) edge_colours.append(final_marker_edge_colour) line_colours.append(final_line_colour) subplots_titles["final"] = "Final" if self.initial_shape is not None and render_initial_shape: image.landmarks["initial"] = self.initial_shape groups.append("initial") face_colours.append(initial_marker_face_colour) edge_colours.append(initial_marker_edge_colour) line_colours.append(initial_line_colour) subplots_titles["initial"] = "Initial" if self.gt_shape is not None and render_gt_shape: image.landmarks["groundtruth"] = self.gt_shape groups.append("groundtruth") face_colours.append(gt_marker_face_colour) edge_colours.append(gt_marker_edge_colour) line_colours.append(gt_line_colour) subplots_titles["groundtruth"] = "Groundtruth" # Render return view_image_multiple_landmarks( image, groups, with_labels=None, figure_id=figure_id, new_figure=new_figure, subplots_enabled=subplots_enabled, subplots_titles=subplots_titles, render_image=render_image, render_landmarks=True, masked=masked, channels=channels, interpolation=interpolation, cmap_name=cmap_name, alpha=alpha, image_view=True, render_lines=render_lines, line_style=line_style, line_width=line_width, line_colour=line_colours, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_edge_width=marker_edge_width, marker_edge_colour=edge_colours, marker_face_colour=face_colours, render_numbering=render_numbering, numbers_horizontal_align=numbers_horizontal_align, numbers_vertical_align=numbers_vertical_align, numbers_font_name=numbers_font_name, numbers_font_size=numbers_font_size, numbers_font_style=numbers_font_style, numbers_font_weight=numbers_font_weight, numbers_font_colour=numbers_font_colour, render_legend=render_legend, legend_title=legend_title, legend_font_name=legend_font_name, legend_font_style=legend_font_style, legend_font_size=legend_font_size, legend_font_weight=legend_font_weight, legend_marker_scale=legend_marker_scale, legend_location=legend_location, legend_bbox_to_anchor=legend_bbox_to_anchor, legend_border_axes_pad=legend_border_axes_pad, legend_n_columns=legend_n_columns, legend_horizontal_spacing=legend_horizontal_spacing, legend_vertical_spacing=legend_vertical_spacing, legend_border=legend_border, legend_border_padding=legend_border_padding, legend_shadow=legend_shadow, legend_rounded_corners=legend_rounded_corners, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, figure_size=figure_size, )
def __str__(self): out = "Fitting result of {} landmark points.".format(self.final_shape.n_points) if self.gt_shape is not None: if self.initial_shape is not None: out += "\nInitial error: {:.4f}".format(self.initial_error()) if hasattr(self, "reconstructed_initial_error"): out += "\nReconstructed initial error: {:.4f}".format( self.reconstructed_initial_error() ) out += "\nFinal error: {:.4f}".format(self.final_error()) return out
[docs]class NonParametricIterativeResult(Result): r""" Class for defining a non-parametric iterative fitting result, i.e. the result of a method that does not optimize over a parametric shape model. It holds the shapes of all the iterations of the fitting procedure. It can optionally store the image on which the fitting was applied, as well as its ground truth shape. Parameters ---------- shapes : `list` of `menpo.shape.PointCloud` The `list` of shapes per iteration. Note that the list does not include the initial shape. The last member of the list is the final shape. initial_shape : `menpo.shape.PointCloud` or ``None``, optional The initial shape from which the fitting process started. If ``None``, then no initial shape is assigned. image : `menpo.image.Image` or `subclass` or ``None``, optional The image on which the fitting process was applied. Note that a copy of the image will be assigned as an attribute. If ``None``, then no image is assigned. gt_shape : `menpo.shape.PointCloud` or ``None``, optional The ground truth shape associated with the image. If ``None``, then no ground truth shape is assigned. costs : `list` of `float` or ``None``, optional The `list` of cost per iteration. If ``None``, then it is assumed that the cost function cannot be computed for the specific algorithm. It must have the same length as `shapes`. """ def __init__( self, shapes, initial_shape=None, image=None, gt_shape=None, costs=None ): super(NonParametricIterativeResult, self).__init__( final_shape=shapes[-1], image=image, initial_shape=initial_shape, gt_shape=gt_shape, ) self._n_iters = len(shapes) # If initial shape is provided, then add it in the beginning of shapes self._shapes = shapes if self.initial_shape is not None: self._shapes = [self.initial_shape] + self._shapes # Add costs as property self._costs = costs @property def is_iterative(self): r""" Flag whether the object is an iterative fitting result. :type: `bool` """ return True @property def shapes(self): r""" Returns the `list` of shapes obtained at each iteration of the fitting process. The `list` includes the `initial_shape` (if it exists) and `final_shape`. :type: `list` of `menpo.shape.PointCloud` """ return self._shapes @property def n_iters(self): r""" Returns the total number of iterations of the fitting process. :type: `int` """ return self._n_iters
[docs] def to_result(self, pass_image=True, pass_initial_shape=True, pass_gt_shape=True): r""" Returns a :map:`Result` instance of the object, i.e. a fitting result object that does not store the iterations. This can be useful for reducing the size of saved fitting results. Parameters ---------- pass_image : `bool`, optional If ``True``, then the image will get passed (if it exists). pass_initial_shape : `bool`, optional If ``True``, then the initial shape will get passed (if it exists). pass_gt_shape : `bool`, optional If ``True``, then the ground truth shape will get passed (if it exists). Returns ------- result : :map:`Result` The final "lightweight" fitting result. """ image = None if pass_image: image = self.image initial_shape = None if pass_initial_shape: initial_shape = self.initial_shape gt_shape = None if pass_gt_shape: gt_shape = self.gt_shape return Result( self.final_shape, image=image, initial_shape=initial_shape, gt_shape=gt_shape, )
[docs] def errors(self, compute_error=None): r""" Returns a list containing the error at each fitting iteration, if the ground truth shape exists. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the shape at each iteration and the ground truth shape. Returns ------- errors : `list` of `float` The error at each iteration of the fitting process. Raises ------ ValueError Ground truth shape has not been set, so the final error cannot be computed """ if compute_error is None: compute_error = euclidean_bb_normalised_error if self.gt_shape is not None: return [compute_error(t, self.gt_shape) for t in self.shapes] else: raise ValueError( "Ground truth shape has not been set, so the " "errors per iteration cannot be computed" )
[docs] def plot_errors( self, compute_error=None, figure_id=None, new_figure=False, render_lines=True, line_colour="b", line_style="-", line_width=2, render_markers=True, marker_style="o", marker_size=4, marker_face_colour="b", marker_edge_colour="k", marker_edge_width=1.0, render_axes=True, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=0.0, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(10, 6), render_grid=True, grid_line_style="--", grid_line_width=0.5, ): r""" Plot of the error evolution at each fitting iteration. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the shape at each iteration and the ground truth shape. figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. render_lines : `bool`, optional If ``True``, the line will be rendered. line_colour : `colour` or ``None`` (See below), optional The colour of the line. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray line_style : `str` (See below), optional The style of the lines. Example options:: {-, --, -., :} line_width : `float`, optional The width of the lines. render_markers : `bool`, optional If ``True``, the markers will be rendered. marker_style : `str` (See below), optional The style of the markers. Example `marker` options :: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int`, optional The size of the markers in points. marker_face_colour : `colour` or ``None``, optional The face (filling) colour of the markers. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_colour : `colour` or ``None``, optional The edge colour of the markers. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_width : `float`, optional The width of the markers' edge. render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : `str` (See below), optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : `str` (See below), optional The font style of the axes. Example options :: {normal, italic, oblique} axes_font_weight : `str` (See below), optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the graph as a percentage of the curves' width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the graph as a percentage of the curves' height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) or ``None``, optional The size of the figure in inches. render_grid : `bool`, optional If ``True``, the grid will be rendered. grid_line_style : ``{'-', '--', '-.', ':'}``, optional The style of the grid lines. grid_line_width : `float`, optional The width of the grid lines. Returns ------- renderer : `menpo.visualize.GraphPlotter` The renderer object. """ from menpo.visualize import plot_curve errors = self.errors(compute_error=compute_error) return plot_curve( x_axis=list(range(len(errors))), y_axis=[errors], figure_id=figure_id, new_figure=new_figure, title="Fitting Errors per Iteration", x_label="Iteration", y_label="Fitting Error", axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, render_lines=render_lines, line_colour=line_colour, line_style=line_style, line_width=line_width, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_face_colour=marker_face_colour, marker_edge_colour=marker_edge_colour, marker_edge_width=marker_edge_width, render_legend=False, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, figure_size=figure_size, render_grid=render_grid, grid_line_style=grid_line_style, grid_line_width=grid_line_width, )
[docs] def displacements(self): r""" A list containing the displacement between the shape of each iteration and the shape of the previous one. :type: `list` of `ndarray` """ return [ np.linalg.norm(s1.points - s2.points, axis=1) for s1, s2 in zip(self.shapes, self.shapes[1:]) ]
[docs] def displacements_stats(self, stat_type="mean"): r""" A list containing a statistical metric on the displacements between the shape of each iteration and the shape of the previous one. Parameters ---------- stat_type : ``{'mean', 'median', 'min', 'max'}``, optional Specifies a statistic metric to be extracted from the displacements. Returns ------- displacements_stat : `list` of `float` The statistical metric on the points displacements for each iteration. Raises ------ ValueError type must be 'mean', 'median', 'min' or 'max' """ if stat_type == "mean": return [np.mean(d) for d in self.displacements()] elif stat_type == "median": return [np.median(d) for d in self.displacements()] elif stat_type == "max": return [np.max(d) for d in self.displacements()] elif stat_type == "min": return [np.min(d) for d in self.displacements()] else: raise ValueError("type must be 'mean', 'median', 'min' or 'max'")
[docs] def plot_displacements( self, stat_type="mean", figure_id=None, new_figure=False, render_lines=True, line_colour="b", line_style="-", line_width=2, render_markers=True, marker_style="o", marker_size=4, marker_face_colour="b", marker_edge_colour="k", marker_edge_width=1.0, render_axes=True, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=0.0, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(10, 6), render_grid=True, grid_line_style="--", grid_line_width=0.5, ): r""" Plot of a statistical metric of the displacement between the shape of each iteration and the shape of the previous one. Parameters ---------- stat_type : {``mean``, ``median``, ``min``, ``max``}, optional Specifies a statistic metric to be extracted from the displacements (see also `displacements_stats()` method). figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. render_lines : `bool`, optional If ``True``, the line will be rendered. line_colour : `colour` or ``None`` (See below), optional The colour of the line. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray line_style : `str` (See below), optional The style of the lines. Example options:: {-, --, -., :} line_width : `float`, optional The width of the lines. render_markers : `bool`, optional If ``True``, the markers will be rendered. marker_style : `str` (See below), optional The style of the markers. Example `marker` options :: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int`, optional The size of the markers in points. marker_face_colour : `colour` or ``None``, optional The face (filling) colour of the markers. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_colour : `colour` or ``None``, optional The edge colour of the markers. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_width : `float`, optional The width of the markers' edge. render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : `str` (See below), optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : `str` (See below), optional The font style of the axes. Example options :: {normal, italic, oblique} axes_font_weight : `str` (See below), optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the graph as a percentage of the curves' width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the graph as a percentage of the curves' height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) or ``None``, optional The size of the figure in inches. render_grid : `bool`, optional If ``True``, the grid will be rendered. grid_line_style : ``{'-', '--', '-.', ':'}``, optional The style of the grid lines. grid_line_width : `float`, optional The width of the grid lines. Returns ------- renderer : `menpo.visualize.GraphPlotter` The renderer object. """ from menpo.visualize import plot_curve # set labels if stat_type == "max": name = "Maximum" elif stat_type == "min": name = "Minimum" elif stat_type == "mean": name = "Mean" elif stat_type == "median": name = "Median" else: raise ValueError("stat_type must be one of {max, min, mean, " "median}.") y_label = "{} Displacement".format(name) title = "{} displacement per Iteration".format(name) # plot displacements = self.displacements_stats(stat_type=stat_type) return plot_curve( x_axis=list(range(len(displacements))), y_axis=[displacements], figure_id=figure_id, new_figure=new_figure, title=title, x_label="Iteration", y_label=y_label, axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, render_lines=render_lines, line_colour=line_colour, line_style=line_style, line_width=line_width, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_face_colour=marker_face_colour, marker_edge_colour=marker_edge_colour, marker_edge_width=marker_edge_width, render_legend=False, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, figure_size=figure_size, render_grid=render_grid, grid_line_style=grid_line_style, grid_line_width=grid_line_width, )
@property def costs(self): r""" Returns a `list` with the cost per iteration. It returns ``None`` if the costs are not computed. :type: `list` of `float` or ``None`` """ return self._costs
[docs] def plot_costs( self, figure_id=None, new_figure=False, render_lines=True, line_colour="b", line_style="-", line_width=2, render_markers=True, marker_style="o", marker_size=4, marker_face_colour="b", marker_edge_colour="k", marker_edge_width=1.0, render_axes=True, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=0.0, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(10, 6), render_grid=True, grid_line_style="--", grid_line_width=0.5, ): r""" Plot of the cost function evolution at each fitting iteration. Parameters ---------- figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. render_lines : `bool`, optional If ``True``, the line will be rendered. line_colour : `colour` or ``None``, optional The colour of the line. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {'r', 'g', 'b', 'c', 'm', 'k', 'w'} or (3, ) ndarray line_style : ``{'-', '--', '-.', ':'}``, optional The style of the lines. line_width : `float`, optional The width of the lines. render_markers : `bool`, optional If ``True``, the markers will be rendered. marker_style : `marker`, optional The style of the markers. Example `marker` options :: {'.', ',', 'o', 'v', '^', '<', '>', '+', 'x', 'D', 'd', 's', 'p', '*', 'h', 'H', '1', '2', '3', '4', '8'} marker_size : `int`, optional The size of the markers in points. marker_face_colour : `colour` or ``None``, optional The face (filling) colour of the markers. If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {'r', 'g', 'b', 'c', 'm', 'k', 'w'} or (3, ) ndarray marker_edge_colour : `colour` or ``None``, optional The edge colour of the markers.If ``None``, the colour is sampled from the jet colormap. Example `colour` options are :: {'r', 'g', 'b', 'c', 'm', 'k', 'w'} or (3, ) ndarray marker_edge_width : `float`, optional The width of the markers' edge. render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : See below, optional The font of the axes. Example options :: {'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'} axes_font_size : `int`, optional The font size of the axes. axes_font_style : ``{'normal', 'italic', 'oblique'}``, optional The font style of the axes. axes_font_weight : See below, optional The font weight of the axes. Example options :: {'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black'} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the graph as a percentage of the curves' width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the graph as a percentage of the curves' height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) or ``None``, optional The size of the figure in inches. render_grid : `bool`, optional If ``True``, the grid will be rendered. grid_line_style : ``{'-', '--', '-.', ':'}``, optional The style of the grid lines. grid_line_width : `float`, optional The width of the grid lines. Returns ------- renderer : `menpo.visualize.GraphPlotter` The renderer object. """ from menpo.visualize import plot_curve costs = self.costs if costs is not None: return plot_curve( x_axis=list(range(len(costs))), y_axis=[costs], figure_id=figure_id, new_figure=new_figure, title="Cost per Iteration", x_label="Iteration", y_label="Cost Function", axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, render_lines=render_lines, line_colour=line_colour, line_style=line_style, line_width=line_width, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_face_colour=marker_face_colour, marker_edge_colour=marker_edge_colour, marker_edge_width=marker_edge_width, render_legend=False, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, figure_size=figure_size, render_grid=render_grid, grid_line_style=grid_line_style, grid_line_width=grid_line_width, ) else: raise ValueError( "costs are either not returned or not well " "defined for the selected fitting algorithm" )
[docs] def view_iterations( self, figure_id=None, new_figure=False, iters=None, render_image=True, subplots_enabled=False, channels=None, interpolation="bilinear", cmap_name=None, alpha=1.0, masked=True, render_lines=True, line_style="-", line_width=2, line_colour=None, render_markers=True, marker_edge_colour=None, marker_face_colour=None, marker_style="o", marker_size=4, marker_edge_width=1.0, render_numbering=False, numbers_horizontal_align="center", numbers_vertical_align="bottom", numbers_font_name="sans-serif", numbers_font_size=10, numbers_font_style="normal", numbers_font_weight="normal", numbers_font_colour="k", render_legend=True, legend_title="", legend_font_name="sans-serif", legend_font_style="normal", legend_font_size=10, legend_font_weight="normal", legend_marker_scale=None, legend_location=2, legend_bbox_to_anchor=(1.05, 1.0), legend_border_axes_pad=None, legend_n_columns=1, legend_horizontal_spacing=None, legend_vertical_spacing=None, legend_border=True, legend_border_padding=None, legend_shadow=False, legend_rounded_corners=False, render_axes=False, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=None, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(7, 7), ): """ Visualize the iterations of the fitting process. Parameters ---------- figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. iters : `int` or `list` of `int` or ``None``, optional The iterations to be visualized. If ``None``, then all the iterations are rendered. ======= ==================== ============= No. Visualised shape Description ======= ==================== ============= 0 `self.initial_shape` Initial shape 1 `self.shapes[1]` Iteration 1 i `self.shapes[i]` Iteration i n_iters `self.final_shape` Final shape ======= ==================== ============= render_image : `bool`, optional If ``True`` and the image exists, then it gets rendered. subplots_enabled : `bool`, optional If ``True``, then the requested final, initial and ground truth shapes get rendered on separate subplots. channels : `int` or `list` of `int` or ``all`` or ``None`` If `int` or `list` of `int`, the specified channel(s) will be rendered. If ``all``, all the channels will be rendered in subplots. If ``None`` and the image is RGB, it will be rendered in RGB mode. If ``None`` and the image is not RGB, it is equivalent to ``all``. interpolation : `str` (See Below), optional The interpolation used to render the image. For example, if ``bilinear``, the image will be smooth and if ``nearest``, the image will be pixelated. Example options :: {none, nearest, bilinear, bicubic, spline16, spline36, hanning, hamming, hermite, kaiser, quadric, catrom, gaussian, bessel, mitchell, sinc, lanczos} cmap_name: `str`, optional, If ``None``, single channel and three channel images default to greyscale and rgb colormaps respectively. alpha : `float`, optional The alpha blending value, between 0 (transparent) and 1 (opaque). masked : `bool`, optional If ``True``, then the image is rendered as masked. render_lines : `bool` or `list` of `bool`, optional If ``True``, the lines will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_style : `str` or `list` of `str` (See below), optional The style of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options:: {-, --, -., :} line_width : `float` or `list` of `float`, optional The width of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_colour : `colour` or `list` of `colour` (See Below), optional The colour of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_markers : `bool` or `list` of `bool`, optional If ``True``, the markers will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_style : `str or `list` of `str` (See below), optional The style of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int` or `list` of `int`, optional The size of the markers in points. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_edge_colour : `colour` or `list` of `colour` (See Below), optional The edge colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_face_colour : `colour` or `list` of `colour` (See Below), optional The face (filling) colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_width : `float` or `list` of `float`, optional The width of the markers' edge. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. render_numbering : `bool`, optional If ``True``, the landmarks will be numbered. numbers_horizontal_align : `str` (See below), optional The horizontal alignment of the numbers' texts. Example options :: {center, right, left} numbers_vertical_align : `str` (See below), optional The vertical alignment of the numbers' texts. Example options :: {center, top, bottom, baseline} numbers_font_name : `str` (See below), optional The font of the numbers. Example options :: {serif, sans-serif, cursive, fantasy, monospace} numbers_font_size : `int`, optional The font size of the numbers. numbers_font_style : ``{normal, italic, oblique}``, optional The font style of the numbers. numbers_font_weight : `str` (See below), optional The font weight of the numbers. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} numbers_font_colour : See Below, optional The font colour of the numbers. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_legend : `bool`, optional If ``True``, the legend will be rendered. legend_title : `str`, optional The title of the legend. legend_font_name : See below, optional The font of the legend. Example options :: {serif, sans-serif, cursive, fantasy, monospace} legend_font_style : `str` (See below), optional The font style of the legend. Example options :: {normal, italic, oblique} legend_font_size : `int`, optional The font size of the legend. legend_font_weight : `str` (See below), optional The font weight of the legend. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} legend_marker_scale : `float`, optional The relative size of the legend markers with respect to the original legend_location : `int`, optional The location of the legend. The predefined values are: =============== == 'best' 0 'upper right' 1 'upper left' 2 'lower left' 3 'lower right' 4 'right' 5 'center left' 6 'center right' 7 'lower center' 8 'upper center' 9 'center' 10 =============== == legend_bbox_to_anchor : (`float`, `float`) `tuple`, optional The bbox that the legend will be anchored. legend_border_axes_pad : `float`, optional The pad between the axes and legend border. legend_n_columns : `int`, optional The number of the legend's columns. legend_horizontal_spacing : `float`, optional The spacing between the columns. legend_vertical_spacing : `float`, optional The vertical space between the legend entries. legend_border : `bool`, optional If ``True``, a frame will be drawn around the legend. legend_border_padding : `float`, optional The fractional whitespace inside the legend border. legend_shadow : `bool`, optional If ``True``, a shadow will be drawn behind legend. legend_rounded_corners : `bool`, optional If ``True``, the frame's corners will be rounded (fancybox). render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : `str` (See below), optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : ``{normal, italic, oblique}``, optional The font style of the axes. axes_font_weight : `str` (See below), optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the Image as a percentage of the Image's width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the Image as a percentage of the Image's height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) `tuple` or ``None`` optional The size of the figure in inches. Returns ------- renderer : `class` The renderer object. """ # Parse iters iters = _parse_iters(iters, len(self.shapes)) # Create image instance if self.image is None: image = Image(np.zeros((10, 10))) render_image = False else: image = Image(self.image.pixels) # Assign pointclouds to image n_digits = len(str(self.n_iters)) groups = [] subplots_titles = {} iters_offset = 1 if self.initial_shape is not None: iters_offset = 0 for j in iters: if j == 0 and self.initial_shape is not None: name = "Initial" image.landmarks[name] = self.initial_shape elif j == len(self.shapes) - 1: name = "Final" image.landmarks[name] = self.final_shape else: name = "iteration {:0{}d}".format(j + iters_offset, n_digits) image.landmarks[name] = self.shapes[j] groups.append(name) subplots_titles[name] = name # Render return view_image_multiple_landmarks( image, groups, with_labels=None, figure_id=figure_id, new_figure=new_figure, subplots_enabled=subplots_enabled, subplots_titles=subplots_titles, render_image=render_image, render_landmarks=True, masked=masked, channels=channels, interpolation=interpolation, cmap_name=cmap_name, alpha=alpha, image_view=True, render_lines=render_lines, line_style=line_style, line_width=line_width, line_colour=line_colour, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_edge_width=marker_edge_width, marker_edge_colour=marker_edge_colour, marker_face_colour=marker_face_colour, render_numbering=render_numbering, numbers_horizontal_align=numbers_horizontal_align, numbers_vertical_align=numbers_vertical_align, numbers_font_name=numbers_font_name, numbers_font_size=numbers_font_size, numbers_font_style=numbers_font_style, numbers_font_weight=numbers_font_weight, numbers_font_colour=numbers_font_colour, render_legend=render_legend, legend_title=legend_title, legend_font_name=legend_font_name, legend_font_style=legend_font_style, legend_font_size=legend_font_size, legend_font_weight=legend_font_weight, legend_marker_scale=legend_marker_scale, legend_location=legend_location, legend_bbox_to_anchor=legend_bbox_to_anchor, legend_border_axes_pad=legend_border_axes_pad, legend_n_columns=legend_n_columns, legend_horizontal_spacing=legend_horizontal_spacing, legend_vertical_spacing=legend_vertical_spacing, legend_border=legend_border, legend_border_padding=legend_border_padding, legend_shadow=legend_shadow, legend_rounded_corners=legend_rounded_corners, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, figure_size=figure_size, )
[docs]class ParametricIterativeResult(NonParametricIterativeResult): r""" Class for defining a parametric iterative fitting result, i.e. the result of a method that optimizes the parameters of a shape model. It holds the shapes and shape parameters of all the iterations of the fitting procedure. It can optionally store the image on which the fitting was applied, as well as its ground truth shape. .. note:: When using a method with a parametric shape model, the first step is to **reconstruct the initial shape** using the shape model. The generated reconstructed shape is then used as initialisation for the iterative optimisation. This step is not counted in the number of iterations. Parameters ---------- shapes : `list` of `menpo.shape.PointCloud` The `list` of shapes per iteration. Note that the list does not include the initial shape. However, it includes the reconstruction of the initial shape. The last member of the list is the final shape. shape_parameters : `list` of `ndarray` The `list` of shape parameters per iteration. Note that the list includes the parameters of the projection of the initial shape. The last member of the list corresponds to the final shape's parameters. It must have the same length as `shapes`. initial_shape : `menpo.shape.PointCloud` or ``None``, optional The initial shape from which the fitting process started. If ``None``, then no initial shape is assigned. image : `menpo.image.Image` or `subclass` or ``None``, optional The image on which the fitting process was applied. Note that a copy of the image will be assigned as an attribute. If ``None``, then no image is assigned. gt_shape : `menpo.shape.PointCloud` or ``None``, optional The ground truth shape associated with the image. If ``None``, then no ground truth shape is assigned. costs : `list` of `float` or ``None``, optional The `list` of cost per iteration. If ``None``, then it is assumed that the cost function cannot be computed for the specific algorithm. It must have the same length as `shapes`. """ def __init__( self, shapes, shape_parameters, initial_shape=None, image=None, gt_shape=None, costs=None, ): # Assign shape parameters self._shape_parameters = shape_parameters # Get reconstructed initial shape self._reconstructed_initial_shape = shapes[0] # Call superclass super(ParametricIterativeResult, self).__init__( shapes=shapes, initial_shape=initial_shape, image=image, gt_shape=gt_shape, costs=costs, ) # Correct n_iters. The initial shape's reconstruction should not count # in the number of iterations. self._n_iters -= 1 @property def shapes(self): r""" Returns the `list` of shapes obtained at each iteration of the fitting process. The `list` includes the `initial_shape` (if it exists), `reconstructed_initial_shape` and `final_shape`. :type: `list` of `menpo.shape.PointCloud` """ return self._shapes @property def shape_parameters(self): r""" Returns the `list` of shape parameters obtained at each iteration of the fitting process. The `list` includes the parameters of the `reconstructed_initial_shape` and `final_shape`. :type: `list` of ``(n_params,)`` `ndarray` """ return self._shape_parameters @property def reconstructed_initial_shape(self): r""" Returns the initial shape's reconstruction with the shape model that was used to initialise the iterative optimisation process. :type: `menpo.shape.PointCloud` """ if self.initial_shape is not None: return self.shapes[1] else: return self.shapes[0] @property def _reconstruction_indices(self): r""" Returns a list with the indices of reconstructed shapes in the `shapes` list. :type: `list` of `int` """ if self.initial_shape is not None: return [1] else: return [0]
[docs] def reconstructed_initial_error(self, compute_error=None): r""" Returns the error of the reconstructed initial shape of the fitting process, if the ground truth shape exists. This is the error computed based on the `reconstructed_initial_shape`. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the reconstructed initial and ground truth shapes. Returns ------- reconstructed_initial_error : `float` The error that corresponds to the initial shape's reconstruction. Raises ------ ValueError Ground truth shape has not been set, so the reconstructed initial error cannot be computed """ if compute_error is None: compute_error = euclidean_bb_normalised_error if self.gt_shape is None: raise ValueError( "Ground truth shape has not been set, so the " "reconstructed initial error cannot be computed" ) else: return compute_error(self.reconstructed_initial_shape, self.gt_shape)
[docs] def view_iterations( self, figure_id=None, new_figure=False, iters=None, render_image=True, subplots_enabled=False, channels=None, interpolation="bilinear", cmap_name=None, alpha=1.0, masked=True, render_lines=True, line_style="-", line_width=2, line_colour=None, render_markers=True, marker_edge_colour=None, marker_face_colour=None, marker_style="o", marker_size=4, marker_edge_width=1.0, render_numbering=False, numbers_horizontal_align="center", numbers_vertical_align="bottom", numbers_font_name="sans-serif", numbers_font_size=10, numbers_font_style="normal", numbers_font_weight="normal", numbers_font_colour="k", render_legend=True, legend_title="", legend_font_name="sans-serif", legend_font_style="normal", legend_font_size=10, legend_font_weight="normal", legend_marker_scale=None, legend_location=2, legend_bbox_to_anchor=(1.05, 1.0), legend_border_axes_pad=None, legend_n_columns=1, legend_horizontal_spacing=None, legend_vertical_spacing=None, legend_border=True, legend_border_padding=None, legend_shadow=False, legend_rounded_corners=False, render_axes=False, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=None, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(7, 7), ): """ Visualize the iterations of the fitting process. Parameters ---------- figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. iters : `int` or `list` of `int` or ``None``, optional The iterations to be visualized. If ``None``, then all the iterations are rendered. ========= ==================================== ====================== No. Visualised shape Description ========= ==================================== ====================== 0 `self.initial_shape` Initial shape 1 `self.reconstructed_initial_shape` Reconstructed initial 2 `self.shapes[2]` Iteration 1 i `self.shapes[i]` Iteration i-1 n_iters+1 `self.final_shape` Final shape ========= ==================================== ====================== render_image : `bool`, optional If ``True`` and the image exists, then it gets rendered. subplots_enabled : `bool`, optional If ``True``, then the requested final, initial and ground truth shapes get rendered on separate subplots. channels : `int` or `list` of `int` or ``all`` or ``None`` If `int` or `list` of `int`, the specified channel(s) will be rendered. If ``all``, all the channels will be rendered in subplots. If ``None`` and the image is RGB, it will be rendered in RGB mode. If ``None`` and the image is not RGB, it is equivalent to ``all``. interpolation : `str` (See Below), optional The interpolation used to render the image. For example, if ``bilinear``, the image will be smooth and if ``nearest``, the image will be pixelated. Example options :: {none, nearest, bilinear, bicubic, spline16, spline36, hanning, hamming, hermite, kaiser, quadric, catrom, gaussian, bessel, mitchell, sinc, lanczos} cmap_name: `str`, optional, If ``None``, single channel and three channel images default to greyscale and rgb colormaps respectively. alpha : `float`, optional The alpha blending value, between 0 (transparent) and 1 (opaque). masked : `bool`, optional If ``True``, then the image is rendered as masked. render_lines : `bool` or `list` of `bool`, optional If ``True``, the lines will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_style : `str` or `list` of `str` (See below), optional The style of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options:: {-, --, -., :} line_width : `float` or `list` of `float`, optional The width of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_colour : `colour` or `list` of `colour` (See Below), optional The colour of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_markers : `bool` or `list` of `bool`, optional If ``True``, the markers will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_style : `str or `list` of `str` (See below), optional The style of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int` or `list` of `int`, optional The size of the markers in points. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_edge_colour : `colour` or `list` of `colour` (See Below), optional The edge colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_face_colour : `colour` or `list` of `colour` (See Below), optional The face (filling) colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_width : `float` or `list` of `float`, optional The width of the markers' edge. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. render_numbering : `bool`, optional If ``True``, the landmarks will be numbered. numbers_horizontal_align : `str` (See below), optional The horizontal alignment of the numbers' texts. Example options :: {center, right, left} numbers_vertical_align : `str` (See below), optional The vertical alignment of the numbers' texts. Example options :: {center, top, bottom, baseline} numbers_font_name : `str` (See below), optional The font of the numbers. Example options :: {serif, sans-serif, cursive, fantasy, monospace} numbers_font_size : `int`, optional The font size of the numbers. numbers_font_style : ``{normal, italic, oblique}``, optional The font style of the numbers. numbers_font_weight : `str` (See below), optional The font weight of the numbers. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} numbers_font_colour : See Below, optional The font colour of the numbers. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_legend : `bool`, optional If ``True``, the legend will be rendered. legend_title : `str`, optional The title of the legend. legend_font_name : See below, optional The font of the legend. Example options :: {serif, sans-serif, cursive, fantasy, monospace} legend_font_style : `str` (See below), optional The font style of the legend. Example options :: {normal, italic, oblique} legend_font_size : `int`, optional The font size of the legend. legend_font_weight : `str` (See below), optional The font weight of the legend. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} legend_marker_scale : `float`, optional The relative size of the legend markers with respect to the original legend_location : `int`, optional The location of the legend. The predefined values are: =============== == 'best' 0 'upper right' 1 'upper left' 2 'lower left' 3 'lower right' 4 'right' 5 'center left' 6 'center right' 7 'lower center' 8 'upper center' 9 'center' 10 =============== == legend_bbox_to_anchor : (`float`, `float`) `tuple`, optional The bbox that the legend will be anchored. legend_border_axes_pad : `float`, optional The pad between the axes and legend border. legend_n_columns : `int`, optional The number of the legend's columns. legend_horizontal_spacing : `float`, optional The spacing between the columns. legend_vertical_spacing : `float`, optional The vertical space between the legend entries. legend_border : `bool`, optional If ``True``, a frame will be drawn around the legend. legend_border_padding : `float`, optional The fractional whitespace inside the legend border. legend_shadow : `bool`, optional If ``True``, a shadow will be drawn behind legend. legend_rounded_corners : `bool`, optional If ``True``, the frame's corners will be rounded (fancybox). render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : `str` (See below), optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : ``{normal, italic, oblique}``, optional The font style of the axes. axes_font_weight : `str` (See below), optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the Image as a percentage of the Image's width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the Image as a percentage of the Image's height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) `tuple` or ``None`` optional The size of the figure in inches. Returns ------- renderer : `class` The renderer object. """ # Parse iters iters = _parse_iters(iters, len(self.shapes)) # Create image instance if self.image is None: image = Image(np.zeros((10, 10))) render_image = False else: image = Image(self.image.pixels) # Assign pointclouds to image n_digits = len(str(self.n_iters)) groups = [] subplots_titles = {} iters_offset = 0 if self.initial_shape is not None: iters_offset = 1 for j in iters: if j == 0 and self.initial_shape is not None: name = "Initial" image.landmarks[name] = self.initial_shape elif j in self._reconstruction_indices: name = "Reconstruction" image.landmarks[name] = self.shapes[j] elif j == len(self.shapes) - 1: name = "Final" image.landmarks[name] = self.final_shape else: s = _get_scale_of_iter(j, self._reconstruction_indices) name = "iteration {:0{}d}".format(j - s + iters_offset, n_digits) image.landmarks[name] = self.shapes[j] groups.append(name) subplots_titles[name] = name # Render return view_image_multiple_landmarks( image, groups, with_labels=None, figure_id=figure_id, new_figure=new_figure, subplots_enabled=subplots_enabled, subplots_titles=subplots_titles, render_image=render_image, render_landmarks=True, masked=masked, channels=channels, interpolation=interpolation, cmap_name=cmap_name, alpha=alpha, image_view=True, render_lines=render_lines, line_style=line_style, line_width=line_width, line_colour=line_colour, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_edge_width=marker_edge_width, marker_edge_colour=marker_edge_colour, marker_face_colour=marker_face_colour, render_numbering=render_numbering, numbers_horizontal_align=numbers_horizontal_align, numbers_vertical_align=numbers_vertical_align, numbers_font_name=numbers_font_name, numbers_font_size=numbers_font_size, numbers_font_style=numbers_font_style, numbers_font_weight=numbers_font_weight, numbers_font_colour=numbers_font_colour, render_legend=render_legend, legend_title=legend_title, legend_font_name=legend_font_name, legend_font_style=legend_font_style, legend_font_size=legend_font_size, legend_font_weight=legend_font_weight, legend_marker_scale=legend_marker_scale, legend_location=legend_location, legend_bbox_to_anchor=legend_bbox_to_anchor, legend_border_axes_pad=legend_border_axes_pad, legend_n_columns=legend_n_columns, legend_horizontal_spacing=legend_horizontal_spacing, legend_vertical_spacing=legend_vertical_spacing, legend_border=legend_border, legend_border_padding=legend_border_padding, legend_shadow=legend_shadow, legend_rounded_corners=legend_rounded_corners, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, figure_size=figure_size, )
[docs]class MultiScaleNonParametricIterativeResult(NonParametricIterativeResult): r""" Class for defining a multi-scale non-parametric iterative fitting result, i.e. the result of a multi-scale method that does not optimise over a parametric shape model. It holds the shapes of all the iterations of the fitting procedure, as well as the scales. It can optionally store the image on which the fitting was applied, as well as its ground truth shape. Parameters ---------- results : `list` of :map:`NonParametricIterativeResult` The `list` of non parametric iterative results per scale. scales : `list` of `float` The scale values (normally small to high). affine_transforms : `list` of `menpo.transform.Affine` The list of affine transforms per scale that transform the shapes into the original image space. scale_transforms : `list` of `menpo.shape.Scale` The list of scaling transforms per scale. image : `menpo.image.Image` or `subclass` or ``None``, optional The image on which the fitting process was applied. Note that a copy of the image will be assigned as an attribute. If ``None``, then no image is assigned. gt_shape : `menpo.shape.PointCloud` or ``None``, optional The ground truth shape associated with the image. If ``None``, then no ground truth shape is assigned. """ def __init__( self, results, scales, affine_transforms, scale_transforms, image=None, gt_shape=None, ): # Make sure results and scales are iterable if not isinstance(results, collections_abc.Iterable): results = [results] if not isinstance(scales, collections_abc.Iterable): scales = [scales] # Check that results and scales have the same length if len(results) != len(scales): raise ValueError( "results and scales must have equal length ({} " "!= {})".format(len(results), len(scales)) ) # Get initial shape initial_shape = None if results[0].initial_shape is not None: initial_shape = _rescale_shapes_to_reference( shapes=[results[0].initial_shape], affine_transform=affine_transforms[0], scale_transform=scale_transforms[0], )[0] # Create shapes list and n_iters_per_scale # If the result object has an initial shape, then it has to be # removed from the final shapes list n_iters_per_scale = [] shapes = [] for i in list(range(len(scales))): n_iters_per_scale.append(results[i].n_iters) if results[i].initial_shape is None: shapes += _rescale_shapes_to_reference( shapes=results[i].shapes, affine_transform=affine_transforms[i], scale_transform=scale_transforms[i], ) else: shapes += _rescale_shapes_to_reference( shapes=results[i].shapes[1:], affine_transform=affine_transforms[i], scale_transform=scale_transforms[i], ) # Call superclass super(MultiScaleNonParametricIterativeResult, self).__init__( shapes=shapes, initial_shape=initial_shape, image=image, gt_shape=gt_shape ) # Get attributes self._n_iters_per_scale = n_iters_per_scale self._n_scales = len(scales) # Create costs list. We assume that if the costs of the first result # object is None, then the costs property of all objects is None. # Similarly, if the costs property of the the first object is not # None, then the same stands for the rest. self._costs = None if results[0].costs is not None: self._costs = [] for r in results: self._costs += r.costs @property def n_iters_per_scale(self): r""" Returns the number of iterations per scale of the fitting process. :type: `list` of `int` """ return self._n_iters_per_scale @property def n_scales(self): r""" Returns the number of scales used during the fitting process. :type: `int` """ return self._n_scales
[docs]class MultiScaleParametricIterativeResult(MultiScaleNonParametricIterativeResult): r""" Class for defining a multi-scale parametric iterative fitting result, i.e. the result of a multi-scale method that optimizes over a parametric shape model. It holds the shapes of all the iterations of the fitting procedure, as well as the scales. It can optionally store the image on which the fitting was applied, as well as its ground truth shape. .. note:: When using a method with a parametric shape model, the first step is to **reconstruct the initial shape** using the shape model. The generated reconstructed shape is then used as initialisation for the iterative optimisation. This step is not counted in the number of iterations. Parameters ---------- results : `list` of :map:`ParametricIterativeResult` The `list` of parametric iterative results per scale. scales : `list` of `float` The scale values (normally small to high). affine_transforms : `list` of `menpo.transform.Affine` The list of affine transforms per scale that transform the shapes into the original image space. scale_transforms : `list` of `menpo.shape.Scale` The list of scaling transforms per scale. image : `menpo.image.Image` or `subclass` or ``None``, optional The image on which the fitting process was applied. Note that a copy of the image will be assigned as an attribute. If ``None``, then no image is assigned. gt_shape : `menpo.shape.PointCloud` or ``None``, optional The ground truth shape associated with the image. If ``None``, then no ground truth shape is assigned. """ def __init__( self, results, scales, affine_transforms, scale_transforms, image=None, gt_shape=None, ): # Call superclass super(MultiScaleParametricIterativeResult, self).__init__( results=results, scales=scales, affine_transforms=affine_transforms, scale_transforms=scale_transforms, image=image, gt_shape=gt_shape, ) # Create shape parameters, and reconstructed initial shapes lists self._shape_parameters = [] for r in results: self._shape_parameters += r.shape_parameters # Correct n_iters self._n_iters -= len(scales) @property def shape_parameters(self): r""" Returns the `list` of shape parameters obtained at each iteration of the fitting process. The `list` includes the parameters of the `initial_shape` (if it exists) and `final_shape`. :type: `list` of ``(n_params,)`` `ndarray` """ return self._shape_parameters @property def reconstructed_initial_shapes(self): r""" Returns the result of the reconstruction step that takes place at each scale before applying the iterative optimisation. :type: `list` of `menpo.shape.PointCloud` """ ids = self._reconstruction_indices return [self.shapes[i] for i in ids] @property def _reconstruction_indices(self): r""" Returns a list with the indices of reconstructed shapes in the `shapes` list. :type: `list` of `int` """ initial_val = 0 if self.initial_shape is not None: initial_val = 1 ids = [] for i in list(range(self.n_scales)): if i == 0: ids.append(initial_val) else: previous_val = ids[i - 1] ids.append(previous_val + self.n_iters_per_scale[i - 1] + 1) return ids
[docs] def reconstructed_initial_error(self, compute_error=None): r""" Returns the error of the reconstructed initial shape of the fitting process, if the ground truth shape exists. This is the error computed based on the `reconstructed_initial_shapes[0]`. Parameters ---------- compute_error: `callable`, optional Callable that computes the error between the reconstructed initial and ground truth shapes. Returns ------- reconstructed_initial_error : `float` The error that corresponds to the initial shape's reconstruction. Raises ------ ValueError Ground truth shape has not been set, so the reconstructed initial error cannot be computed """ if compute_error is None: compute_error = euclidean_bb_normalised_error if self.gt_shape is None: raise ValueError( "Ground truth shape has not been set, so the " "reconstructed initial error cannot be computed" ) else: return compute_error(self.reconstructed_initial_shapes[0], self.gt_shape)
[docs] def view_iterations( self, figure_id=None, new_figure=False, iters=None, render_image=True, subplots_enabled=False, channels=None, interpolation="bilinear", cmap_name=None, alpha=1.0, masked=True, render_lines=True, line_style="-", line_width=2, line_colour=None, render_markers=True, marker_edge_colour=None, marker_face_colour=None, marker_style="o", marker_size=4, marker_edge_width=1.0, render_numbering=False, numbers_horizontal_align="center", numbers_vertical_align="bottom", numbers_font_name="sans-serif", numbers_font_size=10, numbers_font_style="normal", numbers_font_weight="normal", numbers_font_colour="k", render_legend=True, legend_title="", legend_font_name="sans-serif", legend_font_style="normal", legend_font_size=10, legend_font_weight="normal", legend_marker_scale=None, legend_location=2, legend_bbox_to_anchor=(1.05, 1.0), legend_border_axes_pad=None, legend_n_columns=1, legend_horizontal_spacing=None, legend_vertical_spacing=None, legend_border=True, legend_border_padding=None, legend_shadow=False, legend_rounded_corners=False, render_axes=False, axes_font_name="sans-serif", axes_font_size=10, axes_font_style="normal", axes_font_weight="normal", axes_x_limits=None, axes_y_limits=None, axes_x_ticks=None, axes_y_ticks=None, figure_size=(7, 7), ): """ Visualize the iterations of the fitting process. Parameters ---------- figure_id : `object`, optional The id of the figure to be used. new_figure : `bool`, optional If ``True``, a new figure is created. iters : `int` or `list` of `int` or ``None``, optional The iterations to be visualized. If ``None``, then all the iterations are rendered. ========= ==================================== ====================== No. Visualised shape Description ========= ==================================== ====================== 0 `self.initial_shape` Initial shape 1 `self.reconstructed_initial_shape` Reconstructed initial 2 `self.shapes[2]` Iteration 1 i `self.shapes[i]` Iteration i-1 n_iters+1 `self.final_shape` Final shape ========= ==================================== ====================== render_image : `bool`, optional If ``True`` and the image exists, then it gets rendered. subplots_enabled : `bool`, optional If ``True``, then the requested final, initial and ground truth shapes get rendered on separate subplots. channels : `int` or `list` of `int` or ``all`` or ``None`` If `int` or `list` of `int`, the specified channel(s) will be rendered. If ``all``, all the channels will be rendered in subplots. If ``None`` and the image is RGB, it will be rendered in RGB mode. If ``None`` and the image is not RGB, it is equivalent to ``all``. interpolation : `str` (See Below), optional The interpolation used to render the image. For example, if ``bilinear``, the image will be smooth and if ``nearest``, the image will be pixelated. Example options :: {none, nearest, bilinear, bicubic, spline16, spline36, hanning, hamming, hermite, kaiser, quadric, catrom, gaussian, bessel, mitchell, sinc, lanczos} cmap_name: `str`, optional, If ``None``, single channel and three channel images default to greyscale and rgb colormaps respectively. alpha : `float`, optional The alpha blending value, between 0 (transparent) and 1 (opaque). masked : `bool`, optional If ``True``, then the image is rendered as masked. render_lines : `bool` or `list` of `bool`, optional If ``True``, the lines will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_style : `str` or `list` of `str` (See below), optional The style of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options:: {-, --, -., :} line_width : `float` or `list` of `float`, optional The width of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. line_colour : `colour` or `list` of `colour` (See Below), optional The colour of the lines. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_markers : `bool` or `list` of `bool`, optional If ``True``, the markers will be rendered. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_style : `str or `list` of `str` (See below), optional The style of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {., ,, o, v, ^, <, >, +, x, D, d, s, p, *, h, H, 1, 2, 3, 4, 8} marker_size : `int` or `list` of `int`, optional The size of the markers in points. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. marker_edge_colour : `colour` or `list` of `colour` (See Below), optional The edge colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_face_colour : `colour` or `list` of `colour` (See Below), optional The face (filling) colour of the markers. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray marker_edge_width : `float` or `list` of `float`, optional The width of the markers' edge. You can either provide a single value that will be used for all shapes or a list with a different value per iteration shape. render_numbering : `bool`, optional If ``True``, the landmarks will be numbered. numbers_horizontal_align : `str` (See below), optional The horizontal alignment of the numbers' texts. Example options :: {center, right, left} numbers_vertical_align : `str` (See below), optional The vertical alignment of the numbers' texts. Example options :: {center, top, bottom, baseline} numbers_font_name : `str` (See below), optional The font of the numbers. Example options :: {serif, sans-serif, cursive, fantasy, monospace} numbers_font_size : `int`, optional The font size of the numbers. numbers_font_style : ``{normal, italic, oblique}``, optional The font style of the numbers. numbers_font_weight : `str` (See below), optional The font weight of the numbers. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} numbers_font_colour : See Below, optional The font colour of the numbers. Example options :: {r, g, b, c, m, k, w} or (3, ) ndarray render_legend : `bool`, optional If ``True``, the legend will be rendered. legend_title : `str`, optional The title of the legend. legend_font_name : See below, optional The font of the legend. Example options :: {serif, sans-serif, cursive, fantasy, monospace} legend_font_style : `str` (See below), optional The font style of the legend. Example options :: {normal, italic, oblique} legend_font_size : `int`, optional The font size of the legend. legend_font_weight : `str` (See below), optional The font weight of the legend. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} legend_marker_scale : `float`, optional The relative size of the legend markers with respect to the original legend_location : `int`, optional The location of the legend. The predefined values are: =============== == 'best' 0 'upper right' 1 'upper left' 2 'lower left' 3 'lower right' 4 'right' 5 'center left' 6 'center right' 7 'lower center' 8 'upper center' 9 'center' 10 =============== == legend_bbox_to_anchor : (`float`, `float`) `tuple`, optional The bbox that the legend will be anchored. legend_border_axes_pad : `float`, optional The pad between the axes and legend border. legend_n_columns : `int`, optional The number of the legend's columns. legend_horizontal_spacing : `float`, optional The spacing between the columns. legend_vertical_spacing : `float`, optional The vertical space between the legend entries. legend_border : `bool`, optional If ``True``, a frame will be drawn around the legend. legend_border_padding : `float`, optional The fractional whitespace inside the legend border. legend_shadow : `bool`, optional If ``True``, a shadow will be drawn behind legend. legend_rounded_corners : `bool`, optional If ``True``, the frame's corners will be rounded (fancybox). render_axes : `bool`, optional If ``True``, the axes will be rendered. axes_font_name : `str` (See below), optional The font of the axes. Example options :: {serif, sans-serif, cursive, fantasy, monospace} axes_font_size : `int`, optional The font size of the axes. axes_font_style : ``{normal, italic, oblique}``, optional The font style of the axes. axes_font_weight : `str` (See below), optional The font weight of the axes. Example options :: {ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black} axes_x_limits : `float` or (`float`, `float`) or ``None``, optional The limits of the x axis. If `float`, then it sets padding on the right and left of the Image as a percentage of the Image's width. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional The limits of the y axis. If `float`, then it sets padding on the top and bottom of the Image as a percentage of the Image's height. If `tuple` or `list`, then it defines the axis limits. If ``None``, then the limits are set automatically. axes_x_ticks : `list` or `tuple` or ``None``, optional The ticks of the x axis. axes_y_ticks : `list` or `tuple` or ``None``, optional The ticks of the y axis. figure_size : (`float`, `float`) `tuple` or ``None`` optional The size of the figure in inches. Returns ------- renderer : `class` The renderer object. """ # Parse iters iters = _parse_iters(iters, len(self.shapes)) # Create image instance if self.image is None: image = Image(np.zeros((10, 10))) render_image = False else: image = Image(self.image.pixels) # Assign pointclouds to image n_digits = len(str(self.n_iters)) groups = [] subplots_titles = {} iters_offset = -2 if self.initial_shape is not None: iters_offset = -1 for j in iters: if j == 0 and self.initial_shape is not None: name = "Initial" image.landmarks[name] = self.initial_shape elif j in self._reconstruction_indices: name = "Reconstruction" image.landmarks[name] = self.shapes[j] elif j == len(self.shapes) - 1: name = "Final" image.landmarks[name] = self.final_shape else: s = _get_scale_of_iter(j, self._reconstruction_indices) name = "iteration {:0{}d}".format(j - s + iters_offset, n_digits) image.landmarks[name] = self.shapes[j] groups.append(name) subplots_titles[name] = name # Render return view_image_multiple_landmarks( image, groups, with_labels=None, figure_id=figure_id, new_figure=new_figure, subplots_enabled=subplots_enabled, subplots_titles=subplots_titles, render_image=render_image, render_landmarks=True, masked=masked, channels=channels, interpolation=interpolation, cmap_name=cmap_name, alpha=alpha, image_view=True, render_lines=render_lines, line_style=line_style, line_width=line_width, line_colour=line_colour, render_markers=render_markers, marker_style=marker_style, marker_size=marker_size, marker_edge_width=marker_edge_width, marker_edge_colour=marker_edge_colour, marker_face_colour=marker_face_colour, render_numbering=render_numbering, numbers_horizontal_align=numbers_horizontal_align, numbers_vertical_align=numbers_vertical_align, numbers_font_name=numbers_font_name, numbers_font_size=numbers_font_size, numbers_font_style=numbers_font_style, numbers_font_weight=numbers_font_weight, numbers_font_colour=numbers_font_colour, render_legend=render_legend, legend_title=legend_title, legend_font_name=legend_font_name, legend_font_style=legend_font_style, legend_font_size=legend_font_size, legend_font_weight=legend_font_weight, legend_marker_scale=legend_marker_scale, legend_location=legend_location, legend_bbox_to_anchor=legend_bbox_to_anchor, legend_border_axes_pad=legend_border_axes_pad, legend_n_columns=legend_n_columns, legend_horizontal_spacing=legend_horizontal_spacing, legend_vertical_spacing=legend_vertical_spacing, legend_border=legend_border, legend_border_padding=legend_border_padding, legend_shadow=legend_shadow, legend_rounded_corners=legend_rounded_corners, render_axes=render_axes, axes_font_name=axes_font_name, axes_font_size=axes_font_size, axes_font_style=axes_font_style, axes_font_weight=axes_font_weight, axes_x_limits=axes_x_limits, axes_y_limits=axes_y_limits, axes_x_ticks=axes_x_ticks, axes_y_ticks=axes_y_ticks, figure_size=figure_size, )