                  landmarks[detected_face.hash] = detected_face.aligned_landmarks
          return landmarks
  
  
  def stack_images(images):
      """ Stack images """
      logger.debug("Stack images")
  
      def get_transpose_axes(num):
          if num % 2 == 0:
              logger.debug("Even number of images to stack")
 
</s> remove     def transform_landmarks(self, alignments):
        """ For each face transform landmarks and return """
        landmarks = dict()
        for _, faces, _, _ in alignments.yield_faces():
            for face in faces:
 </s> add     def _to_detected_faces(self, alignments, side):
        """ Convert alignments to DetectedFace objects.

        Filter the detected faces to only those that exist in the training folders.

        Parameters
        ----------
        alignments: :class:`lib.alignments.Alignments`
            The alignments for the current faces
        side: {"a" or "b"}
            The side being processed

        Returns
        -------
        list
            List of :class:`lib.faces_detect.DetectedFace` objects
        """
        skip_count = 0
        side_hashes = set(self._hashes[side]["hashes"])
        detected_faces = []
        for _, faces, _, filename in alignments.yield_faces():
            for idx, face in enumerate(faces):
                if not self._validate_face(face, filename, idx, side, side_hashes):
                    skip_count += 1
                    continue </s> remove     def set_training_data(self):
        """ Override to set model specific training data.

            super() this method for defaults otherwise be sure to add """
        logger.debug("Setting training data")
        # Force number of preview images to between 2 and 16
        self.training_opts["training_size"] = self.state.training_size
        self.training_opts["no_logs"] = self.state.current_session["no_logs"]
        self.training_opts["mask_type"] = self.config.get("mask_type", None)
        self.training_opts["coverage_ratio"] = self.calculate_coverage_ratio()
        logger.debug("Set training data: %s", self.training_opts)

 </s> add  </s> remove     def set_tensorboard(self):
        """ Set up tensorboard callback """
        if self.model.training_opts["no_logs"]:
 </s> add         alignments = TrainingAlignments(self._model.training_opts, self._images)
        if self._landmarks_required:
            logger.debug("Adding landmarks to training opts dict")
            self._model.training_opts["landmarks"] = alignments.landmarks

        if self._use_mask:
            logger.debug("Adding masks to training opts dict")
            self._model.training_opts["masks"] = alignments.masks

    def _set_tensorboard(self):
        """ Set up Tensorboard callback for logging loss.

        Bypassed if command line option "no-logs" has been selected.

        Returns
        -------
        dict:
            2 Dictionary keys of "a" and "b" the values of which are the
        :class:`tf.keras.callbacks.TensorBoard` objects for the respective sides.
        """
        if self._model.training_opts["no_logs"]: </s> remove     def _set__mask_class(self):
        """ Returns the correct mask class from :mod:`lib`.model.masks` as defined in the
        :attr:`mask_type` parameter. """
        mask_type = self._training_opts.get("mask_type", None)
        if mask_type:
            logger.debug("Mask type: '%s'", mask_type)
            _mask_class = getattr(masks, mask_type)
        else:
            _mask_class = None
        logger.debug("Mask class: %s", _mask_class)
        return _mask_class

 </s> add  </s> remove     def to_full_frame(self, side, samples, predictions):
        """ Patch the images into the full frame """
 </s> add     def _to_full_frame(self, side, samples, predictions):
        """ Patch targets and prediction images into images of training image size.

        Parameters
        ----------
        side: {"a" or "b"}
            The side that these samples are for
        samples: list
            List of :class:`numpy.ndarray` of target images and feed images
        predictions: list
            List of :class: `numpy.ndarray` of predictions from the model
        """
    Returns
    -------
    :class:`numpy.ndarray`
        The stacked preview images
    """
                  landmarks[detected_face.hash] = detected_face.aligned_landmarks
          return landmarks
  
  
    Returns
    -------
    :class:`numpy.ndarray`
        The stacked preview images
    """
    Returns
    -------
    :class:`numpy.ndarray`
        The stacked preview images
    """
      logger.debug("Stack images")
  
      def get_transpose_axes(num):
          if num % 2 == 0:
              logger.debug("Even number of images to stack")
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. 
plugins/train/trainer/_base.py