U
    !?hb                     @   s  d dl mZ d dlZd dlZd dlZd dlZd dlm	Z	 dd Z
dd Zdd	 Zd
d Zd=ddZdd Zd>ddZd?ddZd@ddZdd ZdAddZdd  ZdBd!d"Zd#d$ Zd%d& Zd'd( Zd)d* ZdCd,d-ZdDd/d0Zd1d2 Zd3d4 ZdEd5d6Zd7d8 Z d9d: Z!d;d< Z"dS )F    )ImageNwrapsc                    s   t   fdd}|S )a  Creates a new function which operates on each channel

    Parameters
    ----------
    single_channel_func: function
        Function that acts on a single color channel

    Returns
    -------
    channel_func: function
        The same function that operates on all color channels

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> from scipy.signal import convolve2d
    >>> single_channel_fun = lambda x: convolve2d(x, np.ones((3, 3)), 'valid')
    >>> multi_channel_fun = apply_to_channels(single_channel_fun)
    >>> I = np.random.rand(480, 320, 3)
    >>> multi_channel_fun(I).shape
    (478, 318, 3)
    c                    s   t jdkrf S j}|d |d dtj fddtjd D dd}|t|jd d t|dd   S d S )N   r      c                    s2   g | ]*}d d d d |f   f qS N)copy).0c)argsimagekwargssingle_channel_func E/var/www/html/venv/lib/python3.8/site-packages/pymatting/util/util.py
<listcomp>+   s   zAapply_to_channels.<locals>.multi_channel_func.<locals>.<listcomp>Zaxis)lenshapereshapenpstackrangelist)r   r   r   r   resultr   )r   r   r   r   multi_channel_func"   s    z-apply_to_channels.<locals>.multi_channel_funcr   )r   r   r   r   r   apply_to_channels	   s    r   c                 C   s   t d| |S )a0  Computes the dot product of two vectors.

    Parameters
    ----------
    a: numpy.ndarray
        First vector (if np.ndim(a) > 1 the function calculates the product for the two last axes)
    b: numpy.ndarray
        Second vector (if np.ndim(b) > 1 the function calculates the product for the two last axes)

    Returns
    -------
    product: scalar
        Dot product of `a` and `b`

    Example
    -------
    >>> import numpy as np
    >>> from pymatting import *
    >>> a = np.ones(2)
    >>> b = np.ones(2)
    >>> vec_vec_dot(a,b)
    2.0
    z...i,...i->...r   Zeinsumabr   r   r   vec_vec_dot7   s    r#   c                 C   s   t d| |S )aK  Calculates the matrix vector product for two arrays.

    Parameters
    ----------
    A: numpy.ndarray
        Matrix (if np.ndim(A) > 2 the function calculates the product for the two last axes)
    b: numpy.ndarray
        Vector (if np.ndim(b) > 1 the function calculates the product for the two last axes)

    Returns
    -------
    product: numpy.ndarray
        Matrix vector product of both arrays

    Example
    -------
    >>> import numpy as np
    >>> from pymatting import *
    >>> A = np.eye(2)
    >>> b = np.ones(2)
    >>> mat_vec_dot(A,b)
    array([1., 1.])
    z...ij,...j->...ir   )Ar"   r   r   r   mat_vec_dotR   s    r%   c                 C   s   t d| |S )aV  Computes the outer product of two vectors

    a: numpy.ndarray
        First vector (if np.ndim(b) > 1 the function calculates the product for the two last axes)
    b: numpy.ndarray
        Second vector (if np.ndim(b) > 1 the function calculates the product for the two last axes)

    Returns
    -------
    product: numpy.ndarray
        Outer product of `a` and `b` as numpy.ndarray

    Example
    -------
    >>> import numpy as np
    >>> from pymatting import *
    >>> a = np.arange(1,3)
    >>> b = np.arange(1,3)
    >>> vec_vec_outer(a,b)
    array([[1, 2],
           [2, 4]])
    z	...i,...jr   r    r   r   r   vec_vec_outerm   s    r&   皙??c                 C   sr   |dk s|dkrt d|dk s(|dkr0t d||kr@t d| |k }| |k}dt|  }d||< d||< |S )a  Fixes broken trimap :math:`T` by thresholding the values

    .. math::
        T^{\text{fixed}}_{ij}=
        \begin{cases}
            0,&\text{if } T_{ij}<\text{lower\_threshold}\\
            1,&\text{if }T_{ij}>\text{upper\_threshold}\\
            0.5, &\text{otherwise}.\\
        \end{cases}


    Parameters
    ----------
    trimap: numpy.ndarray
        Possibly broken trimap
    lower_threshold: float
        Threshold used to determine background pixels, defaults to 0.1
    upper_threshold: float
        Threshold used to determine foreground pixels, defaults to 0.9

    Returns
    -------
    fixed_trimap: numpy.ndarray
        Trimap having values in :math:`\{0, 0.5, 1\}`

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> trimap = np.array([0,0.1, 0.4, 0.9, 1])
    >>> fix_trimap(trimap, 0.2, 0.8)
    array([0. , 0. , 0.5, 1. , 1. ])
    r   r   zInvalid lower thresholdzInvalid upper thresholdz4Lower threshold must be smaller than upper thresholdg      ?)
ValueErrorr   Z	ones_like)trimapZlower_thresholdZupper_thresholdis_bgis_fgZfixedr   r   r   
fix_trimap   s    "r-   c                 C   s*   zt |  W dS  tk
r$   Y dS X dS )aD  Checks if an object is iterable

    Parameters
    ----------
    obj: object
        Object to check

    Returns
    -------
    is_iterable: bool
        Boolean variable indicating whether the object is iterable

    Example
    -------
    >>> from pymatting import *
    >>> l = []
    >>> isiterable(l)
    True
    TFN)iter	TypeError)objr   r   r   
isiterable   s
    r1   bicubicc                 C   s^   t jt jt jt jt jt jt jd}t|sFt| j	| t| j
| f}| |||  } | S )N)r2   ZbilinearboxZhammingZlanczosZnearestnone)r   ZBICUBICZBILINEARZBOXZHAMMINGZLANCZOSZNEARESTr1   intwidthheightresizelower)r   sizeresamplefiltersr   r   r   _resize_pil_image   s    
r=   r3   c                 C   sZ   t | }|dk	r4| }|dkr&dn|}||}|dk	rHt|||}t|d }|S )a   This function can be used to load an image from a file.

    Parameters
    ----------
    path: str
        Path of image to load.
    mode: str
        Can be "GRAY", "RGB" or something else (see PIL.convert())

    Returns
    -------
    image: numpy.ndarray
        Loaded image
    NZGRAYLg     o@)r   openupperconvertr=   r   array)pathmoder:   r;   r   r   r   r   
load_image   s    

rE   Tc                 C   s   |j tjtjtjfkst|j tjtjfkrFt|d ddtj}|rttj	
| \}}t|dkrttj|dd t||  dS )a  Given a path, save an image there.

    Parameters
    ----------
    path: str
        Where to save the image.
    image: numpy.ndarray, dtype in [np.uint8, np.float32, np.float64]
        Image to save.
        Images of float dtypes should be in range [0, 1].
        Images of uint8 dtype should be in range [0, 255]
    make_directory: bool
        Whether to create the directories needed for the image path.
       r   T)exist_okN)dtyper   uint8float32float64AssertionErrorclipastypeosrC   splitr   makedirsr   	fromarraysave)rC   r   Zmake_directory	directory_r   r   r   
save_image  s    rV   c                 C   s   t | jdkst| jtjtjtjfks,t| jtjtjfkrXt| d dd	tj} t | jdkrztj
| gd ddS | jd dkrtj| gd ddS | jd dkr| S | jd dkr| d	d	d	d	d	df S td
| jd	S )a  Convertes an image to rgb8 color space

    Parameters
    ----------
    image: numpy.ndarray
        Image to convert

    Returns
    -------
    image: numpy.ndarray
        Converted image with same height and width as input image but with three color channels
    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> I = np.eye(2)
    >>> to_rgb8(I)
    array([[[255, 255, 255],
            [  0,   0,   0]],
           [[  0,   0,   0],
            [255, 255, 255]]], dtype=uint8)
    )r      rF   r   r   rW   r   r      NzInvalid image shape:)r   r   rL   rH   r   rI   rJ   rK   rM   rN   r   concatenater)   )r   r   r   r   to_rgb8"  s    rZ   c              	   C   s  | D ]"}|dk	r|j tjtjfkstqt| }|dkr<dS |dkrr|dkrrttt|}|| d | }n2|dkr|| d | }n|dkr|| d | }dd | D }t	dd |D }t	dd |D }t	d	d |D dd
}	|	dkrt
| D ]\}
}|dk	rt|jdkr6|ddddtjf }|jd dkrZtj|g|	 dd}|jd dkr|	dkrt|tj|jdd |j d}|| |
< q|dkrtdd | D }tj|| || |	f|d}t|D ]}t|D ]}|||  }
|
t| kr q| |
 }|dk	r||jd |jd d}|||| || |jd  || || |jd  f< qq|jd dkr|dddddf }|S )aU  Plots a grid of images.

    Parameters
    ----------
    images : list of numpy.ndarray
        List of images to plot
    nx: int
        Number of rows
    ny: int
        Number of columns
    dtype: type
        Data type of output array

    Returns
    -------
    grid: numpy.ndarray
       Grid of images with datatype `dtype`
    Nr   r   c                 S   s   g | ]}|d k	r|j qS r   r   r
   r   r   r   r   r   t  s      zmake_grid.<locals>.<listcomp>c                 s   s   | ]}|d  V  qdS )r   Nr   r
   r   r   r   r   	<genexpr>v  s     zmake_grid.<locals>.<genexpr>c                 s   s   | ]}|d  V  qdS )r   Nr   r]   r   r   r   r^   w  s     c                 S   s    g | ]}t |d kr|d  qS )r   )r   r]   r   r   r   r   x  s      )defaultr   r   rW   rX   rH   c                 s   s   | ]}|d k	r|j V  qd S r   r`   r\   r   r   r   r^     s      r   )rH   r   rJ   rK   rL   r   r5   ceilsqrtmax	enumerater   newaxisrY   stack_imagesonesnextzerosr   r   )imagesnxnyrH   r   nZshapeshwdir   yxr   r   r   	make_gridN  sh    
 


  rt   c                 C   s8   t | }t|d ddtj}t|}|  dS )zPlot grid of images.

    Parameters
    ----------
    images : list of numpy.ndarray
        List of images to plot
    height : int, matrix
        Height in pixels the output grid, defaults to 512

    rF   r   N)rt   r   rM   rN   rI   r   rR   show)rj   gridr   r   r   show_images  s    
rw   c           
      C   s   |r|   } |  }|  }|dk r6tjd| dd |dkrPtjd| dd | jtjtjfkrvtjd| j dd | |k}| |k}|	 dkrt
d	| |	 dkrt
d
| ||B }| }	||||	fS )a  This function splits the trimap into foreground pixels, background pixels, and unknown pixels.

    Foreground pixels are pixels where the trimap has values larger than or equal to `fg_threshold` (default: 0.9).
    Background pixels are pixels where the trimap has values smaller than or equal to `bg_threshold` (default: 0.1).
    Pixels with other values are assumed to be unknown.

    Parameters
    ----------
    trimap: numpy.ndarray
        Trimap with shape :math:`h \times w`
    flatten: bool
        If true np.flatten is called on the trimap

    Returns
    -------
    is_fg: numpy.ndarray
        Boolean array indicating which pixel belongs to the foreground
    is_bg: numpy.ndarray
        Boolean array indicating which pixel belongs to the background
    is_known: numpy.ndarray
        Boolean array indicating which pixel is known
    is_unknown: numpy.ndarray
        Boolean array indicating which pixel is unknown
    bg_threshold: float
        Pixels with smaller trimap values will be considered background.
    fg_threshold: float
        Pixels with larger trimap values will be considered foreground.


    Example
    -------
    >>> import numpy as np
    >>> from pymatting import *
    >>> trimap = np.array([[1,0],[0.5,0.2]])
    >>> is_fg, is_bg, is_known, is_unknown = trimap_split(trimap)
    >>> is_fg
    array([ True, False, False, False])
    >>> is_bg
    array([False,  True, False, False])
    >>> is_known
    array([ True,  True, False, False])
    >>> is_unknown
    array([False, False,  True,  True])
            z:Trimap values should be in [0, 1], but trimap.min() is %s.rW   
stacklevel      ?z:Trimap values should be in [0, 1], but trimap.max() is %s.zfUnexpected trimap.dtype %s. Are you sure that you do not want to use np.float32 or np.float64 instead?r   z7Trimap did not contain background values (values <= %f)z7Trimap did not contain foreground values (values >= %f))flattenminrc   warningswarnrH   r   rJ   rK   sumr)   )
r*   r|   Zbg_thresholdZfg_threshold	min_value	max_valuer,   r+   Zis_knownZ
is_unknownr   r   r   trimap_split  sD    -r   c                 C   s   t | jdks| jd dkr4tjdt| j dd |  }|  }|dk r^tjd| dd |dkrxtjd| dd | jtj	tj
fkrtjd	| j dd d
S )a  Performs a sanity check for input images. Image values should be in the
    range [0, 1], the `dtype` should be `np.float32` or `np.float64` and the
    image shape should be `(?, ?, 3)`.

    Parameters
    ----------
    image: numpy.ndarray
        Image with shape :math:`h \times w \times 3`

    Example
    -------
    >>> import numpy as np
    >>> from pymatting import check_image
    >>> image = (np.random.randn(64, 64, 2) * 255).astype(np.int32)
    >>> sanity_check_image(image)
    __main__:1: UserWarning: Expected RGB image of shape (?, ?, 3), but image.shape is (64, 64, 2).
    __main__:1: UserWarning: Image values should be in [0, 1], but image.min() is -933.
    __main__:1: UserWarning: Image values should be in [0, 1], but image.max() is 999.
    __main__:1: UserWarning: Unexpected image.dtype int32. Are you sure that you do not want to use np.float32 or np.float64 instead?

    rW   r   z=Expected RGB image of shape (?, ?, 3), but image.shape is %s.ry   rx   z8Image values should be in [0, 1], but image.min() is %s.r{   z8Image values should be in [0, 1], but image.max() is %s.zeUnexpected image.dtype %s. Are you sure that you do not want to use np.float32 or np.float64 instead?N)r   r   r~   r   strr}   rc   rH   r   rJ   rK   )r   r   r   r   r   r   sanity_check_image  s4    r   c                 C   s:   t |jdkr&|ddddtjf }||  d| |  S )a  This function composes a new image for given foreground image, background image and alpha matte.

    This is done by applying the composition equation

    .. math::
        I = \alpha F + (1-\alpha)B.

    Parameters
    ----------
    foreground: numpy.ndarray
        Foreground image
    background: numpy.ndarray
        Background image
    alpha: numpy.ndarray
        Alpha matte

    Returns
    -------
    image: numpy.ndarray
        Composed image as numpy.ndarray

    Example
    -------
    >>> from pymatting import *
    >>> foreground = load_image("data/lemur/lemur_foreground.png", "RGB")
    >>> background = load_image("data/lemur/beach.png", "RGB")
    >>> alpha = load_image("data/lemur/lemur_alpha.png", "GRAY")
    >>> I = blend(foreground, background, alpha)
    r   Nr   r   r   r   re   )
foreground
backgroundalphar   r   r   blendE  s    r   c                  G   s   dd | D } t j| ddS )a  This function stacks images along the third axis.
    This is useful for combining e.g. rgb color channels or color and alpha channels.

    Parameters
    ----------
    *images: numpy.ndarray
        Images to be stacked.

    Returns
    -------
    image: numpy.ndarray
        Stacked images as numpy.ndarray

    Example
    -------
    >>> from pymatting.util.util import stack_images
    >>> import numpy as np
    >>> I = stack_images(np.random.rand(4,5,3), np.random.rand(4,5,3))
    >>> I.shape
    (4, 5, 6)
    c                 S   s6   g | ].}t |jd kr|n|ddddtjf qS )rW   Nr   r\   r   r   r   r     s   z stack_images.<locals>.<listcomp>r   r   )r   rY   )rj   r   r   r   rf   i  s    rf   c                 C   s   |  t| jd | j}|S )a  Calculate the sum of each row of a matrix

    Parameters
    ----------
    A: np.ndarray or scipy.sparse.spmatrix
        Matrix to sum rows of

    Returns
    -------
    row_sums: np.ndarray
        Vector of summed rows

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> A = np.random.rand(2,2)
    >>> A
    array([[0.62750946, 0.12917617],
           [0.8599449 , 0.5777254 ]])
    >>> row_sum(A)
    array([0.75668563, 1.4376703 ])
    r   )dotr   rg   r   rH   )r$   row_sumsr   r   r   row_sum  s    r   rx   c                 C   s6   t | }d|||k < d| }tj|}|| } | S )a  Normalize the rows of a matrix

    Rows with sum below threshold are left as-is.

    Parameters
    ----------
    A: scipy.sparse.spmatrix
        Matrix to normalize
    threshold: float
        Threshold to avoid division by zero

    Returns
    -------
    A: scipy.sparse.spmatrix
        Matrix with normalized rows

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> A = np.arange(4).reshape(2,2)
    >>> normalize_rows(A)
    array([[0. , 1. ],
           [0.4, 0.6]])
    r{   )r   scipysparsediagsr   )r$   	thresholdr   Zrow_normalization_factorsDr   r   r   normalize_rows  s    
r   Fc                 C   sV   |r*t t | |}t t || }n$t | }t |}t ||\}}||fS )ap  Calculates image pixel coordinates for an image with a specified shape

    Parameters
    ----------
    width: int
        Width of the input image
    height: int
        Height of the input image
    flatten: bool
        Whether the array containing the coordinates should be flattened or not, defaults to False

    Returns
    -------
    x: numpy.ndarray
        x coordinates
    y: numpy.ndarray
        y coordinates

    Example
    -------
    >>> from pymatting import *
    >>> x, y = grid_coordinates(2,2)
    >>> x
    array([[0, 1],
           [0, 1]])
    >>> y
    array([[0, 0],
           [1, 1]])
    )r   ZtileZarangerepeatZmeshgrid)r6   r7   r|   rs   rr   r   r   r   grid_coordinates  s    

r   c                 C   s  t | }t|}| | }t j|| t jd}t j|| t jd}	t j|| t jd}
d}t| |dd\}}t|||D ]~\}}}t 	|| d| d }t 	|| d|d }|||   |||| < |||   |	||| < ||
||| < ||7 }q|t
jj|
||	ff||fd}|S )a  Calculates a convolution matrix that can be applied to a vectorized image

    Additionally, this function allows to specify which pixels should be used for the convoltion, i.e.

    .. math:: \left(I * K\right)_{ij} = \sum_k K_k I_{i+{\Delta_y}_k,j+{\Delta_y}_k},

    where :math:`K` is the flattened convolution kernel.

    Parameters
    ----------
    width: int
        Width of the input image
    height: int
        Height of the input image
    kernel: numpy.ndarray
        Convolutional kernel
    dx: numpy.ndarray
        Offset in x direction
    dy: nunpy.ndarray
        Offset in y direction

    Returns
    -------
    M: scipy.sparse.csr_matrix
        Convolution matrix
    r`   r   Tr|   r   r[   )r   asarrayr|   r   ri   Zint32rK   r   ziprM   r   r   Z
csr_matrix)r6   r7   kernelZdxZdyweightscountrm   Zi_indsZj_indsvalueskrs   rr   Zdx2Zdy2weightZx2y2r$   r   r   r   sparse_conv_matrix_with_offsets  s"    
r   c                 C   sD   |j \}}t||dd\}}||d 8 }||d 8 }t| ||||S )aR  Calculates a convolution matrix that can be applied to a vectorized image

    Parameters
    ----------
    width: int
        Width of the input image
    height: int
        Height of the input image
    kernel: numpy.ndarray
        Convolutional kernel

    Returns
    -------
    M: scipy.sparse.csr_matrix
        Convolution matrix

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> sparse_conv_matrix(3,3,np.ones((3,3)))
    <9x9 sparse matrix of type '<class 'numpy.float64'>'
    with 49 stored elements in Compressed Sparse Row format>
    Tr   r   )r   r   r   )r6   r7   r   Zkhkwrs   rr   r   r   r   sparse_conv_matrix'  s
    
r   c                 C   s0   |rt | } |t|  }tj|}||  }|S )a  Calculates the random walk normalized Laplacian matrix from the weight matrix

    Parameters
    ----------
    W: numpy.ndarray
        Array of weights
    normalize: bool
        Whether the rows of W should be normalized to 1, defaults to True
    regularization: float
        Regularization strength, defaults to 0, i.e. no regularizaion

    Returns
    -------
    L: scipy.sparse.spmatrix
        Laplacian matrix

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> weights_to_laplacian(np.ones((4,4)))
    matrix([[ 0.75, -0.25, -0.25, -0.25],
            [-0.25,  0.75, -0.25, -0.25],
            [-0.25, -0.25,  0.75, -0.25],
            [-0.25, -0.25, -0.25,  0.75]])
    )r   r   r   r   r   )W	normalizeZregularizationrp   r   r>   r   r   r   weights_to_laplacianH  s    r   c                 C   s*   t | } |  }|  }| | ||  S )a  Normalizes an array such that all values are between 0 and 1

    Parameters
    ----------
    values: numpy.ndarray
        Array to normalize

    Returns
    -------
    result: numpy.ndarray
        Normalized array

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> normalize(np.array([0, 1, 3, 10]))
    array([0. , 0.1, 0.3, 1. ])
    )r   r   r}   rc   )r   r!   r"   r   r   r   r   n  s    
r   c                 C   s   | | d | S )a1  Divides a number x by another integer n and rounds up the result

    Parameters
    ----------
    x: int
        Numerator
    n: int
        Denominator

    Returns
    -------
    result: int
        Result

    Example
    -------
    >>> from pymatting import *
    >>> div_round_up(3,2)
    2
    r   r   )rs   rm   r   r   r   div_round_up  s    r   c           	      C   s   || }t j| | | ddt t | }t |dd}|dddddf }t j| d| |  |t | |dkd}t |dd}t ||g}|S )a  Remove background from image with at most two colors.
    Might not work if image has more than two colors.

    Parameters
    ----------
    image: numpy.ndarray
        RGB input image
    fg_color: numpy.ndarray
        RGB Foreground color
    bg_color: numpy.ndarray
        RGB Background color

    Returns
    -------
    output: numpy.ndarray
        RGBA output image

    Example
    -------
    >>> from pymatting import *
    >>> import numpy as np
    >>> image = np.random.rand(480, 320, 3)
    >>> fg_color = np.random.rand(3)
    >>> bg_color = np.random.rand(3)
    >>> output = remove_background_bicolor(image, fg_color, bg_color)
    >>> print(output.shape)
    (480, 320, 4)
    r   r   rx   r{   Nr   )outwhere)r   r   ZsquarerM   divideZ
zeros_likeZdstack)	r   Zfg_colorZbg_colorZfg_bgur   r!   Zactual_coloroutputr   r   r   remove_background_bicolor  s    &   r   )r'   r(   )r2   )NNr3   )T)NNN)Tr'   r(   )rx   )F)Trx   )#ZPILr   numpyr   Zscipy.sparser   rO   r~   	functoolsr   r   r#   r%   r&   r-   r1   r=   rE   rV   rZ   rt   rw   r   r   r   rf   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s<   .
3


,
W
Y5$
(
*2!
&