              List of :class:`numpy.ndarray` faces with the opaque mask layer applied
          """
          orig_masks = np.tile(1 - np.rint(masks), 3)
          orig_masks[np.where((orig_masks == [1., 1., 1.]).all(axis=3))] = [0., 0., 1.]
  
          if faces[-1].shape[-1] == 4:  # Mask contained in alpha channel of predictions
              pred_masks = [np.tile(1 - np.rint(face[..., -1])[..., None], 3) for face in faces[-2:]]
              for swap_masks in pred_masks:
                  swap_masks[np.where((swap_masks == [1., 1., 1.]).all(axis=3))] = [0., 0., 1.]
              faces[-2:] = [face[..., :-1] for face in faces[-2:]]
 
</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         full, faces = samples[:2]
 </s> add         faces, full = samples[:2] </s> remove         landmarks = np.array([face.landmarks for face in aligned])
        cropped = np.array([align.extract_face(img) for align, img in zip(aligned, batch)])
        return cropped, landmarks
 </s> add  </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, ...]] = []
        masks3: Union[List[np.ndarray], np.ndarray] = []
              List of :class:`numpy.ndarray` faces with the opaque mask layer applied
          """
          orig_masks = np.tile(1 - np.rint(masks), 3)
          orig_masks[np.where((orig_masks == [1., 1., 1.]).all(axis=3))] = [0., 0., 1.]
        masks3: Union[List[np.ndarray], np.ndarray] = []
  
          if faces[-1].shape[-1] == 4:  # Mask contained in alpha channel of predictions
              pred_masks = [np.tile(1 - np.rint(face[..., -1])[..., None], 3) for face in faces[-2:]]
              for swap_masks in pred_masks:
                  swap_masks[np.where((swap_masks == [1., 1., 1.]).all(axis=3))] = [0., 0., 1.]
              faces[-2:] = [face[..., :-1] for face in faces[-2:]]
keep keep keep add keep 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 
plugins/train/trainer/_base.py