          return processed
  
      def _get_landmarks(self, filenames, batch, side):
          """ Obtains the 68 Point Landmarks for the images in this batch. This is only called if
          config item ``warp_to_landmarks`` is ``True`` or if :attr:`mask_type` is not ``None``. If
          the landmarks for an image cannot be found, then an error is raised. """
          logger.trace("Retrieving landmarks: (filenames: %s, side: '%s')", filenames, side)
          src_points = [self._landmarks[side].get(sha1(face).hexdigest(), None) for face in batch]
  
          # Raise error on missing alignments
          if not all(isinstance(pts, np.ndarray) for pts in src_points):
 
</s> remove         src_points = [self._landmarks[side].get(sha1(face).hexdigest(), None) for face in batch]

 </s> add         src_points = [self._landmarks[side].get(filename, None) for filename in filenames] </s> remove     def _get_landmarks(self, filenames, batch, side):
 </s> add     def _apply_mask(self, filenames, batch, side):
        """ Applies the mask to the 4th channel of the image. If masks are not being used
        applies a dummy all ones mask """
        logger.trace("Input batch shape: %s, side: %s", batch.shape, side)
        if self._masks is None:
            logger.trace("Creating dummy masks. side: %s", side)
            masks = np.ones_like(batch[..., :1], dtype=batch.dtype)
        else:
            logger.trace("Obtaining masks for batch. side: %s", side)
            masks = np.array([self._masks[side][filename].mask
                              for filename, face in zip(filenames, batch)], dtype=batch.dtype)
            masks = self._resize_masks(batch.shape[1], masks)

        logger.trace("masks shape: %s", masks.shape)
        batch = np.concatenate((batch, masks), axis=-1)
        logger.trace("Output batch shape: %s, side: %s", batch.shape, side)
        return batch

    @staticmethod
    def _resize_masks(target_size, masks):
        """ Resize the masks to the target size """
        logger.trace("target size: %s, masks shape: %s", target_size, masks.shape)
        mask_size = masks.shape[1]
        if target_size == mask_size:
            logger.trace("Mask and targets the same size. Not resizing")
            return masks
        interpolator = cv2.INTER_CUBIC if mask_size < target_size else cv2.INTER_AREA
        masks = np.array([cv2.resize(mask,
                                     (target_size, target_size),
                                     interpolation=interpolator)[..., None]
                          for mask in masks])
        logger.trace("Resized masks: %s", masks.shape)
        return masks

    def _get_landmarks(self, filenames, side): </s> remove             indices = [idx for idx, hsh in enumerate(src_points) if hsh is None]
            missing = [filenames[idx] for idx in indices]
 </s> add             missing = [filenames[idx] for idx, pts in enumerate(src_points) if pts is None] </s> remove         batch = read_image_batch(filenames)
 </s> add         filenames, batch = read_image_batch(filenames)
        batch = self._apply_mask(filenames, batch, side) </s> remove         to_landmarks = self._training_opts["warp_to_landmarks"]
 </s> add 
        config item ``warp_to_landmarks`` is ``True``. If the landmarks for an image cannot be
        found, then an error is raised. """
          return processed
  
      def _get_landmarks(self, filenames, batch, side):
          """ Obtains the 68 Point Landmarks for the images in this batch. This is only called if
        config item ``warp_to_landmarks`` is ``True``. If the landmarks for an image cannot be
        found, then an error is raised. """
        config item ``warp_to_landmarks`` is ``True``. If the landmarks for an image cannot be
        found, then an error is raised. """
          logger.trace("Retrieving landmarks: (filenames: %s, side: '%s')", filenames, side)
          src_points = [self._landmarks[side].get(sha1(face).hexdigest(), None) for face in batch]
  
          # Raise error on missing alignments
          if not all(isinstance(pts, np.ndarray) for pts in src_points):
keep keep keep keep replace replace keep keep keep keep keep
 Smart Masks - Training Implementation (#914)

* Smart Masks - Training

- Reinstate smart mask training code
- Reinstate mask_type back to model.config
- change 'replicate_input_mask to 'learn_mask'
- Add learn mask option
- Add mask loading from alignments to plugins.train.trainer
- Add mask_blur and mask threshold options
- _base.py - Pass mask options through training_opts dict
- plugins.train.model - check for mask_type not None for learn_mask and penalized_mask_loss
- Limit alignments loading to just those faces that appear in the training folder
- Raise error if not all training images have an alignment, and alignment file is required
- lib.training_data - Mask generation code
- lib.faces_detect - cv2 dimension stripping bugfix
- Remove cv2 linting code

* Update mask helptext in cli.py

* Fix Warp to Landmarks
Remove SHA1 hashing from training data

* Update mask training config

* Capture missing masks at training init

* lib.image.read_image_batch - Return filenames with batch for ordering

* scripts.train - Documentation

* plugins.train.trainer - documentation

* Ensure backward compatibility.
Fix convert for new predicted masks

* Update removed masks to components for legacy models. 
lib/training_data.py