          filename: str
              The filename of the loaded image.
          image: numpy.ndarray
              The loaded image.
          sha1_hash: str, optional
              The sha1 hash of the loaded image. Only yielded if :class:`ImageIO` was
              initialized with :attr:`load_with_hash` set to ``True`` and the :attr:`location`
              is a folder of images.
          """
          logger.debug("Initializing Load Generator")
          self._set_thread()
          while True:
              self._thread.check_and_raise_error()
 
</s> remove         sha1_hash: str, optional
            The sha1 hash of the loaded image. Only yielded if :class:`ImageIO` was
            initialized with :attr:`load_with_hash` set to ``True`` and the :attr:`location`
            is a folder of images.
 </s> add  </s> remove         with_hash = self._args[0]
        logger.debug("Loading images from folder: '%s'. with_hash: %s", self.location, with_hash)
 </s> add         logger.debug("Loading frames from folder: '%s'", self.location) </s> remove         If :class:`ImageIO` was initialized with :attr:`load_with_hash` set to ``True`` then
        the sha1 hash of the image is added as the final item in the output `tuple`.
 </s> add         If :class:`FacesLoader` is in use then the sha1 hash of the image is added as the final
        item in the output `tuple`. </s> remove         self._thread.join()
 </s> add         self.close()


class FacesLoader(ImagesLoader):
    """ Loads faces from a faces folder along with the face's hash.

    Examples
    --------
    Loading faces with their sha1 hash:

    >>> loader = FacesLoader('/path/to/faces/folder')
    >>> for filename, face, sha1_hash in loader.load():
    >>>     <do processing>
    """
    def __init__(self, path, skip_list=None, count=None):
        logger.debug("Initializing %s: (path: %s, count: %s)", self.__class__.__name__,
                     path, count)
        super().__init__(path, queue_size=8, skip_list=skip_list, count=count)

    def _from_folder(self):
        """ Generator for loading images from a folder
        Faces will only ever be loaded from a folder, so this is the only function requiring
        an override

        Yields
        ------
        filename: str
            The filename of the loaded image.
        image: numpy.ndarray
            The loaded image.
        sha1_hash: str
            The sha1 hash of the loaded image.
        """
        logger.debug("Loading images from folder: '%s'", self.location)
        for idx, filename in enumerate(self.file_list):
            if idx in self._skip_list:
                logger.trace("Skipping face %s due to skip list")
                continue
            image_read = read_image(filename, raise_error=False, with_hash=True)
            retval = filename, *image_read
            if retval[1] is None:
                logger.warning("Face not loaded: '%s'", filename)
                continue
            yield retval


class SingleFrameLoader(ImagesLoader):
    """ Allows direct access to a frame by filename or frame index.

    As we are interested in instant access to frames, there is no requirement to process in a
    background thread, as either way we need to wait for the frame to load.

    Parameters
    ----------
    video_meta_data: dict, optional
        Existing video meta information containing the pts_time and iskey flags for the given
        video. Used in conjunction with single_frame_reader for faster seeks. Providing this means
        that the video does not need to be scanned again. Set to ``None`` if the video is to be
        scanned. Default: ``None``
     """
    def __init__(self, path, video_meta_data=None):
        logger.debug("Initializing %s: (path: %s, video_meta_data: %s)",
                     self.__class__.__name__, path, video_meta_data)
        self._video_meta_data = dict() if video_meta_data is None else video_meta_data
        self._reader = None
        super().__init__(path, queue_size=1, fast_count=False)

    @property
    def video_meta_data(self):
        """ dict: For videos contains the keys `frame_pts` holding a list of time stamps for each
        frame and `keyframes` holding the frame index of each key frame.

        Notes
        -----
        Only populated if the input is a video and single frame reader is being used, otherwise
        returns ``None``.
        """
        return self._video_meta_data

    def _get_count_and_filelist(self, fast_count, count):
        if self._is_video:
            self._reader = imageio.get_reader(self.location, "ffmpeg")
            count, video_meta_data = self._reader.get_frame_info(
                frame_pts=self._video_meta_data.get("pts_time", None),
                keyframes=self._video_meta_data.get("keyframes", None))
            self._video_meta_data = video_meta_data
        super()._get_count_and_filelist(fast_count, count)

    def image_from_index(self, index):
        """ Return a single image from :attr:`file_list` for the given index.

        Parameters
        ----------
        index: int
            The index number (frame number) of the frame to retrieve. NB: The first frame is
            index `0`

        Returns
        -------
        filename: str
            The filename of the returned image
        image: :class:`numpy.ndarray`
            The image for the given index

        Notes
        -----
        Retrieving frames from video files can be slow as the whole video file needs to be
        iterated to retrieve the requested frame. If a frame has already been retrieved, then
        retrieving frames of a higher index will be quicker than retrieving frames of a lower
        index, as iteration needs to start from the beginning again when navigating backwards.

        We do not use a background thread for this task, as it is assumed that requesting an image
        by index will be done when required.
        """
        if self.is_video:
            image = self._reader.get_data(index)[..., ::-1]
            filename = self._dummy_video_framename(index)
        else:
            filename = self.file_list[index]
            image = read_image(filename, raise_error=True)
            filename = os.path.basename(filename)
        logger.trace("index: %s, filename: %s image shape: %s", index, filename, image.shape)
        return filename, image </s> remove     load_with_hash: bool, optional
        Set to ``True`` to return the sha1 hash of the image along with the image.
        Default: ``False``.
 </s> add 
        sha1_hash: str, (:class:`FacesLoader` only)
            The sha1 hash of the loaded image. Only yielded if :class:`FacesLoader` is being
            executed.
          filename: str
              The filename of the loaded image.
          image: numpy.ndarray
              The loaded image.
        sha1_hash: str, (:class:`FacesLoader` only)
            The sha1 hash of the loaded image. Only yielded if :class:`FacesLoader` is being
            executed.
        sha1_hash: str, (:class:`FacesLoader` only)
            The sha1 hash of the loaded image. Only yielded if :class:`FacesLoader` is being
            executed.
        sha1_hash: str, (:class:`FacesLoader` only)
            The sha1 hash of the loaded image. Only yielded if :class:`FacesLoader` is being
            executed.
        sha1_hash: str, (:class:`FacesLoader` only)
            The sha1 hash of the loaded image. Only yielded if :class:`FacesLoader` is being
            executed.
          """
          logger.debug("Initializing Load Generator")
          self._set_thread()
          while True:
              self._thread.check_and_raise_error()
keep keep keep keep replace replace replace replace keep keep keep keep keep
 Core Update (#995)

* lib.alignments - Add Video Meta Data methods

* lib.image - Monkey Path ImageIO for video scanning 
lib/image.py