          batch_dst_points = np.array([landmarks[choice(fname)] for fname in closest_matches])
          logger.trace("Returning: (batch_dst_points: %s)", batch_dst_points.shape)
          return batch_dst_points
  
      def _cache_closest_matches(self, filenames, batch_src_points, landmarks):
          """ Cache the nearest landmarks for this batch """
          logger.trace("Caching closest matches")
          dst_landmarks = list(landmarks.items())
          dst_points = np.array([lm[1] for lm in dst_landmarks])
          batch_closest_matches = list()
  
          for filename, src_points in zip(filenames, batch_src_points):
              closest = (np.mean(np.square(src_points - dst_points), axis=(1, 2))).argsort()[:10]
              closest_matches = tuple(dst_landmarks[i][0] for i in closest)
              self._nearest_landmarks[os.path.basename(filename)] = closest_matches
 
</s> remove         batch_dst_points = np.array([landmarks[choice(fname)] for fname in closest_matches])
        logger.trace("Returning: (batch_dst_points: %s)", batch_dst_points.shape)
        return batch_dst_points
 </s> add         if not self._use_mask:
            mask = np.zeros_like(batch[..., 0])[..., None] + 255
            batch = np.concatenate([batch, mask], axis=-1) </s> remove         if self._face_cache.extract_version == 1.0:
            # Legacy extract. Don't crop, just return batch with landmarks
            return batch, np.array([face.landmarks for face in aligned])
 </s> add         for filename, src_points in zip(filenames, batch_src_points):
            closest = (np.mean(np.square(src_points - dst_points), axis=(1, 2))).argsort()[:10]
            closest_matches = tuple(dst_landmarks[i][0] for i in closest)
            self._nearest_landmarks[os.path.basename(filename)] = closest_matches
            batch_closest_matches.append(closest_matches)
        logger.trace("Cached closest matches")  # type:ignore
        return batch_closest_matches </s> remove         batch: :class:`numpy.ndarray`
            The centered faces cropped out of the loaded batch
        landmarks: :class:`numpy.ndarray`
            The aligned landmarks for this batch. NB: The aligned landmarks do not directly
            correspond to the size of the extracted face. They are scaled to the source training
            image, not the sub-image.
 </s> add         :class:`np.ndarray`
            Randomly selected closest matches from the other side's landmarks
        """
        logger.trace("Retrieving closest matched landmarks: (filenames: '%s', "  # type: ignore
                     "src_points: '%s')", filenames, batch_src_points)
        lm_side: Literal["a", "b"] = "a" if self._side == "b" else "b"
        other_cache = get_cache(lm_side)
        landmarks = other_cache.aligned_landmarks

        try:
            closest_matches = [self._nearest_landmarks[os.path.basename(filename)]
                               for filename in filenames]
        except KeyError:
            # Resize mismatched training image size landmarks
            sizes = {side: cache.size for side, cache in zip((self._side, lm_side),
                                                             (self._face_cache, other_cache))}
            if len(set(sizes.values())) > 1:
                scale = sizes[self._side] / sizes[lm_side]
                landmarks = {key: lms * scale for key, lms in landmarks.items()}
            closest_matches = self._cache_closest_matches(filenames, batch_src_points, landmarks)

        batch_dst_points = np.array([landmarks[choice(fname)] for fname in closest_matches])
        logger.trace("Returning: (batch_dst_points: %s)", batch_dst_points.shape)  # type: ignore
        return batch_dst_points

    def _cache_closest_matches(self,
                               filenames: List[str],
                               batch_src_points: np.ndarray,
                               landmarks: Dict[str, np.ndarray]) -> List[Tuple[str, ...]]:
        """ Cache the nearest landmarks for this batch

        Parameters
        ----------
        filenames: list
            Filenames for current batch
        batch_src_points: :class:`np.ndarray`
            The source landmarks for the current batch
        landmarks: dict
            The destination landmarks with associated filenames </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     def _random_warp_landmarks(self, batch, batch_src_points, batch_dst_points):
        """ From dfaker. Warp the image to a similar set of landmarks from the opposite side """
        logger.trace("Randomly warping landmarks")
        edge_anchors = self._constants["warp_lm_edge_anchors"]
        grids = self._constants["warp_lm_grids"]
        slices = self._constants["tgt_slices"]
 </s> add     def _random_warp_landmarks(self,
                               batch: np.ndarray,
                               batch_src_points: np.ndarray,
                               batch_dst_points: np.ndarray) -> np.ndarray:
        """ From dfaker. Warp the image to a similar set of landmarks from the opposite side

        batch: :class:`numpy.ndarray`
            The batch should be a 4-dimensional array of shape (`batchsize`, `height`, `width`,
            `3`) and in `BGR` format.
        batch_src_points :class:`numpy.ndarray`
            A batch of 68 point landmarks for the source faces. This is a 3-dimensional array in
            the shape (`batchsize`, `68`, `2`).
        batch_dst_points :class:`numpy.ndarray`
            A batch of randomly chosen closest match destination faces landmarks. This is a
            3-dimensional array in the shape (`batchsize`, `68`, `2`).

        Returns
        ----------
        :class:`numpy.ndarray`
            A 4-dimensional array of the same shape as :attr:`batch` with warping applied.
        """
        logger.trace("Randomly warping landmarks")  # type: ignore
        edge_anchors = self._constants.warp_lm_edge_anchors
        grids = self._constants.warp_lm_grids
        feed = self._to_float32(batch[..., :4])
          batch_dst_points = np.array([landmarks[choice(fname)] for fname in closest_matches])
          logger.trace("Returning: (batch_dst_points: %s)", batch_dst_points.shape)
          return batch_dst_points
  
        feed = self._to_float32(batch[..., :4])
        feed = self._to_float32(batch[..., :4])
        feed = self._to_float32(batch[..., :4])
        feed = self._to_float32(batch[..., :4])
        feed = self._to_float32(batch[..., :4])
        feed = self._to_float32(batch[..., :4])
  
          for filename, src_points in zip(filenames, batch_src_points):
              closest = (np.mean(np.square(src_points - dst_points), axis=(1, 2))).argsort()[:10]
              closest_matches = tuple(dst_landmarks[i][0] for i in closest)
              self._nearest_landmarks[os.path.basename(filename)] = closest_matches
keep keep keep keep 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