              missing = [filenames[idx] for idx in indices]
              msg = ("Files missing alignments for this batch: {}"
                     "\nAt least one of your images does not have a matching entry in your "
                     "alignments file."
                     "\nIf you are training with a mask or using 'warp to landmarks' then every "
                     "face you intend to train on must exist within the alignments file."
                     "\nThe specific files that caused this failure are listed above."
                     "\nMost likely there will be more than just these files missing from the "
                     "alignments file. You can use the Alignments Tool to help identify missing "
                     "alignments".format(missing))
 
</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                 detected_face.load_aligned(None, size=self.size)
                landmarks[detected_face.hash] = detected_face.aligned_landmarks
 </s> add                 detected_faces.append(detected_face)
        logger.debug("Detected Faces count: %s, Skipped faces count: %s",
                     len(detected_faces), skip_count)
        if skip_count != 0:
            logger.warning("%s alignments have been removed as their corresponding faces do not "
                           "exist in the input folder for side %s. Run in verbose mode if you "
                           "wish to see which alignments have been excluded.",
                           skip_count, side.upper())
        return detected_faces

    def _validate_face(self, face, filename, idx, side, side_hashes):
        """ Validate that the currently processing face has a corresponding hash entry and the
        requested mask exists

        Parameters
        ----------
        face: dict
            A face retrieved from an alignments file
        filename: str
            The original frame filename that the given face comes from
        idx: int
            The index of the face in the frame
        side: {'A', 'B'}
            The side that this face belongs to
        side_hashes: set
            A set of hashes that exist in the alignments folder for these faces

        Returns
        -------
        bool
            ``True`` if the face is valid otherwise ``False``

        Raises
        ------
        FaceswapError
            If the current face doesn't pass validation
        """
        mask_type = self._training_opts["mask_type"]
        if mask_type is not None and "mask" not in face:
            msg = ("You have selected a Mask Type in your training configuration options but at "
                   "least one face has no mask stored for it.\nYou should generate the required "
                   "masks with the Mask Tool or set the Mask Type configuration option to `none`."
                   "\nThe face that caused this failure was side: `{}`, frame: `{}`, index: {}. "
                   "However there are probably more faces without masks".format(
                       side.upper(), filename, idx))
            raise FaceswapError(msg)

        if mask_type is not None and mask_type not in face["mask"]:
            msg = ("At least one of your faces does not have the mask `{}` stored for it.\nYou "
                   "should run the Mask Tool to generate this mask for your faceset or "
                   "select a different mask in the training configuration options.\n"
                   "The face that caused this failure was [side: `{}`, frame: `{}`, index: {}]. "
                   "The masks that exist for this face are: {}.\nBe aware that there are probably "
                   "more faces without this Mask Type".format(
                       mask_type, side.upper(), filename, idx, list(face["mask"].keys())))
            raise FaceswapError(msg)

        if face["hash"] not in side_hashes:
            logger.verbose("Skipping alignment for non-existant face in frame '%s' index: %s",
                           filename, idx)
            return False
        return True

    def _check_all_faces(self):
        """ Ensure that all faces in the training folder exist in the alignments file.
        If not, output missing filenames

        Raises
        ------
        FaceswapError
            If there are faces in the training folder which do not exist in the alignments file
        """
        logger.debug("Checking faces exist in alignments")
        missing_alignments = dict()
        for side, train_hashes in self._hashes.items():
            align_hashes = set(face.hash for face in self._detected_faces[side])
            if not align_hashes.issuperset(train_hashes["hashes"]):
                missing_alignments[side] = [
                    os.path.basename(filename)
                    for hsh, filename in zip(train_hashes["hashes"], train_hashes["filenames"])
                    if hsh not in align_hashes]
        if missing_alignments:
            msg = ("There are faces in your training folder(s) which do not exist in your "
                   "alignments file. Training cannot continue. See above for a full list of "
                   "files missing alignments.")
            for side, filelist in missing_alignments.items():
                logger.error("Faces missing alignments for side %s: %s",
                             side.capitalize(), filelist)
            raise FaceswapError(msg)

    # Get landmarks
    def _transform_landmarks(self, side, detected_faces):
        """ Transform frame landmarks to their aligned face variant.

        Parameters
        ----------
        side: {"a" or "b"}
            The side currently being processed
        detected_faces: list
            A list of :class:`lib.faces_detect.DetectedFace` objects

        Returns
        -------
        dict
            The face filenames as keys with the aligned landmarks as value.
        """
        landmarks = dict()
        for face in detected_faces:
            face.load_aligned(None, size=self._training_opts["training_size"])
            for filename in self._hash_to_filenames(side, face.hash):
                landmarks[filename] = face.aligned_landmarks </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             loss = self.model.predictors[self.side].train_on_batch(*batch)
 </s> add             loss = self._model.predictors[self._side].train_on_batch(model_inputs, model_targets) </s> remove             if not self.args.redirect_gui:
 </s> add             if not self._args.redirect_gui:
                   "\nIf you are using 'warp to landmarks' then every "
              missing = [filenames[idx] for idx in indices]
              msg = ("Files missing alignments for this batch: {}"
                     "\nAt least one of your images does not have a matching entry in your "
                     "alignments file."
                   "\nIf you are using 'warp to landmarks' then every "
                     "face you intend to train on must exist within the alignments file."
                     "\nThe specific files that caused this failure are listed above."
                     "\nMost likely there will be more than just these files missing from the "
                     "alignments file. You can use the Alignments Tool to help identify missing "
                     "alignments".format(missing))
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. 
lib/training_data.py