                  model_inputs.append(side_inputs)
                  model_targets.append(side_targets)
          return model_inputs, model_targets
  
      def _compile_mask_targets(self, targets, masks, additional_masks):
          """ Compile the masks into the targets for penalized loss and for targeted learning.
  
          Penalized loss expects the target mask to be included for all outputs in the 4th channel
          of the targets. Any additional masks are placed into subsequent channels for extraction
          by the relevant loss functions.
  
 
</s> remove         Penalized loss expects the target mask to be included for all outputs in the 4th channel
        of the targets. Any additional masks are placed into subsequent channels for extraction
        by the relevant loss functions.
 </s> add     def generate_preview(self,
                         is_timelapse: bool = False) -> Dict[Literal["a", "b"], List[np.ndarray]]:
        """ Generate the images for preview window or timelapse </s> remove             batch = next(self._feeds[side])
            side_inputs = batch["feed"]
            side_targets = self._compile_mask_targets(batch["targets"],
                                                      batch["masks"],
                                                      batch.get("additional_masks", None))
            if self._model.config["learn_mask"]:
                side_targets = side_targets + [batch["masks"]]
            logger.trace("side: %s, input_shapes: %s, target_shapes: %s",
                         side, [i.shape for i in side_inputs], [i.shape for i in side_targets])
            if get_backend() == "amd":
                model_inputs.extend(side_inputs)
                model_targets.extend(side_targets)
            else:
                model_inputs.append(side_inputs)
                model_targets.append(side_targets)
        return model_inputs, model_targets
 </s> add             side_feed, side_targets = next(self._feeds[side])
            if self._model.config["learn_mask"]:  # Add the face mask as it's own target
                side_targets += [side_targets[-1][..., 3][..., None]]
            logger.trace("side: %s, input_shapes: %s, target_shapes: %s",  # type: ignore
                         side, side_feed.shape, [i.shape for i in side_targets])
            model_inputs.append([side_feed])
            model_targets.append(side_targets) </s> remove         targets: list
            The targets for the model, with the mask as the final entry in the list
        masks: list
            The masks for the model
        additional_masks: list or ``None``
            Any additional masks for the model, or ``None`` if no additional masks are required
 </s> add         is_timelapse, bool, optional
            ``True`` if preview is to be generated for a Timelapse otherwise ``False``.
            Default: ``False`` </s> remove         grids = np.mgrid[0:p_mx:complex(self._training_size), 0:p_mx:complex(self._training_size)]

        self._constants = dict(clahe_base_contrast=clahe_base_contrast,
                               tgt_slices=tgt_slices,
                               warp_mapx=warp_mapx,
                               warp_mapy=warp_mapy,
                               warp_pad=warp_pad,
                               warp_slices=warp_slices,
                               warp_lm_edge_anchors=edge_anchors,
                               warp_lm_grids=grids)
        self.initialized = True
        logger.debug("Initialized constants: %s", {k: str(v) if isinstance(v, np.ndarray) else v
                                                   for k, v in self._constants.items()})

    # <<< TARGET IMAGES >>> #
    def get_targets(self, batch):
        """ Returns the target images, and masks, if required.

        Parameters
        ----------
        batch: :class:`numpy.ndarray`
            This should be a 4+-dimensional array of training images in the format (`batchsize`,
            `height`, `width`, `channels`). Targets should be requested after performing image
            transformations but prior to performing warps.

            The 4th channel should be the mask. Any channels above the 4th should be any additional
            masks that are requested.

        Returns
        -------
        dict
            The following keys will be within the returned dictionary:

            * **targets** (`list`) - A list of 4-dimensional :class:`numpy.ndarray` s in the \
            order and size of each output of the model as defined in :attr:`output_shapes`. The \
            format of these arrays will be (`batchsize`, `height`, `width`, `3`). **NB:** \
            masks are not included in the `targets` list. If masks are to be included in the \
            output they will be returned as their own item from the `masks` key.

            * **masks** (:class:`numpy.ndarray`) - A 4-dimensional array containing the target \
            masks in the format (`batchsize`, `height`, `width`, `1`).
        """
        logger.trace("Compiling targets: batch shape: %s", batch.shape)
        slices = self._constants["tgt_slices"]
        target_batch = [np.array([cv2.resize(image[slices, slices, :],
                                             (size, size),
                                             cv2.INTER_AREA)
                                  for image in batch], dtype='float32') / 255.
                        for size in self._output_sizes]
        logger.trace("Target image shapes: %s",
                     [tgt_images.shape for tgt_images in target_batch])

        retval = self._separate_target_mask(target_batch)
        logger.trace("Final targets: %s",
                     {k: v.shape if isinstance(v, np.ndarray) else [img.shape for img in v]
                      for k, v in retval.items()})
        return retval

    @staticmethod
    def _separate_target_mask(target_batch):
        """ Return the batch and the batch of final masks

        Parameters
        ----------
        target_batch: list
            List of 4 dimension :class:`numpy.ndarray` objects resized the model outputs.
            The 4th channel of the array contains the face mask, any additional channels after
            this are additional masks (e.g. eye mask and mouth mask)

        Returns
        -------
        dict:
            The targets and the masks separated into their own items. The targets are a list of
            3 channel, 4 dimensional :class:`numpy.ndarray` objects sized for each output from the
            model. The masks are a :class:`numpy.ndarray` of the final output size. Any additional
            masks(e.g. eye and mouth masks) will be collated together into a :class:`numpy.ndarray`
            of the final output size. The number of channels will be the number of additional
            masks available
        """
        logger.trace("target_batch shapes: %s", [tgt.shape for tgt in target_batch])
        retval = dict(targets=[batch[..., :3] for batch in target_batch],
                      masks=target_batch[-1][..., 3][..., None])
        if target_batch[-1].shape[-1] > 4:
            retval["additional_masks"] = target_batch[-1][..., 4:]
        logger.trace("returning: %s", {k: v.shape if isinstance(v, np.ndarray) else [tgt.shape
                                                                                     for tgt in v]
                                       for k, v in retval.items()})
 </s> add         grids = np.mgrid[0: p_mx: complex(self._processing_size),  # type: ignore
                         0: p_mx: complex(self._processing_size)]  # type: ignore
        retval = AugConstants(clahe_base_contrast=max(2, self._processing_size // 128),
                              clahe_chance=int(self._config.get("color_clahe_chance", 50)) / 100,
                              clahe_max_size=int(self._config.get("color_clahe_max_size", 4)),
                              lab_adjust=lab_adjust,
                              transform_rotation=int(self._config.get("rotation_range", 10)),
                              transform_zoom=int(self._config.get("zoom_amount", 5)) / 100,
                              transform_shift=tform_shift,
                              warp_maps=np.stack((warp_mapx, warp_mapy), axis=1),
                              warp_pad=(warp_pad, warp_pad),
                              warp_slices=slice(warp_pad // 10, -warp_pad // 10),
                              warp_lm_edge_anchors=edge_anchors,
                              warp_lm_grids=grids)
        logger.debug("Initialized constants: %s", retval) </s> remove         list
            The targets for the model with the mask compiled into the 4th channel. The original
            mask is still output as the final item in the list
        """
        if not self._model.config["penalized_mask_loss"] and additional_masks is None:
            logger.trace("No masks to compile. Returning targets")
            return targets

        if not self._model.config["penalized_mask_loss"] and additional_masks is not None:
            masks = additional_masks
        elif additional_masks is not None:
            masks = np.concatenate((masks, additional_masks), axis=-1)

        for idx, tgt in enumerate(targets):
            tgt_dim = tgt.shape[1]
            if tgt_dim == masks.shape[1]:
                add_masks = masks
            else:
                add_masks = np.array([cv2.resize(mask, (tgt_dim, tgt_dim))
                                      for mask in masks])
                if add_masks.ndim == 3:
                    add_masks = add_masks[..., None]
            targets[idx] = np.concatenate((tgt, add_masks), axis=-1)
        logger.trace("masks added to targets: %s", [tgt.shape for tgt in targets])
        return targets

    def generate_preview(self, do_preview):
        """ Generate the preview images.

        Parameters
        ----------
        do_preview: bool
            Whether the previews should be generated. ``True`` if they should ``False`` if they
            should not be generated, in which case currently stored previews should be deleted.
 </s> add         dict
            Dictionary for side A and B of list of numpy arrays corresponding to the
            samples, targets and masks for this preview
        return model_inputs, model_targets
                  model_inputs.append(side_inputs)
                  model_targets.append(side_targets)
          return model_inputs, model_targets
  
        return model_inputs, model_targets
        return model_inputs, model_targets
  
          Penalized loss expects the target mask to be included for all outputs in the 4th channel
          of the targets. Any additional masks are placed into subsequent channels for extraction
          by the relevant loss functions.
  
keep keep keep keep 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 
plugins/train/trainer/_base.py