              item = queue.get()
              if item == "EOF":
                  logger.debug("EOF received")
                  break
              filename, image = item
              logger.trace("Saving image: '%s'", filename)
              cv2.imwrite(filename, image)
  
      def save(self, filename, image):
          """ Save the given image in the background thread
  
          Ensure that :func:`close` is called once all save operations are complete.
 
</s> remove         queue = args[0]
 </s> add         executor = futures.ThreadPoolExecutor(thread_name_prefix=self.__class__.__name__) </s> remove         args: tuple
            The save arguments
 </s> add         queue: queue.Queue()
            The ImageIO Queue </s> remove     def save_faces(self):
        """ Save the generated faces """
        logger.debug("Save Faces: Start")
        while True:
            if self.save_queue.shutdown.is_set():
                logger.debug("Save Queue: Stop signal received. Terminating")
                break
            item = self.save_queue.get()
            logger.trace(item)
            if item == "EOF":
                break
            filename, face = item

            logger.trace("Saving face: '%s'", filename)
            try:
                with open(filename, "wb") as out_file:
                    out_file.write(face)
            except Exception as err:  # pylint: disable=broad-except
                logger.error("Failed to save image '%s'. Original Error: %s", filename, err)
                continue
        logger.debug("Save Faces: Complete")

    def process_item_count(self):
        """ Return the number of items to be processedd """
        processed = sum(os.path.basename(frame) in self.alignments.data.keys()
                        for frame in self.images.input_images)
        logger.debug("Items already processed: %s", processed)

        if processed != 0 and self.args.skip_existing:
            logger.info("Skipping previously extracted frames: %s", processed)
        if processed != 0 and self.args.skip_faces:
            logger.info("Skipping frames with detected faces: %s", processed)

        to_process = (self.images.images_found - processed) // self.skip_num
        logger.debug("Items to be Processed: %s", to_process)
        if to_process == 0:
            logger.error("No frames to process. Exiting")
            queue_manager.terminate_queues()
            exit(0)
        return to_process

    def run_extraction(self):
        """ Run Face Detection """
        to_process = self.process_item_count()
        size = self.args.size if hasattr(self.args, "size") else 256
 </s> add     def _run_extraction(self):
        """ The main Faceswap Extraction process

        Receives items from :class:`plugins.extract.Pipeline.Extractor` and either saves out the
        faces and data (if on the final pass) or reprocesses data through the pipeline for serial
        processing.
        """
        size = self._args.size if hasattr(self._args, "size") else 256
        saver = ImagesSaver(self._output_dir, as_bytes=True) </s> remove             if idx % self.skip_num != 0:
                logger.trace("Skipping image '%s' due to extract_every_n = %s",
                             filename, self.skip_num)
                continue
            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
            imagename = os.path.basename(filename)
            if imagename in self.alignments.data.keys():
                logger.trace("Skipping image: '%s'", filename)
                continue
 </s> add  </s> remove         """ Closes down and joins the internal threads

        Must be called after a :func:`save` operation to ensure all items are saved before the
        parent process exits.
        """
        logger.debug("Received Close")
        if self._task == "save":
            logger.debug("Putting EOF to save queue")
            self._queue.put("EOF")
        self._thread.join()
        logger.debug("Closed")
 </s> add         """ Signal to the Save Threads that they should be closed and cleanly shutdown
        the saver """
        logger.debug("Putting EOF to save queue")
        self._queue.put("EOF")
        super().close()
            logger.trace("Submitting: '%s'", item[0])
            executor.submit(self._save, *item)
        executor.shutdown()

    def _save(self, filename, image):
        """ Save a single image inside a ThreadPoolExecutor

        Parameters
        ----------
        filename: str
            The filename of the image to be saved. Can include or exclude the folder location.
        image: numpy.ndarray
            The image to be saved
        """
        if not os.path.commonprefix([self.location, filename]):
            filename = os.path.join(self.location, filename)
        try:
            if self._as_bytes:
                with open(filename, "wb") as out_file:
                    out_file.write(image)
            else:
                cv2.imwrite(filename, image)
            logger.trace("Saved image: '%s'", filename)
        except Exception as err:  # pylint: disable=broad-except
            logger.error("Failed to save image '%s'. Original Error: %s", filename, err)
              item = queue.get()
              if item == "EOF":
                  logger.debug("EOF received")
                  break
            logger.trace("Submitting: '%s'", item[0])
            executor.submit(self._save, *item)
        executor.shutdown()

    def _save(self, filename, image):
        """ Save a single image inside a ThreadPoolExecutor

        Parameters
        ----------
        filename: str
            The filename of the image to be saved. Can include or exclude the folder location.
        image: numpy.ndarray
            The image to be saved
        """
        if not os.path.commonprefix([self.location, filename]):
            filename = os.path.join(self.location, filename)
        try:
            if self._as_bytes:
                with open(filename, "wb") as out_file:
                    out_file.write(image)
            else:
                cv2.imwrite(filename, image)
            logger.trace("Saved image: '%s'", filename)
        except Exception as err:  # pylint: disable=broad-except
            logger.error("Failed to save image '%s'. Original Error: %s", filename, err)
            logger.trace("Submitting: '%s'", item[0])
            executor.submit(self._save, *item)
        executor.shutdown()

    def _save(self, filename, image):
        """ Save a single image inside a ThreadPoolExecutor

        Parameters
        ----------
        filename: str
            The filename of the image to be saved. Can include or exclude the folder location.
        image: numpy.ndarray
            The image to be saved
        """
        if not os.path.commonprefix([self.location, filename]):
            filename = os.path.join(self.location, filename)
        try:
            if self._as_bytes:
                with open(filename, "wb") as out_file:
                    out_file.write(image)
            else:
                cv2.imwrite(filename, image)
            logger.trace("Saved image: '%s'", filename)
        except Exception as err:  # pylint: disable=broad-except
            logger.error("Failed to save image '%s'. Original Error: %s", filename, err)
            logger.trace("Submitting: '%s'", item[0])
            executor.submit(self._save, *item)
        executor.shutdown()

    def _save(self, filename, image):
        """ Save a single image inside a ThreadPoolExecutor

        Parameters
        ----------
        filename: str
            The filename of the image to be saved. Can include or exclude the folder location.
        image: numpy.ndarray
            The image to be saved
        """
        if not os.path.commonprefix([self.location, filename]):
            filename = os.path.join(self.location, filename)
        try:
            if self._as_bytes:
                with open(filename, "wb") as out_file:
                    out_file.write(image)
            else:
                cv2.imwrite(filename, image)
            logger.trace("Saved image: '%s'", filename)
        except Exception as err:  # pylint: disable=broad-except
            logger.error("Failed to save image '%s'. Original Error: %s", filename, err)
  
      def save(self, filename, image):
          """ Save the given image in the background thread
  
          Ensure that :func:`close` is called once all save operations are complete.
keep keep keep keep 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 
lib/image.py