def _prepare_image(self, image, initial_shape, gt_shape=None): r""" The image is first rescaled wrt the reference_landmarks, then smoothing or gaussian pyramid are computed and, finally, features are extracted from each pyramidal element. """ image.landmarks['initial_shape'] = initial_shape image = image.rescale_to_reference_shape(self.aam.reference_shape, group='initial_shape') if gt_shape: image.landmarks['gt_shape'] = initial_shape if self.aam.n_levels > 1: if self.aam.scaled_reference_frames: pyramid = image.smoothing_pyramid( n_levels=self.aam.n_levels, downscale=self.aam.downscale) else: pyramid = image.gaussian_pyramid( n_levels=self.aam.n_levels, downscale=self.aam.downscale) images = [compute_features(i, self.aam.feature_type) for i in pyramid] images.reverse() else: images = [compute_features(image, self.aam.feature_type)] return images
def aam_builder(images, group=None, label='all', interpolator='scipy', diagonal_range=None, boundary=3, transform_cls=PiecewiseAffineTransform, trilist=None, patch_size=None, n_levels=3, downscale=2, scaled_reference_frames=False, feature_type=None, max_shape_components=None, max_appearance_components=None): r""" Builds an AAM object from a set of landmarked images. Parameters ---------- images: list of :class:`menpo.image.Image` The set of landmarked images from which to build the AAM. group : string, Optional The key of the landmark set that should be used. If None, and if there is only one set of landmarks, this set will be used. Default: None label: string, Optional The label of of the landmark manager that you wish to use. If no label is passed, the convex hull of all landmarks is used. Default: 'all' interpolator:'scipy', Optional The interpolator that should be used to perform the warps. Default: 'scipy' diagonal_range: int, Optional All images will be rescaled to ensure that the scale of their landmarks matches the scale of the mean shape. If int, ensures that the mean shape is scaled so that the diagonal of the bounding box containing it matches the diagonal_range value. If None, the mean landmarks are not rescaled. Note that, because the reference frame is computed from the mean landmarks, this kwarg also specifies the diagonal length of the reference frame (provided that features computation does not change the image size). Default: None boundary: int, Optional The number of pixels to be left as a safe margin on the boundaries of the reference frame (has potential effects on the gradient computation). Default: 3 transform_cls: :class:`menpo.transform.PureAlignmentTransform`, Optional The :class:`menpo.transform.PureAlignmentTransform` that will be used to warp the images. Default: :class:`menpo.transform.PiecewiseAffineTransform` trilist: (t, 3) ndarray, Optional Triangle list that will be used to build the reference frame. If None, defaults to performing Delaunay triangulation on the points. Default: None .. note:: This kwarg will be completely ignored if the kwarg transform_cls is not set :class:`menpo.transform.PiecewiseAffineTransform` or if the kwarg patch_size is not set to None. patch_size: tuple of ints or None, Optional If tuple, the appearance model of the AAM will be obtained by sampling the appearance patches around the landmarks. If None, the standard representation for the AAMs' appearance model will be used instead. Default: None .. note:: If tuple, the kwarg transform_cls will be automatically set to :class:`menpo.transform.TPS`. n_levels: int, Optional The number of multi-resolution pyramidal levels to be used. Default: 3 downscale: float > 1, Optional The downscale factor that will be used to create the different AAM pyramidal levels. Default: 2 scaled_reference_frames: boolean, Optional If False, the resolution of all reference frames used to build the appearance model will be fixed (the original images will be both smoothed and scaled using a Gaussian pyramid). Consequently, all appearance models will have the same dimensionality. If True, the reference frames used to create the appearance model will be themselves scaled (the original images will only be smoothed). Consequently, the dimensionality of all appearance models will be different. Default: False feature_type: string or closure, Optional If None, the appearance model will be build using the original image representation, i.e. no features will be extracted from the original images. If string or closure, the appearance model will be built from a feature representation of the original images: If string, the `ammbuilder` will try to compute image features by executing: feature_image = eval('img.feature_type.' + feature_type + '()') For this to work properly the feature_type needs to be one of Menpo's standard image feature methods. Note that, in this case, the feature computation will be carried out using the respective default options. Non-default feature options and new experimental features can be used by defining a closure. In this case, the closure must define a function that receives an image as input and returns a particular feature representation of that image. For example: def igo_double_from_std_normalized_intensities(image) image = deepcopy(image) image.normalize_std_inplace() return image.feature_type.igo(double_angles=True) See `menpo.image.MaskedNDImage` for details more details on Menpo's standard image features and feature options. Default: None max_shape_components: 0 < int < n_components, Optional If int, it specifies the specific number of components of the original shape model to be retained. Default: None max_appearance_components: 0 < int < n_components, Optional If int, it specifies the specific number of components of the original appearance model to be retained. Default: None Returns ------- aam : :class:`menpo.aam.AAM` The AAM object """ if patch_size is not None: transform_cls = TPS print '- Rescaling images' shapes = [i.landmarks[group][label].lms for i in images] reference_shape = mean_pointcloud(shapes) if diagonal_range: x, y = reference_shape.range() scale = diagonal_range / np.sqrt(x**2 + y**2) Scale(scale, reference_shape.n_dims).apply_inplace(reference_shape) images = [i.rescale_to_reference_shape(reference_shape, group=group, label=label, interpolator=interpolator) for i in images] if scaled_reference_frames: print '- Setting gaussian smoothing generators' generator = [i.smoothing_pyramid(n_levels=n_levels, downscale=downscale) for i in images] else: print '- Setting gaussian pyramid generators' generator = [i.gaussian_pyramid(n_levels=n_levels, downscale=downscale) for i in images] print '- Building model pyramids' shape_models = [] appearance_models = [] # for each level for j in np.arange(n_levels): print ' - Level {}'.format(j) print ' - Computing feature_type' images = [compute_features(g.next(), feature_type) for g in generator] # extract potentially rescaled shapes shapes = [i.landmarks[group][label].lms for i in images] if scaled_reference_frames or j == 0: print ' - Building shape model' if j != 0: shapes = [Scale(1/downscale, n_dims=shapes[0].n_dims).apply(s) for s in shapes] # centralize shapes centered_shapes = [Translation(-s.centre).apply(s) for s in shapes] # align centralized shape using Procrustes Analysis gpa = GeneralizedProcrustesAnalysis(centered_shapes) aligned_shapes = [s.aligned_source for s in gpa.transforms] # build shape model shape_model = PCAModel(aligned_shapes) if max_shape_components is not None: # trim shape model if required shape_model.trim_components(max_shape_components) print ' - Building reference frame' mean_shape = mean_pointcloud(aligned_shapes) if patch_size is not None: # build patch based reference frame reference_frame = build_patch_reference_frame( mean_shape, boundary=boundary, patch_size=patch_size) else: # build reference frame reference_frame = build_reference_frame( mean_shape, boundary=boundary, trilist=trilist) # add shape model to the list shape_models.append(shape_model) print ' - Computing transforms' transforms = [transform_cls(reference_frame.landmarks['source'].lms, i.landmarks[group][label].lms) for i in images] print ' - Warping images' images = [i.warp_to(reference_frame.mask, t, interpolator=interpolator) for i, t in zip(images, transforms)] for i in images: i.landmarks['source'] = reference_frame.landmarks['source'] if patch_size: for i in images: i.build_mask_around_landmarks(patch_size, group='source') else: for i in images: i.constrain_mask_to_landmarks(group='source', trilist=trilist) print ' - Building appearance model' appearance_model = PCAModel(images) # trim appearance model if required if max_appearance_components is not None: appearance_model.trim_components(max_appearance_components) # add appearance model to the list appearance_models.append(appearance_model) # reverse the list of shape and appearance models so that they are # ordered from lower to higher resolution shape_models.reverse() appearance_models.reverse() return AAM(shape_models, appearance_models, transform_cls, feature_type, reference_shape, downscale, patch_size, interpolator)