          if self._thread is not None:
              self._thread.join()
          logger.debug("Closed")
  
  
  class ImagesLoader(ImageIO):
      """ Perform image loading from a folder of images or a video.
  
 
</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  </s> remove                 logger.debug("Image not loaded: '%s'", filename)
 </s> add                 logger.warning("Frame not loaded: '%s'", filename) </s> remove         when reading images from the given location. Default: ``None``
 </s> add         when executing the :func:`load` function from the given location. Default: ``None``
    count: int, optional
        If the number of images that the loader will encounter is already known, it can be passed
        in here to skip the image counting step, which can save time at launch. Set to ``None`` if
        the count is not already known. Default: ``None`` </s> remove     def _get_count_and_filelist(self, fast_count):
 </s> add     def _get_fps(self):
        """ Get the Frames per Second.

        If the input is a folder of images than 25.0 will be returned, as it is not possible to
        calculate the fps just from frames alone. For video files the correct FPS will be returned.

        Returns
        -------
        float: The Frames per Second of the input sources
        """
        if self._is_video:
            reader = imageio.get_reader(self.location, "ffmpeg")
            retval = reader.get_meta_data()["fps"]
            reader.close()
        else:
            retval = 25.0
        logger.debug(retval)
        return retval

    def _get_count_and_filelist(self, fast_count, count):
        self._thread = None
          if self._thread is not None:
              self._thread.join()
        self._thread = None
          logger.debug("Closed")
  
  
  class ImagesLoader(ImageIO):
      """ Perform image loading from a folder of images or a video.
  
keep add keep 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