      def process(self):
          """ Perform the extraction process """
          logger.info('Starting, this may take a while...')
          # queue_manager.debug_monitor(3)
          self.threaded_io("load")
          self.threaded_io("save")
          self.run_extraction()
          for thread in self.threads:
              thread.join()
          self.alignments.save()
          Utils.finalize(self.images.images_found // self.skip_num,
                         self.alignments.faces_count,
                         self.verify_output)
 
</s> remove         """ Perform the extraction process """
 </s> add         """ The entry point for triggering the Extraction Process.

        Should only be called from  :class:`lib.cli.ScriptExecutor`
        """ </s> remove         self.alignments.save()
        Utils.finalize(self.images.images_found // self.skip_num,
                       self.alignments.faces_count,
                       self.verify_output)
 </s> add         self._alignments.save()
        Utils.finalize(self._images.process_count + self._existing_count,
                       self._alignments.faces_count,
                       self._verify_output) </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     def skip_num(self):
        """ Number of frames to skip if extract_every_n is passed """
        return self.args.extract_every_n if hasattr(self.args, "extract_every_n") else 1
 </s> add     def _save_interval(self):
        """ int: The number of frames to be processed between each saving of the alignments file if
        it has been provided, otherwise ``None`` """
        if hasattr(self._args, "save_interval"):
            return self._args.save_interval
        return None

    @property
    def _skip_num(self):
        """ int: Number of frames to skip if extract_every_n has been provided """
        return self._args.extract_every_n if hasattr(self._args, "extract_every_n") else 1

    def _set_skip_list(self):
        """ Add the skip list to the image loader

        Checks against `extract_every_n` and the existence of alignments data (can exist if
        `skip_existing` or `skip_existing_faces` has been provided) and compiles a list of frame
        indices that should not be processed, providing these to :class:`lib.image.ImagesLoader`.
        """
        if self._skip_num == 1 and not self._alignments.data:
            logger.debug("No frames to be skipped")
            return
        skip_list = []
        for idx, filename in enumerate(self._images.file_list):
            if idx % self._skip_num != 0:
                logger.trace("Adding image '%s' to skip list due to extract_every_n = %s",
                             filename, self._skip_num)
                skip_list.append(idx)
            # Items may be in the alignments file if skip-existing[-faces] is selected
            elif os.path.basename(filename) in self._alignments.data:
                self._existing_count += 1
                logger.trace("Removing image: '%s' due to previously existing", filename)
                skip_list.append(idx)
        if self._existing_count != 0:
            logger.info("Skipping %s frames due to skip_existing/skip_existing_faces.",
                        self._existing_count)
        logger.debug("Adding skip list: %s", skip_list)
        self._images.add_skip_list(skip_list)
        self._threaded_redirector("load")
        self._run_extraction()
        for thread in self._threads:
      def process(self):
          """ Perform the extraction process """
          logger.info('Starting, this may take a while...')
          # queue_manager.debug_monitor(3)
        self._threaded_redirector("load")
        self._run_extraction()
        for thread in self._threads:
        self._threaded_redirector("load")
        self._run_extraction()
        for thread in self._threads:
        self._threaded_redirector("load")
        self._run_extraction()
        for thread in self._threads:
        self._threaded_redirector("load")
        self._run_extraction()
        for thread in self._threads:
              thread.join()
          self.alignments.save()
          Utils.finalize(self.images.images_found // self.skip_num,
                         self.alignments.faces_count,
                         self.verify_output)
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 
scripts/extract.py