          loss_funcs = []
          for idx, loss_name in enumerate(self.names):
              if loss_name.startswith("mask"):
                  loss_funcs.append(self.selected_mask_loss)
              elif self.mask_input is not None and self.config.get("penalized_mask_loss", False):
                  face_size = self.output_shapes[idx][1]
                  mask_size = self.mask_shape[1]
                  scaling = face_size / mask_size
                  logger.debug("face_size: %s mask_size: %s, mask_scaling: %s",
                               face_size, mask_size, scaling)
 
</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         self.model = model
        self.use_mask = use_mask
 </s> add         self._model = model
        self._use_mask = use_mask </s> remove         images = [executor.submit(read_image, filename, raise_error=True)
                  for filename in filenames]
        batch = np.array([future.result() for future in futures.as_completed(images)])
    logger.trace("Returning images: %s", batch.shape)
    return batch
 </s> add         images = {executor.submit(read_image, filename, raise_error=True): filename
                  for filename in filenames}
        batch = []
        filenames = []
        for future in futures.as_completed(images):
            batch.append(future.result())
            filenames.append(images[future])
        batch = np.array(batch)
    logger.trace("Returning images: (filenames: %s, batch shape: %s)", filenames, batch.shape)
    return filenames, batch </s> remove         self.coverage_ratio = coverage_ratio
        self.scaling = scaling
 </s> add         self._coverage_ratio = coverage_ratio
        self._scaling = scaling </s> remove         text_sizes = [cv2.getTextSize(texts[idx],  # pylint: disable=no-member
                                      font,
                                      self.scaling * 0.8,
                                      1)[0]
 </s> add         text_sizes = [cv2.getTextSize(texts[idx], font, self._scaling * 0.8, 1)[0]
            elif self.config["penalized_mask_loss"] and self.config["mask_type"] is not None:
          loss_funcs = []
          for idx, loss_name in enumerate(self.names):
              if loss_name.startswith("mask"):
                  loss_funcs.append(self.selected_mask_loss)
            elif self.config["penalized_mask_loss"] and self.config["mask_type"] is not None:
                  face_size = self.output_shapes[idx][1]
                  mask_size = self.mask_shape[1]
                  scaling = face_size / mask_size
                  logger.debug("face_size: %s mask_size: %s, mask_scaling: %s",
                               face_size, mask_size, scaling)
keep keep keep keep 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. 
plugins/train/model/_base.py