          full paths are passed in, they are stripped to base name for use as the cache key.
      config: dict
          The user selected training configuration options
      """
      def __init__(self, filenames, config):
          self._lock = Lock()
          self._cache = {os.path.basename(filename): dict(cached=False) for filename in filenames}
          self._aligned_landmarks = None
          self._partial_load = False
          self._cache_full = False
          self._extract_version = None
          self._has_reset = False
          self._size = None
  
          self._centering = config["centering"]
          self._config = config
  
      @property
      def cache_full(self):
          """bool: ``True`` if the cache has been fully populated. ``False`` if there are items still
 
</s> remove         The user selected training configuration options
 </s> add         The configuration `dict` generated from :file:`config.train.ini` containing the trainer
        plugin configuration options.
    side: {'a' or 'b'}
        The side of the model that this iterator is for.
    images: list
        A list of image paths that will be used to compile the final augmented data from.
    batch_size: int
        The batch size for this iterator. Images will be returned in :class:`numpy.ndarray`
        objects of this size from the iterator. </s> remove     filenames: list
        The filenames of all the images. This can either be the full path or the base name. If the
        full paths are passed in, they are stripped to base name for use as the cache key.
 </s> add     model: :class:`~plugins.train.model.ModelBase`
        The model that this data generator is feeding </s> remove     def cache_full(self):
        """bool: ``True`` if the cache has been fully populated. ``False`` if there are items still
        to be cached. """
        if self._cache_full:
            return self._cache_full
        with self._lock:
            return self._cache_full

    @property
    def partially_loaded(self):
        """ bool: ``True`` if the cache has been partially loaded for Warp To Landmarks otherwise
        ``False`` """
        if self._partial_load:
            return self._partial_load
        with self._lock:
            return self._partial_load
 </s> add     def _total_channels(self) -> int:
        """int: The total number of channels, including mask channels that the target image
        should hold. """
        channels = 3
        if self._config["mask_type"] and (self._config["learn_mask"] or
                                          self._config["penalized_mask_loss"]):
            channels += 1

        mults = [area for area in ["eye", "mouth"] if int(self._config[f"{area}_multiplier"]) > 1]
        if self._config["penalized_mask_loss"] and mults:
            channels += len(mults)
        return channels

    def minibatch_ab(self, do_shuffle: bool = True) -> Generator[BatchType, None, None]:
        """ A Background iterator to return augmented images, samples and targets. </s> remove logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

_FACE_CACHES = dict()


def _get_cache(side, filenames, config):
    """ Obtain a :class:`_Cache` object for the given side. If the object does not pre-exist then
    create it.

    Parameters
    ----------
    side: str
        `"a"` or `"b"`. The side of the model to obtain the cache for
    filenames: list
        The filenames of all the images. This can either be the full path or the base name. If the
        full paths are passed in, they are stripped to base name for use as the cache key.
    config: dict
        The user selected training configuration options

    Returns
    -------
    :class:`_Cache`
        The face meta information cache for the requested side
    """
    if not _FACE_CACHES.get(side):
        logger.debug("Creating cache. Side: %s", side)
        _FACE_CACHES[side] = _Cache(filenames, config)
    return _FACE_CACHES[side]


def _check_reset(face_cache):
    """ Check whether a given cache needs to be reset because a face centering change has been
    detected in the other cache.

    Parameters
    ----------
    face_cache: :class:`_Cache`
        The cache object that is checking whether it should reset

    Returns
    -------
    bool
        ``True`` if the given object should reset the cache, otherwise ``False``
    """
    check_cache = next((cache for cache in _FACE_CACHES.values() if cache != face_cache), None)
    retval = check_cache if check_cache is None else check_cache.check_reset()
    return retval
 </s> add if sys.version_info < (3, 8):
    from typing_extensions import get_args, Literal
else:
    from typing import get_args, Literal </s> remove         if set_flag:
            logger.warning("You are using legacy extracted faces but have selected '%s' centering "
                           "which is incompatible. Switching centering to 'legacy'",
                           self._centering)
        self._config["centering"] = "legacy"
        self._centering = "legacy"
        self._cache = {key: dict(cached=False) for key in self._cache}
        self._cache_full = False
        self._size = None
        if set_flag:
            self._has_reset = True

    def _add_aligned_face(self, filename, alignments, image_size):
        """ Add a :class:`lib.align.AlignedFace` object to the cache.
 </s> add         if not self._use_mask:
            return

        masks = np.array([face.get_training_masks() for face in detected_faces])
        batch[..., 3:] = masks

        logger.trace("side: %s, masks: %s, batch: %s",  # type: ignore
                     self._side, masks.shape, batch.shape)

    def _process_batch(self, filenames: List[str]) -> BatchType:
        """ Prepares data for feeding through subclassed methods.

        If this is the first time a face has been loaded, then it's meta data is extracted from the
        png header and added to :attr:`_face_cache`
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
          full paths are passed in, they are stripped to base name for use as the cache key.
      config: dict
          The user selected training configuration options
      """
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
    def __init__(self,
                 config: ConfigType,
                 model: "ModelBase",
                 side: Literal["a", "b"],
                 images: List[str],
                 batch_size: int) -> None:
        logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "  # type: ignore
                     "batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
                     len(images), batch_size, config)
          self._config = config
  
      @property
      def cache_full(self):
          """bool: ``True`` if the cache has been fully populated. ``False`` if there are items still
keep keep keep keep 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