                            for filename in set(filenames)}
          for future in futures.as_completed(images):
              return_idx = return_indices[images[future]].pop()
              if with_metadata:
                  batch[return_idx], meta[return_idx] = future.result()
              else:
                  batch[return_idx] = future.result()
  
      batch = np.array(batch)
      retval = (batch, meta) if with_metadata else batch
 
</s> remove                 batch[return_idx] = future.result()
 </s> add                 batch[ret_idx] = future.result() </s> remove             return_idx = return_indices[images[future]].pop()
 </s> add             ret_idx = images[future] </s> remove                                   raise_error=True, with_metadata=with_metadata): filename
                  for filename in filenames}
        batch = [None for _ in range(len(filenames))]
        if with_metadata:
            meta = [None for _ in range(len(filenames))]
        # There is no guarantee that the same filename will not be passed through multiple times
        # (and when shuffle is true this can definitely happen), so we can't just call
        # filenames.index().
        return_indices = {filename: [idx for idx, fname in enumerate(filenames)
                                     if fname == filename]
                          for filename in set(filenames)}
 </s> add                                   raise_error=True, with_metadata=with_metadata): idx
                  for idx, filename in enumerate(filenames)} </s> remove     executor = futures.ThreadPoolExecutor()
    with executor:
 </s> add     batch = [None for _ in range(len(filenames))]
    if with_metadata:
        meta = [None for _ in range(len(filenames))]

    with futures.ThreadPoolExecutor() as executor: </s> remove             side_images = images[side] if images is not None else self._target[side]
            side_masks = masks[side] if masks is not None else self._masks[side]
            side_samples = samples[side] if samples is not None else self._samples[side]
            retval[side] = [side_samples[0:num_images],
                            side_images[0:num_images],
                            side_masks[0:num_images]]
 </s> add             retval[side] = [feed[side][0:num_images],
                            samples[side][0:num_images],
                            masks[side][0:num_images]]
        logger.debug("Compiled Samples: %s", {k: [i.shape for i in v] for k, v in retval.items()})
                batch[ret_idx], meta[ret_idx] = future.result()
                            for filename in set(filenames)}
          for future in futures.as_completed(images):
              return_idx = return_indices[images[future]].pop()
              if with_metadata:
                batch[ret_idx], meta[ret_idx] = future.result()
              else:
                  batch[return_idx] = future.result()
  
      batch = np.array(batch)
      retval = (batch, meta) if with_metadata else batch
keep keep keep keep 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/image.py