          """ int: The number of images or video frames to be processed """
          return self._count
  
      @property
      def location(self):
          """ str: The folder or video that was passed in as the :attr:`path` parameter. """
          return self._location
  
      def _check_input(self):
          """ Check whether the input path is valid and return if it is a video.
  
          Returns
 
</s> remove     def _check_input(self):
        """ Check whether the input path is valid and return if it is a video.
 </s> add     def add_skip_list(self, skip_list):
        """ Add a skip list to this :class:`ImagesLoader`

        Parameters
        ----------
        skip_list: list
            A list of indices corresponding to the frame indices that should be skipped
        """
        logger.debug(skip_list)
        self._skip_list = set(skip_list)

    def _check_for_video(self):
        """ Check whether the input is a video </s> remove         """ int: The number of images or video frames to be processed """
 </s> add         """ int: The number of images or video frames in the source location. This count includes
        any files that will ultimately be skipped if a :attr:`skip_list` has been provided. See
        also: :attr:`process_count`""" </s> remove         self._task = task.lower()
        self._is_video = self._check_input()
        self._input = self.location if self._is_video else get_image_paths(self.location)
        self._count = count_frames(self._input) if self._is_video else len(self._input)
        self._queue = queue_manager.get_queue(name="{}_{}".format(self.__class__.__name__,
                                                                  self._task),
                                              maxsize=queue_size)
        self._thread = self._set_thread(io_args=(load_with_hash, ))
        self._thread.start()
 </s> add         self._count = None
        self._file_list = None
        self._get_count_and_filelist(fast_count) </s> remove         images or a video file. For saving this must be an existing folder.
    task: {'load', 'save'}
        The task to be performed. ``'load'`` to load images/video frames, ``'save'`` to save
        images.
    load_with_hash: bool, optional
        When loading images, set to ``True`` to return the sha1 hash of the image along with the
        image. Default: ``False``.
 </s> add         images, video file or a list of image files. For saving this must be an existing folder.
    queue_size: int
        The amount of images to hold in the internal buffer.
    args: tuple, optional
        The arguments to be passed to the loader or saver thread. Default: ``None``

    See Also
    --------
    lib.image.ImagesLoader : Background Image Loader inheriting from this class.
    lib.image.ImagesSaver : Background Image Saver inheriting from this class.
    """

    def __init__(self, path, queue_size, args=None):
        logger.debug("Initializing %s: (path: %s, queue_size: %s, args: %s)",
                     self.__class__.__name__, path, queue_size, args)

        self._args = tuple() if args is None else args

        self._location = path
        self._check_location_exists()

        self._queue = queue_manager.get_queue(name=self.__class__.__name__, maxsize=queue_size)
        self._thread = None

    @property
    def location(self):
        """ str: The folder or video that was passed in as the :attr:`path` parameter. """
        return self._location

    def _check_location_exists(self):
        """ Check whether the input location exists.

        Raises
        ------
        FaceswapError
            If the given location does not exist
        """
        if isinstance(self.location, str) and not os.path.exists(self.location):
            raise FaceswapError("The location '{}' does not exist".format(self.location))
        if isinstance(self.location, (list, tuple)) and not all(os.path.exists(location)
                                                                for location in self.location):
            raise FaceswapError("Not all locations in the input list exist")

    def _set_thread(self):
        """ Set the load/save thread """
        if self._thread is not None:
            return
        self._thread = MultiThread(self._process,
                                   self._queue,
                                   name=self.__class__.__name__,
                                   thread_count=1)
        logger.trace(self._thread)
        self._thread.start()

    def _process(self, queue):
        """ Image IO process to be run in a thread. Override for loader/saver process.

        Parameters
        ----------
        queue: queue.Queue()
            The ImageIO Queue
        """
        raise NotImplementedError

    def close(self):
        """ Closes down and joins the internal threads """
        logger.debug("Received Close")
        self._thread.join()
        logger.debug("Closed")


class ImagesLoader(ImageIO):
    """ Perform image loading from a folder of images or a video.

    Images will be loaded and returned in the order that they appear in the folder, or in the video
    to ensure deterministic ordering. Loading occurs in a background thread, caching 8 images at a
    time so that other processes do not need to wait on disk reads.

    See also :class:`ImageIO` for additional attributes.

    Parameters
    ----------
    path: str or list
        The path to load images from. This can be a folder which contains images a video file or a
        list of image files. </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)
    def process_count(self):
        """ int: The number of images or video frames to be processed (IE the total count less
        items that are to be skipped from the :attr:`skip_list`)"""
        return self._count - len(self._skip_list)

    @property
    def is_video(self):
        """ bool: ``True`` if the input is a video, ``False`` if it is not """
        return self._is_video

    @property
    def file_list(self):
        """ list: A full list of files in the source location. This includes any files that will
        ultimately be skipped if a :attr:`skip_list` has been provided. If the input is a video
        then this is a list of dummy filenames as corresponding to an alignments file """
        return self._file_list
          """ int: The number of images or video frames to be processed """
          return self._count
  
      @property
    def process_count(self):
        """ int: The number of images or video frames to be processed (IE the total count less
        items that are to be skipped from the :attr:`skip_list`)"""
        return self._count - len(self._skip_list)

    @property
    def is_video(self):
        """ bool: ``True`` if the input is a video, ``False`` if it is not """
        return self._is_video

    @property
    def file_list(self):
        """ list: A full list of files in the source location. This includes any files that will
        ultimately be skipped if a :attr:`skip_list` has been provided. If the input is a video
        then this is a list of dummy filenames as corresponding to an alignments file """
        return self._file_list
    def process_count(self):
        """ int: The number of images or video frames to be processed (IE the total count less
        items that are to be skipped from the :attr:`skip_list`)"""
        return self._count - len(self._skip_list)

    @property
    def is_video(self):
        """ bool: ``True`` if the input is a video, ``False`` if it is not """
        return self._is_video

    @property
    def file_list(self):
        """ list: A full list of files in the source location. This includes any files that will
        ultimately be skipped if a :attr:`skip_list` has been provided. If the input is a video
        then this is a list of dummy filenames as corresponding to an alignments file """
        return self._file_list
    def process_count(self):
        """ int: The number of images or video frames to be processed (IE the total count less
        items that are to be skipped from the :attr:`skip_list`)"""
        return self._count - len(self._skip_list)

    @property
    def is_video(self):
        """ bool: ``True`` if the input is a video, ``False`` if it is not """
        return self._is_video

    @property
    def file_list(self):
        """ list: A full list of files in the source location. This includes any files that will
        ultimately be skipped if a :attr:`skip_list` has been provided. If the input is a video
        then this is a list of dummy filenames as corresponding to an alignments file """
        return self._file_list
  
      def _check_input(self):
          """ Check whether the input path is valid and return if it is a video.
  
          Returns
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