          tasks = ["extract_{}_in".format(phase) for phase in self._flow]
          tasks.append("extract_{}_out".format(self._final_phase))
          for task in tasks:
              # Limit queue size to avoid stacking ram
              self._queue_size = 32
              if task == "extract_{}_in".format(self._flow[0]) or (not self._is_parallel
                                                                   and not task.endswith("_out")):
                  self._queue_size = 64
              queue_manager.add_queue(task, maxsize=self._queue_size)
              queues[task] = queue_manager.get_queue(task)
          logger.debug("Queues: %s", queues)
          return queues
  
 
</s> remove         self._queue_size = 32
 </s> add         # We only ever need 1 item in each queue. This is 2 items cached (1 in queue 1 waiting
        # for queue) at each point. Adding more just stacks RAM with no speed benefit.
        self._queue_size = 1 </s> remove         for retval in iterator(*io_args):
 </s> add         for retval in iterator():
            filename, image = retval[:2]
            if image is None or (not image.any() and image.ndim not in (2, 3)):
                # All black frames will return not np.any() so check dims too
                logger.warning("Unable to open image. Skipping: '%s'", filename)
                continue </s> remove     def threaded_io(self, task, io_args=None):
        """ Perform I/O task in a background thread """
 </s> add     def _threaded_redirector(self, task, io_args=None):
        """ Redirect image input/output tasks to relevant queues in background thread

        Parameters
        ----------
        task: str
            The name of the task to be put into a background thread
        io_args: tuple, optional
            Any arguments that need to be provided to the background function
        """ </s> remove         for phase in range(self.extractor.passes):
 </s> add         for phase in range(self._extractor.passes): </s> remove         if task == "load":
            func = self.load_images
        elif task == "save":
            func = self.save_faces
        elif task == "reload":
            func = self.reload_images
 </s> add         func = getattr(self, "_{}".format(task))
          tasks = ["extract_{}_in".format(phase) for phase in self._flow]
          tasks.append("extract_{}_out".format(self._final_phase))
          for task in tasks:
              # Limit queue size to avoid stacking ram




              queue_manager.add_queue(task, maxsize=self._queue_size)
              queues[task] = queue_manager.get_queue(task)
          logger.debug("Queues: %s", queues)
          return queues
  
keep keep keep keep replace replace replace replace keep keep keep keep keep
 scripts.extract - code optimizations

Limit queue sizes to reduce RAM usage
Rename lib.image.BackgroundIO to ImageIO
Create separate ImagesLoader and ImagesSaver classes
Load/Save images from centralized lib.image.ImageIO
scripts.extract documentation 
plugins/extract/pipeline.py