import cupy as cp
def Normalize_g(values, vmin=None, vmax=None):
    #归一化操作
    values = cp.asarray(values)
    if vmin is None:
        vmin = cp.min(values[cp.isfinite(values)])
    if vmax is None:
        vmax = cp.max(values[cp.isfinite(values)])
    cp.subtract(values, vmin, out=values)
    cp.true_divide(values, vmax - vmin, out=values)
    values = cp.clip(values, 0.0, 1.0, out=values)
    return values

def Denormalize_g(normalized_values, vmin, vmax):
    # 反归一化操作
    denormalized_values = normalized_values * (vmax - vmin) + vmin
    denormalized_values = cp.clip(denormalized_values, vmin, vmax)
    return denormalized_values


def Linear_g(img, vmin=None, vmax=None, slope=1, intercept=0):
    r"""
    .. math::
        y = slope x + intercept"""
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    if slope != 1:
        cp.multiply(result_data, slope, out=result_data)
    if intercept != 0:
        cp.add(result_data, intercept, out=result_data)
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return result_data

def Log_g(img, vmin=None, vmax=None, a=1000):
    r"""
    .. math::
        y = \frac{\log{(a x + 1)}}{\log{(a + 1)}}"""
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    cp.multiply(result_data, a, out=result_data)
    cp.add(result_data, 1.0, out=result_data)
    cp.log(result_data, out=result_data)
    cp.true_divide(result_data, cp.log(a + 1.0), out=result_data)
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return  result_data

def Power_g(img, vmin=None, vmax=None, a=4):
    r"""
    .. math::
        y = x^a"""
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    result_data = cp.power(result_data, a)
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return  result_data


def Asinh_g(img, vmin=None, vmax=None, a=0.1):
    r"""
    .. math::
    y = \frac{{\rm asinh}(x / a)}{{\rm asinh}(1 / a)}."""
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    cp.true_divide(result_data, a, out=result_data)
    cp.arcsinh(result_data, out=result_data)
    cp.true_divide(result_data, cp.arcsinh(1.0 / a), out=result_data)  
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return  result_data


def Sinh_g(img, vmin=None, vmax=None, a=1.0/3.0):
    r"""
    .. math::
        y = \frac{{\rm sinh}(x / a)}{{\rm sinh}(1 / a)}"""
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    cp.true_divide(result_data , a, out=result_data)
    cp.sinh(result_data , out=result_data )
    cp.true_divide(result_data , cp.sinh(1.0 / a), out=result_data )
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return  result_data


def HistEq_g(img, vmin=None, vmax=None, values=None):
    # 直方图均衡化
    # img[img<= 5e-5] = 0 
    img = cp.asarray(img)
    if vmax is None:
        vmax = cp.max(img)
    if vmin is None:
        vmin = cp.min(img)
    gray_data = cp.clip(img, vmin, vmax)
    result_data = Normalize_g(gray_data, vmin=vmin, vmax=vmax)
    hist_data = cp.sort(result_data.ravel())
    hist_data = hist_data[cp.isfinite(hist_data)]
    # Compute relative position of each pixel
    if values is None:
        values = cp.linspace(0.0, 1.0, len(hist_data))
    result_data[:] = cp.interp(result_data, hist_data, values)
    # result_data = Denormalize_g(result_data, vmin, vmax)
    return  result_data

def Bytescale_g(data, cmin=None, cmax=None, high=255, low=0):
    """
    Byte scales an array (image).

    Byte scaling means converting the input image to uint8 dtype and sca
    ling
    the range to ``(low, high)`` (default 0-255).
    If the input image already has dtype uint8, no scaling is done.

    Parameters
         
    data : ndarray
        PIL image data array.
    cmin : scalar, optional
        Bias scaling of small values. Default is ``data.min()``.
    cmax : scalar, optional
        Bias scaling of large values. Default is ``data.max()``.
    high : scalar, optional
        Scale max value to `high`.  Default is 255.
    low : scalar, optional
        Scale min value to `low`.  Default is 0.

    Returns
       -
    img_array : uint8 ndarray
        The byte-scaled array.

    Examples
        
    >>> img = array([[ 91.06794177,   3.39058326,  84.4221549 ],
                     [ 73.88003259,  80.91433048,   4.88878881],
                     [ 51.53875334,  34.45808177,  27.5873488 ]])
    >>> bytescale(img)
    array([[255,   0, 236],
           [205, 225,   4],
           [140,  90,  70]], dtype=uint8)
    >>> bytescale(img, high=200, low=100)
    array([[200, 100, 192],
           [180, 188, 102],
           [155, 135, 128]], dtype=uint8)
    >>> bytescale(img, cmin=0, cmax=255)
    array([[91,  3, 84],
           [74, 81,  5],
           [52, 34, 28]], dtype=uint8)

    """
    data = cp.asarray(data)
    high = cp.asarray(high)
    low = cp.asarray(low)
    if data.dtype == cp.uint8:
        return data

    if high < low:
        raise ValueError("`high` should be larger than `low`.")

    if cmin is None:
        cmin = data.min()
    if cmax is None:
        cmax = data.max()

    cscale = cmax - cmin
    if cscale < 0:
        raise ValueError("`cmax` should be larger than `cmin`.")
    elif cscale == 0:
        cscale = 1

    scale = float(high - low) / cscale
    bytedata = (data * 1.0 - cmin) * scale + 0.4999
    bytedata[bytedata > high] = high
    bytedata[bytedata < 0] = 0
    return bytedata + low

