Beispiel #1
0
def deltaE_ciede2000(lab1, lab2, kL=1, kC=1, kH=1):
    """Color difference as given by the CIEDE 2000 standard.

    CIEDE 2000 is a major revision of CIDE94.  The perceptual calibration is
    largely based on experience with automotive paint on smooth surfaces.

    Parameters
    ----------
    lab1 : array_like
        reference color (Lab colorspace)
    lab2 : array_like
        comparison color (Lab colorspace)
    kL : float (range), optional
        lightness scale factor, 1 for "acceptably close"; 2 for "imperceptible"
        see deltaE_cmc
    kC : float (range), optional
        chroma scale factor, usually 1
    kH : float (range), optional
        hue scale factor, usually 1

    Returns
    -------
    deltaE : array_like
        The distance between `lab1` and `lab2`

    Notes
    -----
    CIEDE 2000 assumes parametric weighting factors for the lightness, chroma,
    and hue (`kL`, `kC`, `kH` respectively).  These default to 1.

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Color_difference
    .. [2] http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf
           :DOI:`10.1364/AO.33.008069`
    .. [3] M. Melgosa, J. Quesada, and E. Hita, "Uniformity of some recent
           color metrics tested with an accurate color-difference tolerance
           dataset," Appl. Opt. 33, 8069-8077 (1994).
    """
    warnings.warn(
        "The numerical accuracy of this function on the GPU is reduced "
        "relative to the CPU version"
    )
    unroll = False
    if lab1.ndim == 1 and lab2.ndim == 1:
        unroll = True
        if lab1.ndim == 1:
            lab1 = lab1[None, :]
        if lab2.ndim == 1:
            lab2 = lab2[None, :]
    L1, a1, b1 = cp.rollaxis(lab1, -1)[:3]
    L2, a2, b2 = cp.rollaxis(lab2, -1)[:3]

    # distort `a` based on average chroma
    # then convert to lch coordines from distorted `a`
    # all subsequence calculations are in the new coordiantes
    # (often denoted "prime" in the literature)
    Cbar = 0.5 * (cp.hypot(a1, b1) + cp.hypot(a2, b2))
    c7 = Cbar ** 7
    G = 0.5 * (1 - cp.sqrt(c7 / (c7 + 25 ** 7)))
    scale = 1 + G
    C1, h1 = _cart2polar_2pi(a1 * scale, b1)
    C2, h2 = _cart2polar_2pi(a2 * scale, b2)
    # recall that c, h are polar coordiantes.  c==r, h==theta

    # cide2000 has four terms to delta_e:
    # 1) Luminance term
    # 2) Hue term
    # 3) Chroma term
    # 4) hue Rotation term

    # lightness term
    Lbar = 0.5 * (L1 + L2)
    tmp = Lbar - 50
    tmp *= tmp
    SL = 1 + 0.015 * tmp / cp.sqrt(20 + tmp)
    L_term = (L2 - L1) / (kL * SL)

    # chroma term
    Cbar = 0.5 * (C1 + C2)  # new coordiantes
    SC = 1 + 0.045 * Cbar
    C_term = (C2 - C1) / (kC * SC)

    # hue term
    h_diff = h2 - h1
    h_sum = h1 + h2
    CC = C1 * C2

    dH = h_diff.copy()
    dH[h_diff > np.pi] -= 2 * np.pi
    dH[h_diff < -np.pi] += 2 * np.pi
    dH[CC == 0.] = 0.  # if r == 0, dtheta == 0
    dH_term = 2 * cp.sqrt(CC) * cp.sin(dH / 2)

    Hbar = h_sum.copy()
    mask = cp.logical_and(CC != 0., cp.abs(h_diff) > np.pi)
    Hbar[mask * (h_sum < 2 * np.pi)] += 2 * np.pi
    Hbar[mask * (h_sum >= 2 * np.pi)] -= 2 * np.pi
    Hbar[CC == 0.] *= 2
    Hbar *= 0.5

    T = (1 -
         0.17 * cp.cos(Hbar - np.deg2rad(30)) +
         0.24 * cp.cos(2 * Hbar) +
         0.32 * cp.cos(3 * Hbar + np.deg2rad(6)) -
         0.20 * cp.cos(4 * Hbar - np.deg2rad(63))
         )
    SH = 1 + 0.015 * Cbar * T

    H_term = dH_term / (kH * SH)

    # hue rotation
    c7 = Cbar ** 7
    Rc = 2 * cp.sqrt(c7 / (c7 + 25 ** 7))
    tmp = (cp.rad2deg(Hbar) - 275) / 25
    tmp *= tmp
    dtheta = np.deg2rad(30) * cp.exp(-tmp)
    R_term = -cp.sin(2 * dtheta) * Rc * C_term * H_term

    # put it all together
    dE2 = L_term * L_term
    dE2 += C_term * C_term
    dE2 += H_term * H_term
    dE2 += R_term
    cp.sqrt(cp.maximum(dE2, 0, out=dE2), out=dE2)
    if unroll:
        dE2 = dE2[0]
    return dE2
Beispiel #2
0
def deltaE_cmc(lab1, lab2, kL=1, kC=1):
    """Color difference from the  CMC l:c standard.

    This color difference was developed by the Colour Measurement Committee
    (CMC) of the Society of Dyers and Colourists (United Kingdom). It is
    intended for use in the textile industry.

    The scale factors `kL`, `kC` set the weight given to differences in
    lightness and chroma relative to differences in hue.  The usual values are
    ``kL=2``, ``kC=1`` for "acceptability" and ``kL=1``, ``kC=1`` for
    "imperceptibility".  Colors with ``dE > 1`` are "different" for the given
    scale factors.

    Parameters
    ----------
    lab1 : array_like
        reference color (Lab colorspace)
    lab2 : array_like
        comparison color (Lab colorspace)

    Returns
    -------
    dE : array_like
        distance between colors `lab1` and `lab2`

    Notes
    -----
    deltaE_cmc the defines the scales for the lightness, hue, and chroma
    in terms of the first color.  Consequently
    ``deltaE_cmc(lab1, lab2) != deltaE_cmc(lab2, lab1)``

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Color_difference
    .. [2] http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
    .. [3] F. J. J. Clarke, R. McDonald, and B. Rigg, "Modification to the
           JPC79 colour-difference formula," J. Soc. Dyers Colour. 100, 128-132
           (1984).
    """
    L1, C1, h1 = cp.rollaxis(lab2lch(lab1), -1)[:3]
    L2, C2, h2 = cp.rollaxis(lab2lch(lab2), -1)[:3]

    dC = C1 - C2
    dL = L1 - L2
    dH2 = get_dH2(lab1, lab2)

    T = cp.where(cp.logical_and(cp.rad2deg(h1) >= 164, cp.rad2deg(h1) <= 345),
                 0.56 + 0.2 * cp.abs(np.cos(h1 + cp.deg2rad(168))),
                 0.36 + 0.4 * cp.abs(np.cos(h1 + cp.deg2rad(35)))
                 )
    c1_4 = C1 ** 4
    F = cp.sqrt(c1_4 / (c1_4 + 1900))

    SL = cp.where(L1 < 16, 0.511, 0.040975 * L1 / (1. + 0.01765 * L1))
    SC = 0.638 + 0.0638 * C1 / (1. + 0.0131 * C1)
    SH = SC * (F * T + 1 - F)

    dE2 = (dL / (kL * SL)) ** 2
    dE2 += (dC / (kC * SC)) ** 2
    dE2 += dH2 / (SH ** 2)
    return cp.sqrt(cp.maximum(dE2, 0, out=dE2), out=dE2)
Beispiel #3
0
def slope_aspect_gpu(dem,
                     resolution_x,
                     resolution_y,
                     ve_factor=1,
                     output_units="radian"):
    """
    Procedure can return terrain slope and aspect in radian units (default) or in alternative units (if specified).
    Slope is defined as 0 for Hz plane and pi/2 for vertical plane.
    Aspect iz defined as geographic azimuth: clockwise increasing, 0 or 2pi for the North direction.
    Currently applied finite difference method.

    Parameters
    ----------
    dem : input dem 2D cupy array
    resolution_x : dem resolution in X direction
    resolution_y : DEM resolution in Y direction
    ve_factor : vertical exaggeration factor (must be greater than 0)
    output_units : percent, degree, radians

    Returns
    -------
    {"slope": slope_out, "aspect": aspect_out} : dictionaries with 2D numpy arrays
    """

    if ve_factor <= 0:
        raise Exception(
            "rvt.vis.slope_aspect: ve_factor must be a positive number!")

    if resolution_x < 0 or resolution_y < 0:
        raise Exception(
            "rvt.vis.slope_aspect: resolution must be a positive number!")

    dem = dem.astype(np.float32)
    if ve_factor != 1:
        dem = dem * ve_factor

    # add frame of 0 (additional row up bottom and column left right)
    dem = cp.pad(dem, pad_width=1, mode="constant", constant_values=0)

    # derivatives in X and Y direction
    dzdx = ((cp.roll(dem, 1, axis=1) - cp.roll(dem, -1, axis=1)) /
            2) / resolution_x
    dzdy = ((cp.roll(dem, -1, axis=0) - cp.roll(dem, 1, axis=0)) /
            2) / resolution_y
    tan_slope = cp.sqrt(dzdx**2 + dzdy**2)

    # Compute slope
    if output_units == "percent":
        slope_out = tan_slope * 100
    elif output_units == "degree":
        slope_out = cp.rad2deg(np.arctan(tan_slope))
    elif output_units == "radian":
        slope_out = cp.arctan(tan_slope)
    else:
        raise Exception(
            "rvt.vis.calculate_slope: Wrong function input 'output_units'!")

    # compute Aspect
    # aspect identifies the down slope direction of the maximum rate of change in value from each cell to its neighbors:
    #     0
    # 270    90
    #    180
    dzdy[
        dzdy ==
        0] = 10e-9  # important for numeric stability - where dzdy is zero, make tangens to really high value

    aspect_out = cp.arctan2(dzdx, dzdy)  # atan2 took care of the quadrants
    if output_units == "degree":
        aspect_out = cp.rad2deg(aspect_out)

    # remove the frame (padding)
    slope_out = slope_out[1:-1, 1:-1]
    aspect_out = aspect_out[1:-1, 1:-1]

    # edges to -1
    slope_out[:, 0] = -1
    slope_out[0, :] = -1
    slope_out[:, -1] = -1
    slope_out[-1, :] = -1
    aspect_out[:, 0] = -1
    aspect_out[0, :] = -1
    aspect_out[:, -1] = -1
    aspect_out[-1, :] = -1

    return {"slope": slope_out, "aspect": aspect_out}