  
          Parameters
          ----------
          filenames: list
              List of full paths to image file names
          side: str
              The side of the model being trained on (`a` or `b`)
          """
          logger.trace("Process batch: (filenames: '%s', side: '%s')", filenames, side)
  
          if not self._face_cache.cache_full:
              batch = self._face_cache.cache_metadata(filenames)
          else:
              batch = read_image_batch(filenames)
  
          cache = self._face_cache.get_items(filenames)
          batch, landmarks = self._crop_to_center(filenames, cache, batch, side)
          batch = self._apply_mask(filenames, cache, batch, side)
          processed = dict()
  
          # Initialize processing training size on first image
          if not self._processing.initialized:
              self._processing.initialize(batch.shape[1])
  
          # Get Landmarks prior to manipulating the image
          if self._warp_to_landmarks:
              batch_dst_pts = self._get_closest_match(filenames, side, landmarks)
              warp_kwargs = dict(batch_src_points=landmarks, batch_dst_points=batch_dst_pts)
 
</s> remove         # Get Landmarks prior to manipulating the image
        if self._warp_to_landmarks:
            batch_dst_pts = self._get_closest_match(filenames, side, landmarks)
            warp_kwargs = dict(batch_src_points=landmarks, batch_dst_points=batch_dst_pts)
        else:
            warp_kwargs = dict()
 </s> add         Returns
        -------
        feed: list
            4-dimensional array of faces to feed the training the model (:attr:`x` parameter for
            :func:`keras.models.model.train_on_batch`.). The array returned is in the format
            (`batch size`, `height`, `width`, `channels`).
        targets: list
            List of 4-dimensional :class:`numpy.ndarray` objects in the order and size of each
            output of the model. The format of these arrays will be (`batch size`, `height`,
            `width`, `x`). This is the :attr:`y` parameter for
            :func:`keras.models.model.train_on_batch`. The number of channels here will vary.
            The first 3 channels are (rgb/bgr). The 4th channel is the face mask. Any subsequent
            channels are area masks (e.g. eye/mouth masks)
        """
        logger.trace("Process training: (side: '%s', filenames: '%s', images: %s, "  # type:ignore
                     "batch: %s, detected_faces: %s)", self._side, filenames, images.shape,
                     batch.shape, len(detected_faces)) </s> remove         # Random Warp # TODO change masks to have a input mask and a warped target mask
        if self._no_warp:
            processed["feed"] = [self._processing.skip_warp(batch[..., :3])]
 </s> add         # TODO Look at potential for applying mask on input
        # Random Warp
        if self._warp_to_landmarks:
            landmarks = np.array([face.aligned.landmarks for face in detected_faces])
            batch_dst_pts = self._get_closest_match(filenames, landmarks)
            warp_kwargs = dict(batch_src_points=landmarks, batch_dst_points=batch_dst_pts) </s> remove         logger.trace("Processed batch: (filenames: %s, side: '%s', processed: %s)",
                     filenames,
                     side,
                     {k: v.shape if isinstance(v, np.ndarray) else[i.shape for i in v]
                      for k, v in processed.items()})
        return processed
 </s> add         logger.trace("Processed batch: (filenames: %s, side: '%s', "  # type: ignore
                     "feed: %s, targets: %s)", filenames, self._side,
                     [f.shape for f in feed], [t.shape for t in targets]) </s> remove         logger.trace("Cropping training images info: (filenames: %s, side: '%s')", filenames, side)
        aligned = [item["aligned_face"] for item in cache]
 </s> add         logger.trace("Caching closest matches")  # type:ignore
        dst_landmarks = list(landmarks.items())
        dst_points = np.array([lm[1] for lm in dst_landmarks])
        batch_closest_matches: List[Tuple[str, ...]] = [] </s> remove         keys = [os.path.basename(filename) for filename in filenames]
        with self._lock:
            if _check_reset(self):
                self._reset_cache(False)

            needs_cache = [filename
                           for filename, key in zip(filenames, keys)
                           if not self._cache[key]["cached"]]
            logger.trace("Needs cache: %s", needs_cache)

            if not needs_cache:
                # Don't bother reading the metadata if no images in this batch need caching
                logger.debug("All metadata already cached for: %s", keys)
                return read_image_batch(filenames)

            batch, metadata = read_image_batch(filenames, with_metadata=True)

            if len(batch.shape) == 1:
                folder = os.path.dirname(filenames[0])
                details = [
                    "{0} ({1})".format(
                        key, f"{img.shape[1]}px" if isinstance(img, np.ndarray) else type(img))
                    for key, img in zip(keys, batch)]
                msg = (f"There are mismatched image sizes in the folder '{folder}'. All training "
                       "images for each side must have the same dimensions.\nThe batch that "
                       f"failed contains the following files:\n{details}.")
                raise FaceswapError(msg)

            # Populate items into cache
            for filename in needs_cache:
                key = os.path.basename(filename)
                meta = metadata[filenames.index(filename)]

                # Version Check
                self._validate_version(meta, filename)
                if self._partial_load:  # Faces already loaded for Warp-to-landmarks
                    detected_face = self._cache[key]["detected_face"]
                else:
                    detected_face = self._add_aligned_face(filename,
                                                           meta["alignments"],
                                                           batch.shape[1])

                self._add_mask(filename, detected_face)
                for area in ("eye", "mouth"):
                    self._add_localized_mask(filename, detected_face, area)

                self._cache[key]["cached"] = True
            # Update the :attr:`cache_full` attribute
            cache_full = all(item["cached"] for item in self._cache.values())
            if cache_full:
                logger.verbose("Cache filled: '%s'", os.path.dirname(filenames[0]))
                self._cache_full = cache_full

        return batch

    def pre_fill(self, filenames, side):
        """ When warp to landmarks is enabled, the cache must be pre-filled, as each side needs
        access to the other side's alignments.
 </s> add         if not self._face_cache.cache_full:
            raw_faces = self._face_cache.cache_metadata(filenames)
        else:
            raw_faces = read_image_batch(filenames)

        detected_faces = self._face_cache.get_items(filenames)
        logger.trace("filenames: %s, raw_faces: '%s', detected_faces: %s",  # type: ignore
                     filenames, raw_faces.shape, len(detected_faces))
        return raw_faces, detected_faces

    def _crop_to_coverage(self,
                          filenames: List[str],
                          images: np.ndarray,
                          detected_faces: List[DetectedFace],
                          batch: np.ndarray) -> None:
        """ Crops the training image out of the full extract image based on the centering and
        coveage used in the user's configuration settings.

        If legacy extract images are being used then this just returns the extracted batch with
        their corresponding landmarks.

        Uses thread pool execution for about a 33% speed increase @ 64 batch size
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
  
          Parameters
          ----------
          filenames: list
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
            List of full paths to image file names for a single batch
        images: :class:`numpy.ndarray`
            The batch of faces corresponding to the filenames
        detected_faces: list
            List of :class:`~lib.align.DetectedFace` objects with aligned data and masks loaded for
            the current batch
        batch: :class:`numpy.ndarray`
            The pre-allocated batch with images and masks populated for the selected coverage and
            centering
  
          # Get Landmarks prior to manipulating the image
          if self._warp_to_landmarks:
              batch_dst_pts = self._get_closest_match(filenames, side, landmarks)
              warp_kwargs = dict(batch_src_points=landmarks, batch_dst_points=batch_dst_pts)
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Data Augmentation update (#1263)

- lib.detected_face
    - Subclass Masks for Landmark based masks
    - Add training mask propery + methods to DetectedFace
  - lib.training_training
    - subclass TrainingDataGenerator for training and preview data
    - Split cache into own module
    - Reduce thread count to 1 to prevent image corruption + data re-use
    - Process on largest model input/output size rather than stored image size
    - Size and crop masks during caching stage
    - Implement ring buffer for data flow
    - Fix preview reload bug
  - augmentation
    - typing
    - switch color aug order
    - better initialization
    - Fix warp + landmark warp to correctly apply at different image scales
    - Slightly improved warp caching
    - Don't store whether image is_preview. Handle all data as training images implicitly
  - plugins.trainer: Typing and fixes to work with trainingdata refactor 
lib/training/generator.py