Ejemplo n.º 1
0
def spd_to_fci(spd, use_cielab=True):
    """
    Calculate Feeling of Contrast Index (FCI).
    
    Args:
        :spd:
            | ndarray with spectral power distribution(s) of the test light source(s).
        :use_cielab:
            |  True, optional
            | True: use original formulation of FCI, which adopts a CIECAT94 
            | chromatic adaptation transform followed by a conversion to 
            | CIELAB coordinates before calculating the gamuts.
            | False: use CIECAM02 coordinates and embedded CAT02 transform.
            
    Returns:
        :fci:
            | ndarray with FCI values.
            
    References:
        1. `Hashimoto, K., Yano, T., Shimizu, M., & Nayatani, Y. (2007). 
        New method for specifying color-rendering properties of light sources 
        based on feeling of contrast. 
        Color Research and Application, 32(5), 361–371. 
        <http://dx.doi.org/10.1002/col.20338>`_
    """

    # get xyz:
    xyz, xyzw = spd_to_xyz(spd,
                           cieobs='1931_2',
                           relative=True,
                           rfl=_RFL_FCI,
                           out=2)

    # set condition parameters:
    D = 1
    Yb = 20
    La = Yb * 1000 / np.pi / 100

    if use_cielab:
        # apply ciecat94 chromatic adaptation transform:
        xyzc = cat.apply_ciecat94(
            xyz, xyzw=xyzw, E=1000, Yb=20, D=D, cat94_old=True
        )  # there is apparently an updated version with an alpha incomplete adaptation factor and noise = 0.1; However, FCI doesn't use that version.

        # convert to cielab:
        lab = xyz_to_lab(xyzc, xyzw=_XYZW_D65_REF)
        labd65 = np.repeat(xyz_to_lab(_XYZ_D65_REF, xyzw=_XYZW_D65_REF),
                           lab.shape[1],
                           axis=1)
    else:
        f = lambda xyz, xyzw: cam.xyz_to_jabC_ciecam02(
            xyz, xyzw=xyzw, La=1000 * 20 / np.pi / 100, Yb=20, surround='avg')
        lab = f(xyz, xyzw)
        labd65 = np.repeat(f(_XYZ_D65_REF, _XYZW_D65_REF),
                           lab.shape[1],
                           axis=1)

    fci = 100 * (_polyarea3D(lab) / _polyarea3D(labd65))**1.5

    return fci
Ejemplo n.º 2
0
 def test_lab(self, getvars):
     S, spds, rfls, xyz, xyzw = getvars
     labw = lx.xyz_to_lab(xyzw, xyzw=xyzw[:1, :])
     lab = lx.xyz_to_lab(xyz, xyzw=xyzw[:1, :])
     xyzw_ = lx.lab_to_xyz(labw, xyzw=xyzw[:1, :])
     xyz_ = lx.lab_to_xyz(lab, xyzw=xyzw[:1, :])
     assert np.isclose(xyz, xyz_).all()
     assert np.isclose(xyzw, xyzw_).all()
Ejemplo n.º 3
0
 def to_lab(self, xyzw=None, cieobs=_CIEOBS):
     """ 
     Convert XYZ tristimulus values to CIE 1976 L*a*b* (CIELAB) coordinates.
      
     Args:
         :xyzw: 
             | None or ndarray with xyz values of white point, optional
             | None defaults to xyz of CIE D65 using the :cieobs: observer.
         :cieobs:
             | luxpy._CIEOBS, optional
             | CMF set to use when calculating xyzw.
         
     Returns:
         :lab: 
             | luxpy.LAB with .value field that is a ndarray 
             | with CIE 1976 L*a*b* (CIELAB) color coordinates
     """
     return LAB(value=xyz_to_lab(self.value, xyzw=xyzw, cieobs=cieobs),
                relative=self.relative,
                dtype='lab',
                xyzw=xyzw,
                cieobs=cieobs)
Ejemplo n.º 4
0
def DE2000(xyzt,
           xyzr,
           dtype='xyz',
           DEtype='jab',
           avg=None,
           avg_axis=0,
           out='DEi',
           xyzwt=None,
           xyzwr=None,
           KLCH=None):
    """
    Calculate DE2000 color difference.
    
    Args:
        :xyzt: 
            | ndarray with tristimulus values of test data.
        :xyzr:
            | ndarray with tristimulus values of reference data.
        :dtype:
            | 'xyz' or 'lab', optional
            | Specifies data type in :xyzt: and :xyzr:.
        :xyzwt:
            | None or ndarray, optional
            |   White point tristimulus values of test data
            |   None defaults to the one set in lx.xyz_to_lab()
        :xyzwr:
            | None or ndarray, optional
            |    Whitepoint tristimulus values of reference data
            |    None defaults to the one set in lx.xyz_to_lab()
        :DEtype:
            | 'jab' or str, optional
            | Options: 
            |    - 'jab' : calculates full color difference over all 3 dimensions.
            |    - 'ab'  : calculates chromaticity difference.
            |    - 'j'   : calculates lightness or brightness difference 
            |             (depending on :outin:).
            |    - 'j,ab': calculates both 'j' and 'ab' options 
            |              and returns them as a tuple.
        :KLCH: 
            | None, optional
            | Weigths for L, C, H 
            | None: default to [1,1,1] 
        :avg:
            | None, optional
            | None: don't calculate average DE, 
            |       otherwise use function handle in :avg:.
        :avg_axis:
            | axis to calculate average over, optional
        :out: 
            | 'DEi' or str, optional
            | Requested output.
        
    Note:
        For the other input arguments, see specific color space used.
        
    Returns:
        :returns: 
            | ndarray with DEi [, DEa] or other as specified by :out:
            
    References:
        1. `Sharma, G., Wu, W., & Dalal, E. N. (2005). 
        The CIEDE2000 color‐difference formula: Implementation notes, 
        supplementary test data, and mathematical observations. 
        Color Research & Application, 30(1), 21–30. 
        <https://doi.org/10.1002/col.20070>`_
    """

    if KLCH is None:
        KLCH = [1, 1, 1]

    if dtype == 'xyz':
        labt = xyz_to_lab(xyzt, xyzw=xyzwt)
        labr = xyz_to_lab(xyzr, xyzw=xyzwr)
    else:
        labt = xyzt
        labr = xyzr

    Lt = labt[..., 0:1]
    at = labt[..., 1:2]
    bt = labt[..., 2:3]
    Ct = np.sqrt(at**2 + bt**2)
    #ht = cam.hue_angle(at,bt,htype = 'rad')

    Lr = labr[..., 0:1]
    ar = labr[..., 1:2]
    br = labr[..., 2:3]
    Cr = np.sqrt(ar**2 + br**2)
    #hr = cam.hue_angle(at,bt,htype = 'rad')

    # Step 1:
    Cavg = (Ct + Cr) / 2
    G = 0.5 * (1 - np.sqrt((Cavg**7.0) / ((Cavg**7.0) + (25.0**7))))
    apt = (1 + G) * at
    apr = (1 + G) * ar

    Cpt = np.sqrt(apt**2 + bt**2)
    Cpr = np.sqrt(apr**2 + br**2)
    Cpprod = Cpt * Cpr

    hpt = cam.hue_angle(apt, bt, htype='deg')
    hpr = cam.hue_angle(apr, br, htype='deg')
    hpt[(apt == 0) * (bt == 0)] = 0
    hpr[(apr == 0) * (br == 0)] = 0

    # Step 2:
    dL = np.abs(Lr - Lt)
    dCp = np.abs(Cpr - Cpt)
    dhp_ = hpr - hpt

    dhp = dhp_.copy()
    dhp[np.where(np.abs(dhp_) > 180)] = dhp[np.where(np.abs(dhp_) > 180)] - 360
    dhp[np.where(
        np.abs(dhp_) < -180)] = dhp[np.where(np.abs(dhp_) < -180)] + 360
    dhp[np.where(Cpprod == 0)] = 0

    #dH = 2*np.sqrt(Cpprod)*np.sin(dhp/2*np.pi/180)
    dH = deltaH(dhp, Cpprod, htype='deg')

    # Step 3:
    Lp = (Lr + Lt) / 2
    Cp = (Cpr + Cpt) / 2

    hps = hpt + hpr
    hp = (hpt + hpr) / 2
    hp[np.where((np.abs(dhp_) > 180)
                & (hps < 360))] = hp[np.where((np.abs(dhp_) > 180)
                                              & (hps < 360))] + 180
    hp[np.where((np.abs(dhp_) > 180)
                & (hps >= 360))] = hp[np.where((np.abs(dhp_) > 180)
                                               & (hps >= 360))] - 180
    hp[np.where(Cpprod == 0)] = 0

    T = 1 - 0.17*np.cos((hp - 30)*np.pi/180) + 0.24*np.cos(2*hp*np.pi/180) +\
        0.32*np.cos((3*hp + 6)*np.pi/180) - 0.20*np.cos((4*hp - 63)*np.pi/180)
    dtheta = 30 * np.exp(-((hp - 275) / 25)**2)
    RC = 2 * np.sqrt((Cp**7) / ((Cp**7) + (25**7)))
    SL = 1 + ((0.015 * (Lp - 50)**2) / np.sqrt(20 + (Lp - 50)**2))
    SC = 1 + 0.045 * Cp
    SH = 1 + 0.015 * Cp * T
    RT = -np.sin(2 * dtheta * np.pi / 180) * RC

    kL, kC, kH = KLCH

    DEi = ((dL / (kL * SL))**2, (dCp / (kC * SC))**2 + (dH / (kH * SH))**2 +
           RT * (dCp / (kC * SC)) * (dH / (kH * SH)))

    return _process_DEi(DEi,
                        DEtype=DEtype,
                        avg=avg,
                        avg_axis=avg_axis,
                        out=out)
Ejemplo n.º 5
0
# check xyz_to_cct():
cct, duv = lx.xyz_to_cct(xyzw, cieobs='1931_2', out=2)

# check xyz_to_..., ..._to_xyz:
labw = lx.xyz_to_Yxy(xyzw)
lab = lx.xyz_to_Yxy(xyz)
xyzw_ = lx.Yxy_to_xyz(labw)
xyz_ = lx.Yxy_to_xyz(lab)

labw = lx.xyz_to_Yuv(xyzw)
lab = lx.xyz_to_Yuv(xyz)
xyzw_ = lx.Yuv_to_xyz(labw)
xyz_ = lx.Yuv_to_xyz(lab)

labw = lx.xyz_to_lab(xyzw, xyzw=xyzw[:1, :])
lab = lx.xyz_to_lab(xyz, xyzw=xyzw[:1, :])
xyzw_ = lx.lab_to_xyz(labw, xyzw=xyzw[:1, :])
xyz_ = lx.lab_to_xyz(lab, xyzw=xyzw[:1, :])

labw = lx.xyz_to_luv(xyzw, xyzw=xyzw)
lab = lx.xyz_to_luv(xyz, xyzw=xyzw)
xyzw_ = lx.luv_to_xyz(labw, xyzw=xyzw)
xyz_ = lx.luv_to_xyz(lab, xyzw=xyzw)

labw = lx.xyz_to_ipt(xyzw)
lab = lx.xyz_to_ipt(xyz)
xyzw_ = lx.ipt_to_xyz(labw)
xyz_ = lx.ipt_to_xyz(lab)

labw = lx.xyz_to_wuv(xyzw, xyzw=xyzw)
Ejemplo n.º 6
0
def apply_ciecat94(xyz,
                   xyzw,
                   xyzwr=None,
                   E=1000,
                   Er=1000,
                   Yb=20,
                   D=1,
                   cat94_old=True):
    """ 
    Calculate corresponding color tristimulus values using the CIECAT94 chromatic adaptation transform.
    
    Args:
        :xyz:
            | ndarray with sample 1931 2° XYZ tristimulus values under the test illuminant
        :xyzw:
            | ndarray with white point tristimulus values of the test illuminant
        :xyzwr:
            | None, optional
            | ndarray with white point tristimulus values of the reference illuminant
            | None defaults to D65.
        :E:
            | 100, optional
            | Illuminance (lx) of test illumination
        :Er:
            | 63.66, optional
            | Illuminance (lx) of the reference illumination
        :Yb:
            | 20, optional
            | Relative luminance of the adaptation field (background) 
        :D:
            | 1, optional
            | Degree of chromatic adaptation.
            | For object colours D = 1,  
            | and for luminous colours (typically displays) D=0
            
    Returns:
        :xyzc:
            | ndarray with corresponding tristimlus values.

    Reference:
        1. CIE160-2004. (2004). A review of chromatic adaptation transforms (Vols. CIE160-200). CIE.
    """
    #--------------------------------------------
    # Define cone/chromatic adaptation sensor space:
    mcat = _MCATS['kries']
    invmcat = np.linalg.inv(mcat)

    #--------------------------------------------
    # Define default ref. white point:
    if xyzwr is None:
        xyzwr = np.array(
            [[9.5047e+01, 1.0000e+02, 1.0888e+02]]
        )  #spd_to_xyz(_CIE_D65, cieobs = '1931_2', relative = True, rfl = None)

    #--------------------------------------------
    # Calculate Y,x,y of white:
    Yxyw = xyz_to_Yxy(xyzw)
    Yxywr = xyz_to_Yxy(xyzwr)

    #--------------------------------------------
    # Calculate La, Lar:
    La = Yb * E / np.pi / 100
    Lar = Yb * Er / np.pi / 100

    #--------------------------------------------
    # Calculate CIELAB L* of samples:
    Lstar = xyz_to_lab(xyz, xyzw)[..., 0]

    #--------------------------------------------
    # Define xi_, eta_ and zeta_ functions:
    xi_ = lambda Yxy: (0.48105 * Yxy[..., 1] + 0.78841 * Yxy[..., 2] - 0.080811
                       ) / Yxy[..., 2]
    eta_ = lambda Yxy: (-0.27200 * Yxy[..., 1] + 1.11962 * Yxy[..., 2] +
                        0.04570) / Yxy[..., 2]
    zeta_ = lambda Yxy: 0.91822 * (1 - Yxy[..., 1] - Yxy[..., 2]) / Yxy[..., 2]

    #--------------------------------------------
    # Calculate intermediate values for test and ref. illuminants:
    xit, etat, zetat = xi_(Yxyw), eta_(Yxyw), zeta_(Yxyw)
    xir, etar, zetar = xi_(Yxywr), eta_(Yxywr), zeta_(Yxywr)

    #--------------------------------------------
    # Calculate alpha:
    if cat94_old == False:
        alpha = 0.1151 * np.log10(La) + 0.0025 * (Lstar - 50) + (0.22 * D +
                                                                 0.510)
        alpha[alpha > 1] = 1
    else:
        alpha = 1

    #--------------------------------------------
    # Calculate adapted intermediate xip, etap zetap:
    xip = alpha * xit - (1 - alpha) * xir
    etap = alpha * etat - (1 - alpha) * etar
    zetap = alpha * zetat - (1 - alpha) * zetar

    #--------------------------------------------
    # Calculate effective adapting response Rw, Gw, Bw and Rwr, Gwr, Bwr:
    #Rw, Gw, Bw = La*xit, La*etat, La*zetat # according to westland's book: Computational Colour Science wirg Matlab
    Rw, Gw, Bw = La * xip, La * etap, La * zetap  # according to CIE160-2004
    Rwr, Gwr, Bwr = Lar * xir, Lar * etar, Lar * zetar

    #--------------------------------------------
    # Calculate beta1_ and beta2_ exponents for (R,G) and B:
    beta1_ = lambda x: (6.469 + 6.362 * x**0.4495) / (6.469 + x**0.4495)
    beta2_ = lambda x: 0.7844 * (8.414 + 8.091 * x**0.5128) / (8.414 + x**
                                                               0.5128)
    b1Rw, b1Rwr, b1Gw, b1Gwr = beta1_(Rw), beta1_(Rwr), beta1_(Gw), beta1_(Gwr)
    b2Bw, b2Bwr = beta2_(Bw), beta2_(Bwr)

    #--------------------------------------------
    # Noise term:
    n = 1 if cat94_old else 0.1

    #--------------------------------------------
    # K factor = p/q (for correcting the difference between
    # the illuminance of the test and references conditions)
    # calculate q:
    p = ((Yb * xip + n) /
         (20 * xip + n))**(2 / 3 * b1Rw) * ((Yb * etap + n) /
                                            (20 * etap + n))**(1 / 3 * b1Gw)
    q = ((Yb * xir + n) /
         (20 * xir + n))**(2 / 3 * b1Rwr) * ((Yb * etar + n) /
                                             (20 * etar + n))**(1 / 3 * b1Gwr)
    K = p / q

    #--------------------------------------------
    # transform sample xyz to cat sensor space:
    rgb = math.dot23(mcat, xyz.T).T

    #--------------------------------------------
    # Calculate corresponding colors:
    Rc = (Yb * xir + n) * K**(1 / b1Rwr) * ((rgb[..., 0] + n) /
                                            (Yb * xip + n))**(b1Rw / b1Rwr) - n
    Gc = (Yb * etar + n) * K**(1 / b1Gwr) * (
        (rgb[..., 1] + n) / (Yb * etap + n))**(b1Gw / b1Gwr) - n
    Bc = (Yb * zetar + n) * K**(1 / b2Bwr) * (
        (rgb[..., 2] + n) / (Yb * zetap + n))**(b2Bw / b2Bwr) - n

    #--------------------------------------------
    # transform to xyz and return:
    xyzc = math.dot23(invmcat, ajoin((Rc, Gc, Bc)).T).T

    return xyzc