U
    ?hk>                     @   s   d Z ddlZddlmZ ddlZddlmZ ddlmZ ddlZddl	m
Z
 ddlmZ dd	d
dgZdd
 Zdd Zdd ZG dd	 d	Zdd ZG dd deZdS )zEData structures to hold collections of images, with optional caching.    N)glob)Sequence)copy)Image)TiffFile
MultiImageImageCollectionconcatenate_imagesimread_collection_wrapperc                 C   s>   dd | D }zt |}W n tk
r8   tdY nX |S )au  Concatenate all images in the image collection into an array.

    Parameters
    ----------
    ic : an iterable of images
        The images to be concatenated.

    Returns
    -------
    array_cat : ndarray
        An array having one more dimension than the images in `ic`.

    See Also
    --------
    ImageCollection.concatenate, MultiImage.concatenate

    Raises
    ------
    ValueError
        If images in `ic` don't have identical shapes.

    Notes
    -----
    ``concatenate_images`` receives any iterable object containing images,
    including ImageCollection and MultiImage, and returns a NumPy array.
    c                 S   s   g | ]}|t jd f qS ).)npZnewaxis).0image r   G/var/www/html/venv/lib/python3.8/site-packages/skimage/io/collection.py
<listcomp>/   s     z&concatenate_images.<locals>.<listcomp>zImage dimensions must agree.)r   concatenate
ValueError)ZicZ
all_imagesZ	array_catr   r   r   r	      s    c                 C   s   dd t d| D }|S )aA  Convert string to list of strings and ints that gives intuitive sorting.

    Parameters
    ----------
    s : string

    Returns
    -------
    k : a list of strings and ints

    Examples
    --------
    >>> alphanumeric_key('z23a')
    ['z', 23, 'a']
    >>> filenames = ['f9.10.png', 'e10.png', 'f9.9.png', 'f10.10.png',
    ...              'f10.9.png']
    >>> sorted(filenames)
    ['e10.png', 'f10.10.png', 'f10.9.png', 'f9.10.png', 'f9.9.png']
    >>> sorted(filenames, key=alphanumeric_key)
    ['e10.png', 'f9.9.png', 'f9.10.png', 'f10.9.png', 'f10.10.png']
    c                 S   s    g | ]}|  rt|n|qS r   )isdigitint)r   cr   r   r   r   M   s     z$alphanumeric_key.<locals>.<listcomp>z([0-9]+))resplit)skr   r   r   alphanumeric_key7   s    r   c                 C   sP   t | totj| k}t | t }t | t}tdd | D }|pJ|oJ|oJ|}|S )zlHelping function. Returns True if pattern contains a tuple, list, or a
    string separated with os.pathsep.c                 s   s   | ]}t |tV  qd S N)
isinstancestr)r   patr   r   r   	<genexpr>Y   s     z#_is_multipattern.<locals>.<genexpr>)r   r   ospathsepr   all)Zinput_patternZhas_str_ospathsepZnot_a_stringZhas_iterableZhas_stringsZis_multipatternr   r   r   _is_multipatternQ   s    

r#   c                   @   st   e Zd ZdZdddZedd Zedd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd ZdddZdd ZdS )r   a  Load and manage a collection of image files.

    Parameters
    ----------
    load_pattern : str or list of str
        Pattern string or list of strings to load. The filename path can be
        absolute or relative.
    conserve_memory : bool, optional
        If True, `ImageCollection` does not keep more than one in memory at a
        specific time. Otherwise, images will be cached once they are loaded.

    Other parameters
    ----------------
    load_func : callable
        ``imread`` by default. See notes below.
    **load_func_kwargs : dict
        Any other keyword arguments are passed to `load_func`.

    Attributes
    ----------
    files : list of str
        If a pattern string is given for `load_pattern`, this attribute
        stores the expanded file list. Otherwise, this is equal to
        `load_pattern`.

    Notes
    -----
    Note that files are always returned in alphanumerical order. Also note
    that slicing returns a new ImageCollection, *not* a view into the data.

    ImageCollection image loading can be customized through
    `load_func`. For an ImageCollection ``ic``, ``ic[5]`` calls
    ``load_func(load_pattern[5])`` to load that image.

    For example, here is an ImageCollection that, for each video provided,
    loads every second frame::

      import imageio.v3 as iio3
      import itertools

      def vidread_step(f, step):
          vid = iio3.imiter(f)
          return list(itertools.islice(vid, None, None, step)

      video_file = 'no_time_for_that_tiny.gif'
      ic = ImageCollection(video_file, load_func=vidread_step, step=2)

      ic  # is an ImageCollection object of length 1 because 1 video is provided

      x = ic[0]
      x[5]  # the 10th frame of the first video

    Alternatively, if `load_func` is provided and `load_pattern` is a
    sequence, an `ImageCollection` of corresponding length will be created,
    and the individual images will be loaded by calling `load_func` with the
    matching element of the `load_pattern` as its first argument. In this
    case, the elements of the sequence do not need to be names of existing
    files (or strings at all). For example, to create an `ImageCollection`
    containing 500 images from a video::

      class FrameReader:
          def __init__ (self, f):
              self.f = f
          def __call__ (self, index):
              return iio3.imread(self.f, index=index)

      ic = ImageCollection(range(500), load_func=FrameReader('movie.mp4'))

      ic  # is an ImageCollection object of length 500

    Another use of `load_func` would be to convert all images to ``uint8``::

      def imread_convert(f):
          return imread(f).astype(np.uint8)

      ic = ImageCollection('/tmp/*.png', load_func=imread_convert)

    Examples
    --------
    >>> import imageio.v3 as iio3
    >>> import skimage.io as io

    # Where your images are located
    >>> data_dir = os.path.join(os.path.dirname(__file__), '../data')

    >>> coll = io.ImageCollection(data_dir + '/chess*.png')
    >>> len(coll)
    2
    >>> coll[0].shape
    (200, 200)

    >>> image_col = io.ImageCollection([f'{data_dir}/*.png', '{data_dir}/*.jpg'])

    >>> class MultiReader:
    ...     def __init__ (self, f):
    ...         self.f = f
    ...     def __call__ (self, index):
    ...         return iio3.imread(self.f, index=index)
    ...
    >>> filename = data_dir + '/no_time_for_that_tiny.gif'
    >>> ic = io.ImageCollection(range(24), load_func=MultiReader(filename))
    >>> len(image_col)
    23
    >>> isinstance(ic[0], np.ndarray)
    True
    TNc                 K   s  g | _ t|rPt|tr$|tj}|D ]}| j t| q(t	| j t
d| _ nRt|tr|| j t| t	| j t
d| _ n&t|tr|dk	rt|| _ ntd|dkrddlm} || _|  | _n|| _t| j | _d| _|rd}n| j}|| _d| _|| _tj|td| _dS )z'Load and manage a collection of images.)keyNzInvalid pattern as input.   imreaddtype)_filesr#   r   r   r   r    r!   extendr   sortedr   r   list	TypeError_ior'   	load_func_find_images
_numframeslen_frame_index_conserve_memory_cachedload_func_kwargsr   emptyobjectdata)selfload_patternconserve_memoryr0   r7   patternr'   Zmemory_slotsr   r   r   __init__   s6    

zImageCollection.__init__c                 C   s   | j S r   r*   r;   r   r   r   files   s    zImageCollection.filesc                 C   s   | j S r   )r5   rA   r   r   r   r=      s    zImageCollection.conserve_memoryc              
      s   g }| j D ]   dr\t d.}t|}| fddtt|jD 7 }W 5 Q R X q
zt }|	d W n t
k
r   Y q
Y nX d}z|	| W n tk
r   Y qY nX | |f |d7 }qt|dr
|jr
|j  q
|| _t|S )N)z.tiffz.tifrbc                    s   g | ]} |fqS r   r   r   ifnamer   r   r     s     z0ImageCollection._find_images.<locals>.<listcomp>r   r%   fp)r*   lowerendswithopenr   ranger3   Zpagesr   seekOSErrorEOFErrorappendhasattrrH   closer4   )r;   indexfZimgZimrE   r   rF   r   r1      s,    
,



zImageCollection._find_imagesc           	   
      s  t |dr| }t|ttfkr*tdt|tkr0 |}|t j } j	r`| j
ksp j| dkr& j} jr j| \}}|dk	r||d< z j|f| j|< W nN tk
r } z.dt|kr|d=  j|f| j|< n W 5 d}~X Y nX n j j| f| j|< | _
 j| S t j| }t } jrx fdd|D |_ fdd|D |_n fd	d|D |_t||_ j	rڈ j
|kr| j
|_
t j|_ntjd
td|_n j| |_|S dS )a  Return selected image(s) in the collection.

        Loading is done on demand.

        Parameters
        ----------
        n : int or slice
            The image number to be returned, or a slice selecting the images
            and ordering to be returned in a new ImageCollection.

        Returns
        -------
        img : ndarray or ImageCollection.
            The `n`-th image in the collection, or a new ImageCollection with
            the selected images.
        	__index__z+slicing must be with an int or slice objectNimg_numz%unexpected keyword argument 'img_num'c                    s   g | ]} j | d  qS )r   r4   rD   rA   r   r   r   N  s     z/ImageCollection.__getitem__.<locals>.<listcomp>c                    s   g | ]} j | qS r   rW   rD   rA   r   r   r   O  s     c                    s   g | ]} j | qS r   r@   rD   rA   r   r   r   Q  s     r%   r(   )rQ   rU   typer   slicer.   _check_imgnumr3   r:   r=   r6   r7   r4   r0   r   rB   rL   r2   r   r*   rS   r   r8   r9   )	r;   nidxkwargsrG   rV   eZfidxZnew_icr   rA   r   __getitem__  sN    



zImageCollection.__getitem__c                 C   s>   | j }| |  kr|k r*n n
|| }ntd| d|S )z+Check that the given image number is valid.zThere are only z images in the collection)r2   
IndexError)r;   r[   numr   r   r   rZ   _  s
    
zImageCollection._check_imgnumc                 c   s    t t| D ]}| | V  qdS )zIterate over the images.N)rL   r3   )r;   rE   r   r   r   __iter__h  s    zImageCollection.__iter__c                 C   s   | j S )zNumber of images in collection.)r2   rA   r   r   r   __len__m  s    zImageCollection.__len__c                 C   s
   t | jS r   )r   rB   rA   r   r   r   __str__q  s    zImageCollection.__str__c                 C   s   t | j| _dS )zClear the image cache.

        Parameters
        ----------
        n : None or int
            Clear the cache for this image only. By default, the
            entire cache is erased.

        N)r   Z
empty_liker:   )r;   r[   r   r   r   reloadt  s    
zImageCollection.reloadc                 C   s   t | S )a  Concatenate all images in the collection into an array.

        Returns
        -------
        ar : np.ndarray
            An array having one more dimension than the images in `self`.

        See Also
        --------
        concatenate_images

        Raises
        ------
        ValueError
            If images in the `ImageCollection` don't have identical shapes.
        )r	   rA   r   r   r   r     s    zImageCollection.concatenate)TN)N)__name__
__module____qualname____doc__r?   propertyrB   r=   r1   r_   rZ   rb   rc   rd   re   r   r   r   r   r   r   b   s   j
&

J	
c                    s   d fdd	}|S )NTc                    s   t | | dS )a  Return an `ImageCollection` from files matching the given pattern.

        Note that files are always stored in alphabetical order. Also note that
        slicing returns a new ImageCollection, *not* a view into the data.

        See `skimage.io.ImageCollection` for details.

        Parameters
        ----------
        load_pattern : str or list
            Pattern glob or filenames to load. The path can be absolute or
            relative.  Multiple patterns should be separated by a colon,
            e.g. ``/tmp/work/*.png:/tmp/other/*.jpg``.  Also see
            implementation notes below.
        conserve_memory : bool, optional
            If True, never keep more than one in memory at a specific
            time.  Otherwise, images will be cached once they are loaded.

        )r=   r0   )r   )r<   r=   r&   r   r   imread_collection  s    z4imread_collection_wrapper.<locals>.imread_collection)Tr   )r'   rk   r   r&   r   r
     s    c                       s.   e Zd ZdZd fdd	Zedd Z  ZS )	r   a   A class containing all frames from multi-frame TIFF images.

    Parameters
    ----------
    load_pattern : str or list of str
        Pattern glob or filenames to load. The path can be absolute or
        relative.
    conserve_memory : bool, optional
        Whether to conserve memory by only caching the frames of a single
        image. Default is True.

    Notes
    -----
    `MultiImage` returns a list of image-data arrays. In this
    regard, it is very similar to `ImageCollection`, but the two differ in
    their treatment of multi-frame images.

    For a TIFF image containing N frames of size WxH, `MultiImage` stores
    all frames of that image as a single element of shape `(N, W, H)` in the
    list. `ImageCollection` instead creates N elements of shape `(W, H)`.

    For an animated GIF image, `MultiImage` reads only the first frame, while
    `ImageCollection` reads all frames by default.

    Examples
    --------
    # Where your images are located
    >>> data_dir = os.path.join(os.path.dirname(__file__), '../data')

    >>> multipage_tiff = data_dir + '/multipage.tif'
    >>> multi_img = MultiImage(multipage_tiff)
    >>> len(multi_img)  # multi_img contains one element
    1
    >>> multi_img[0].shape  # this element is a two-frame image of shape:
    (2, 15, 10)

    >>> image_col = ImageCollection(multipage_tiff)
    >>> len(image_col)  # image_col contains two elements
    2
    >>> for frame in image_col:
    ...     print(frame.shape)  # each element is a frame of shape (15, 10)
    ...
    (15, 10)
    (15, 10)
    TNc                    s0   ddl m} || _t j||fd|i| dS )zLoad a multi-img.r%   r&   r0   N)r/   r'   	_filenamesuperr?   )r;   filenamer=   r)   Zimread_kwargsr'   	__class__r   r   r?     s    zMultiImage.__init__c                 C   s   | j S r   )rl   rA   r   r   r   rn     s    zMultiImage.filename)TN)rf   rg   rh   ri   r?   rj   rn   __classcell__r   r   ro   r   r     s   .	)ri   r    r   r   collections.abcr   r   numpyr   ZPILr   Ztifffiler   __all__r	   r   r#   r   r
   r   r   r   r   r   <module>   s&   #  4