예제 #1
0
파일: test_class.py 프로젝트: uhqinli/luxpy
 def test_Yuv(self, getvars):
     S, spds, rfls, xyz, xyzw = getvars
     labw = lx.xyz_to_Yuv(xyzw)
     lab = lx.xyz_to_Yuv(xyz)
     xyzw_ = lx.Yuv_to_xyz(labw)
     xyz_ = lx.Yuv_to_xyz(lab)
     assert np.isclose(xyz, xyz_).all()
     assert np.isclose(xyzw, xyzw_).all()
예제 #2
0
def xyz_to_space(input_type='RGB',
                 R=None,
                 G=None,
                 B=None,
                 W=None,
                 file='GW_2019-09-13.txt',
                 space='uv'):
    """
    [ADD THIS]

    Parameters
    ----------
    input_type : TYPE, optional
        DESCRIPTION. The default is 'RGB'.
    R : TYPE, optional
        DESCRIPTION. The default is None.
    G : TYPE, optional
        DESCRIPTION. The default is None.
    B : TYPE, optional
        DESCRIPTION. The default is None.
    W : TYPE, optional
        DESCRIPTION. The default is None.
    file : TYPE, optional
        DESCRIPTION. The default is 'GW_2019-09-13.txt'.
    space : TYPE, optional
        DESCRIPTION. The default is 'uv'.

    Returns
    -------
    TYPE
        DESCRIPTION.

    """

    if input_type == 'RGB':
        R_Yuv = lx.xyz_to_Yuv(R)
        G_Yuv = lx.xyz_to_Yuv(G)
        B_Yuv = lx.xyz_to_Yuv(B)
        return R_Yuv, G_Yuv, B_Yuv

    elif input_type == 'RGBW':
        R_Yuv = lx.xyz_to_Yuv(R)
        G_Yuv = lx.xyz_to_Yuv(G)
        B_Yuv = lx.xyz_to_Yuv(B)
        W_Yuv = lx.xyz_to_Yuv(W)
        return R_Yuv, G_Yuv, B_Yuv, W_Yuv

    elif input_type == 'file':
        XYZs = np.loadtxt(file, delimiter='\t')
        if space == 'uv':
            output = lx.xyz_to_Yuv(XYZs)
        else:
            output = lx.xyz_to_Yxy(XYZs)
        for count, _ in enumerate(output):
            count = count + 1
        return XYZs, output, count
예제 #3
0
def _plot_tm30_report_bottom(axh, spd, notes = '', max_len_notes_line = 40):
    """
    Print some notes, the CIE x, y, u',v' and Ra, R9 values of the source in some empty axes.
    
    Args:
        :axh: 
            | None, optional
            | Plot on specified axes. 
        :spd:
            | ndarray or dict
            | If ndarray: single spectral power distribution.
        :notes:
            | string to be split
        :max_len_notes_line:
            | 40, optional
            | Maximum length of a single line when splitting the string.
        
    Returns:
        :axh:
            | handle to figure axes.    
    """
    ciera = spd_to_cri(spd, cri_type = 'ciera')
    cierai = spd_to_cri(spd, cri_type = 'ciera-14', out = 'Rfi')
    xyzw = spd_to_xyz(spd, cieobs = '1931_2', relative = True)
    Yxyw = xyz_to_Yxy(xyzw)
    Yuvw = xyz_to_Yuv(xyzw)
    
    notes_ = _split_notes(notes, max_len_notes_line = max_len_notes_line)

    axh.set_xticks(np.arange(10))
    axh.set_xticklabels(['' for i in np.arange(10)])
    axh.set_yticks(np.arange(4))
    axh.set_yticklabels(['' for i in np.arange(4)])
    axh.set_axis_off()
    axh.set_xlabel([])
    
    axh.text(0,2.8, 'Notes: ', fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(0.75,2.8,  notes_, fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(6,2.8, "x   {:1.4f}".format(Yxyw[0,1]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(6,2.2, "y   {:1.4f}".format(Yxyw[0,2]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(6,1.6, "u'  {:1.4f}".format(Yuvw[0,1]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(6,1.0, "v'  {:1.4f}".format(Yuvw[0,2]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(7.5,2.8, "CIE 13.3-1995", fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(7.5,2.2, "     (CRI)    ", fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(7.5,1.6, "    $R_a$  {:1.0f}".format(ciera[0,0]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')
    axh.text(7.5,1.0, "    $R_9$  {:1.0f}".format(cierai[9,0]), fontsize = 9, horizontalalignment='left',verticalalignment='top',color = 'k')

    # Create a Rectangle patch
    rect = patches.Rectangle((7.2,0.5),1.7,2.5,linewidth=1,edgecolor='k',facecolor='none')
    
    # Add the patch to the Axes
    axh.add_patch(rect)

    return axh
예제 #4
0
파일: CDATA.py 프로젝트: simongr2/luxpy
    def to_Yuv(self, **kwargs):
        """ 
        Convert XYZ tristimulus values CIE 1976 Yu'v' chromaticity values.

            
        Returns:
            :Yuv: 
                | luxpy.LAB with .value field that is a ndarray 
                | with CIE 1976 Yu'v' chromaticity values.
                | (Y value refers to luminance or luminance factor)
           """
        return LAB(value=xyz_to_Yuv(self.value),
                   relative=self.relative,
                   cieobs=self.cieobs,
                   dtype='Yuv')
예제 #5
0
def xyz_to_neutrality_smet2018(xyz10, nlocitype='uw', uw_model='Linvar'):
    """
    Calculate degree of neutrality using the unique white model in Smet et al. (2014) or the normalized (max = 1) degree of chromatic adaptation model from Smet et al. (2017).
    
    Args:
        :xyz10:
            | ndarray with CIE 1964 10° xyz tristimulus values.
        :nlocitype:
            | 'uw', optional
            | 'uw': use unique white models published in Smet et al. (2014).
            | 'ca': use degree of chromatic adaptation model from Smet et al. (2017).
        :uw_model:
            | 'Linvar', optional
            | Use Luminance invariant unique white model from Smet et al. (2014).
            | Other options: 'L200' (200 cd/m²), 'L1000' (1000 cd/m²) and 'L2000' (2000 cd/m²).
    
    Returns:
        :N: 
            | ndarray with calculated neutrality
            
    References:
        1. `Smet, K., Deconinck, G., & Hanselaer, P., (2014), 
        Chromaticity of unique white in object mode. 
        Optics Express, 22(21), 25830–25841. 
        <https://www.osapublishing.org/oe/abstract.cfm?uri=oe-22-21-25830>`_
        
        2. `Smet, K.A.G., Zhai, Q., Luo, M.R., Hanselaer, P., (2017), 
        Study of chromatic adaptation using memory color matches, 
        Part II: colored illuminants, 
        Opt. Express, 25(7), pp. 8350-8365. 
        <https://www.osapublishing.org/oe/abstract.cfm?uri=oe-25-7-8350&origin=search)>`_
    """

    if nlocitype == 'uw':
        uv = xyz_to_Yuv(xyz10)[..., 1:]
        G0 = lambda up, vp, a: np.exp(-0.5 * (a[0] * (up - a[2])**2 + a[1] *
                                              (vp - a[3])**2 + 2 * a[4] *
                                              (up - a[2]) * (vp - a[3])))
        return G0(uv[..., 0:1], uv[..., 1:2],
                  _UW_NEUTRALITY_PARAMETERS_SMET2014[uw_model])
    elif nlocitype == 'ca':
        return cat.smet2017_D(xyz10, Dmax=1).T
    else:
        raise Exception('Unrecognized nlocitype')
예제 #6
0
def xyz_to_yuv(xyzs):
    """
    [ADD THIS]

    Parameters
    ----------
    xyzs : TYPE
        DESCRIPTION.

    Returns
    -------
    u_v_ : TYPE
        DESCRIPTION.

    """

    yuv = lx.xyz_to_Yuv(xyzs)

    return yuv
예제 #7
0
def calculate_lut(ccts=None, cieobs=None, add_to_lut=True):
    """
    Function that calculates LUT for the ccts stored in 
    ./data/cctluts/cct_lut_cctlist.dat or given as input argument.
    Calculation is performed for CMF set specified in cieobs. 
    Adds a new (temprorary) field to the _CCT_LUT dict.
    
    Args:
        :ccts: 
            | ndarray or str, optional
            | list of ccts for which to (re-)calculate the LUTs.
            | If str, ccts contains path/filename.dat to list.
        :cieobs: 
            | None or str, optional
            | str specifying cmf set.
            
    Returns:
        :returns: 
            | ndarray with cct and duv.
        
    Note:
        Function changes the global variable: _CCT_LUT!
    """
    if ccts is None:
        ccts = getdata('{}cct_lut_cctlist.dat'.format(_CCT_LUT_PATH))
    elif isinstance(ccts, str):
        ccts = getdata(ccts)

    Yuv = np.ones((ccts.shape[0], 2)) * np.nan
    for i, cct in enumerate(ccts):
        Yuv[i, :] = xyz_to_Yuv(
            spd_to_xyz(blackbody(cct, wl3=[360, 830, 1]), cieobs=cieobs))[:,
                                                                          1:3]
    u = Yuv[:, 0, None]  # get CIE 1960 u
    v = (2.0 / 3.0) * Yuv[:, 1, None]  # get CIE 1960 v
    cctuv = np.hstack((ccts, u, v))
    if add_to_lut == True:
        _CCT_LUT[cieobs] = cctuv
    return cctuv
예제 #8
0
def xyz_to_cct_ohno2011(xyz):
    """
    Calculate cct and Duv from CIE 1931 2° xyz following Ohno (2011).
    
    Args:
        :xyz:
            | ndarray with CIE 1931 2° X,Y,Z tristimulus values
            
    Returns:
        :cct, duv:
            | ndarrays with correlated color temperatures and distance to blackbody locus in CIE 1960 uv
            
    References:
        1. Ohno, Y. (2011). Calculation of CCT and Duv and Practical Conversion Formulae. 
        CORM 2011 Conference, Gaithersburg, MD, May 3-5, 2011
    """
    uvp = xyz_to_Yuv(xyz)[..., 1:]
    uv = uvp * np.array([[1, 2 / 3]])
    Lfp = ((uv[..., 0] - 0.292)**2 + (uv[..., 1] - 0.24)**2)**0.5
    a = np.arctan((uv[..., 1] - 0.24) / (uv[..., 0] - 0.292))
    a[a < 0] = a[a < 0] + np.pi
    Lbb = np.polyval(_KIJ[0, :], a)
    Duv = Lfp - Lbb

    T1 = 1 / np.polyval(_KIJ[1, :], a)
    T1[a >= 2.54] = 1 / np.polyval(_KIJ[2, :], a[a >= 2.54])
    dTc1 = np.polyval(_KIJ[3, :], a) * (Lbb + 0.01) / Lfp * Duv / 0.01
    dTc1[a >= 2.54] = 1 / np.polyval(_KIJ[4, :], a[a >= 2.54]) * (
        Lbb[a >= 2.54] + 0.01) / Lfp[a >= 2.54] * Duv[a >= 2.54] / 0.01
    T2 = T1 - dTc1
    c = np.log10(T2)
    c[T2 == 0] = -np.inf
    dTc2 = np.polyval(_KIJ[5, :], c)
    dTc2[Duv < 0] = np.polyval(_KIJ[6, :], c[Duv < 0]) * np.abs(
        Duv[Duv < 0] / 0.03)**2
    Tfinal = T2 - dTc2
    return Tfinal, Duv
예제 #9
0
def xyz_to_u_v_(xyz):
    """
    Conver XYZ to u', v'.|
    ---------------
    Parameters:
        :xyz: array (shape: (1, 3))
    Returns:
        :u_v_mean: array (shape: (1, 2))
    """
    y_uv = lx.xyz_to_Yuv(xyz)
    y_uv_mean = np.array(
        [[y_uv[:, 0].mean(), y_uv[:, 1].mean(), y_uv[:, 2].mean()]])

    y_uv_mean = np.around(y_uv_mean, decimals=3)

    u_mean = y_uv_mean[:, 1]
    u_mean = str(u_mean)[1:-1]
    v_mean = y_uv_mean[:, 2]
    v_mean = str(v_mean)[1:-1]

    u_v_mean = np.array([[u_mean], [v_mean]])
    u_v_mean = u_v_mean.T

    return u_v_mean
예제 #10
0
def cct_to_xyz(ccts,
               duv=None,
               cieobs=_CIEOBS,
               wl=None,
               mode='lut',
               out=None,
               accuracy=0.1,
               force_out_of_lut=True,
               upper_cct_max=10.0 * 20,
               approx_cct_temp=True):
    """
    Convert correlated color temperature (CCT) and Duv (distance above (>0) or 
    below (<0) the Planckian locus) to XYZ tristimulus values.
    
    | Finds xyzw_estimated by minimization of:
    |    
    |    F = numpy.sqrt(((100.0*(cct_min - cct)/(cct))**2.0) 
    |         + (((duv_min - duv)/(duv))**2.0))
    |    
    | with cct,duv the input values and cct_min, duv_min calculated using 
    | luxpy.xyz_to_cct(xyzw_estimated,...).
    
    Args:
        :ccts: 
            | ndarray of cct values
        :duv: 
            | None or ndarray of duv values, optional
            | Note that duv can be supplied together with cct values in :ccts: 
              as ndarray with shape (N,2)
        :cieobs: 
            | luxpy._CIEOBS, optional
            | CMF set used to calculated xyzw.
        :mode: 
            | 'lut' or 'search', optional
            | Determines what method to use.
        :out: 
            | None (or 1), optional
            | If not None or 1: output a ndarray that contains estimated 
              xyz and minimization results: 
            | (cct_min, duv_min, F_min (objective fcn value))
        :wl: 
            | None, optional
            | Wavelengths used when calculating Planckian radiators.
        :accuracy: 
            | float, optional
            | Stop brute-force search when cct :accuracy: is reached.
        :upper_cct_max: 
            | 10.0**20, optional
            | Limit brute-force search to this cct.
        :approx_cct_temp: 
            | True, optional
            | If True: use xyz_to_cct_HA() to get a first estimate of cct to 
              speed up search.
        :force_out_of_lut: 
            | True, optional
            | If True and cct is out of range of the LUT, then switch to 
              brute-force search method, else return numpy.nan values.
        
    Returns:
        :returns: 
            | ndarray with estimated XYZ tristimulus values
    
    Note:
        If duv is not supplied (:ccts:.shape is (N,1) and :duv: is None), 
        source is assumed to be on the Planckian locus.
	 """
    # make ccts a min. 2d np.array:
    if isinstance(ccts, list):
        ccts = np2dT(np.array(ccts))
    else:
        ccts = np2d(ccts)

    if len(ccts.shape) > 2:
        raise Exception('cct_to_xyz(): Input ccts.shape must be <= 2 !')

    # get cct and duv arrays from :ccts:
    cct = np2d(ccts[:, 0, None])

    if (duv is None) & (ccts.shape[1] == 2):
        duv = np2d(ccts[:, 1, None])
    elif duv is not None:
        duv = np2d(duv)

    #get estimates of approximate xyz values in case duv = None:
    BB = cri_ref(ccts=cct, wl3=wl, ref_type=['BB'])
    xyz_est = spd_to_xyz(data=BB, cieobs=cieobs, out=1)
    results = np.ones([ccts.shape[0], 3]) * np.nan

    if duv is not None:

        # optimization/minimization setup:
        def objfcn(uv_offset,
                   uv0,
                   cct,
                   duv,
                   out=1):  #, cieobs = cieobs, wl = wl, mode = mode):
            uv0 = np2d(uv0 + uv_offset)
            Yuv0 = np.concatenate((np2d([100.0]), uv0), axis=1)
            cct_min, duv_min = xyz_to_cct(Yuv_to_xyz(Yuv0),
                                          cieobs=cieobs,
                                          out='cct,duv',
                                          wl=wl,
                                          mode=mode,
                                          accuracy=accuracy,
                                          force_out_of_lut=force_out_of_lut,
                                          upper_cct_max=upper_cct_max,
                                          approx_cct_temp=approx_cct_temp)
            F = np.sqrt(((100.0 * (cct_min[0] - cct[0]) / (cct[0]))**2.0) +
                        (((duv_min[0] - duv[0]) / (duv[0]))**2.0))
            if out == 'F':
                return F
            else:
                return np.concatenate((cct_min, duv_min, np2d(F)), axis=1)

        # loop through each xyz_est:
        for i in range(xyz_est.shape[0]):
            xyz0 = xyz_est[i]
            cct_i = cct[i]
            duv_i = duv[i]
            cct_min, duv_min = xyz_to_cct(xyz0,
                                          cieobs=cieobs,
                                          out='cct,duv',
                                          wl=wl,
                                          mode=mode,
                                          accuracy=accuracy,
                                          force_out_of_lut=force_out_of_lut,
                                          upper_cct_max=upper_cct_max,
                                          approx_cct_temp=approx_cct_temp)

            if np.abs(duv[i]) > _EPS:
                # find xyz:
                Yuv0 = xyz_to_Yuv(xyz0)
                uv0 = Yuv0[0][1:3]

                OptimizeResult = minimize(fun=objfcn,
                                          x0=np.zeros((1, 2)),
                                          args=(uv0, cct_i, duv_i, 'F'),
                                          method='Nelder-Mead',
                                          options={
                                              "maxiter": np.inf,
                                              "maxfev": np.inf,
                                              'xatol': 0.000001,
                                              'fatol': 0.000001
                                          })
                betas = OptimizeResult['x']
                #betas = np.zeros(uv0.shape)
                if out is not None:
                    results[i] = objfcn(betas, uv0, cct_i, duv_i, out=3)

                uv0 = np2d(uv0 + betas)
                Yuv0 = np.concatenate((np2d([100.0]), uv0), axis=1)
                xyz_est[i] = Yuv_to_xyz(Yuv0)

            else:
                xyz_est[i] = xyz0

    if (out is None) | (out == 1):
        return xyz_est
    else:
        # Also output results of minimization:
        return np.concatenate((xyz_est, results), axis=1)
예제 #11
0
def xyz_to_cct_ohno(xyzw,
                    cieobs=_CIEOBS,
                    out='cct',
                    wl=None,
                    accuracy=0.1,
                    force_out_of_lut=True,
                    upper_cct_max=10.0**20,
                    approx_cct_temp=True):
    """
    Convert XYZ tristimulus values to correlated color temperature (CCT) and 
    Duv (distance above (>0) or below (<0) the Planckian locus) 
    using Ohno's method. 
    
    Args:
        :xyzw: 
            | ndarray of tristimulus values
        :cieobs: 
            | luxpy._CIEOBS, optional
            | CMF set used to calculated xyzw.
        :out: 
            | 'cct' (or 1), optional
            | Determines what to return.
            | Other options: 'duv' (or -1), 'cct,duv'(or 2), "[cct,duv]" (or -2)
        :wl: 
            | None, optional
            | Wavelengths used when calculating Planckian radiators.
        :accuracy: 
            | float, optional
            | Stop brute-force search when cct :accuracy: is reached.
        :upper_cct_max: 
            | 10.0**20, optional
            | Limit brute-force search to this cct.
        :approx_cct_temp: 
            | True, optional
            | If True: use xyz_to_cct_HA() to get a first estimate of cct 
              to speed up search.
        :force_out_of_lut: 
            | True, optional
            | If True and cct is out of range of the LUT, then switch to 
              brute-force search method, else return numpy.nan values.
        
    Returns:
        :returns: 
            | ndarray with:
            |    cct: out == 'cct' (or 1)
            |    duv: out == 'duv' (or -1)
            |    cct, duv: out == 'cct,duv' (or 2)
            |    [cct,duv]: out == "[cct,duv]" (or -2) 
            
    Note:
        LUTs are stored in ./data/cctluts/
        
    Reference:
        1. `Ohno Y. Practical use and calculation of CCT and Duv. 
        Leukos. 2014 Jan 2;10(1):47-55.
        <http://www.tandfonline.com/doi/abs/10.1080/15502724.2014.839020>`_
    """

    xyzw = np2d(xyzw)

    if len(xyzw.shape) > 2:
        raise Exception('xyz_to_cct_ohno(): Input xyzwa.ndim must be <= 2 !')

    # get 1960 u,v of test source:
    Yuv = xyz_to_Yuv(
        xyzw)  # remove possible 1-dim + convert xyzw to CIE 1976 u',v'
    axis_of_v3 = len(Yuv.shape) - 1  # axis containing color components
    u = Yuv[:, 1, None]  # get CIE 1960 u
    v = (2.0 / 3.0) * Yuv[:, 2, None]  # get CIE 1960 v

    uv = np2d(np.concatenate((u, v), axis=axis_of_v3))

    # load cct & uv from LUT:
    if cieobs not in _CCT_LUT:
        _CCT_LUT[cieobs] = calculate_lut(ccts=None,
                                         cieobs=cieobs,
                                         add_to_lut=False)
    cct_LUT = _CCT_LUT[cieobs][:, 0, None]
    uv_LUT = _CCT_LUT[cieobs][:, 1:3]

    # calculate CCT of each uv:
    CCT = np.ones(uv.shape[0]) * np.nan  # initialize with NaN's
    Duv = CCT.copy()  # initialize with NaN's
    idx_m = 0
    idx_M = uv_LUT.shape[0] - 1
    for i in range(uv.shape[0]):
        out_of_lut = False
        delta_uv = (((uv_LUT - uv[i])**2.0).sum(
            axis=1))**0.5  # calculate distance of uv with uv_LUT
        idx_min = delta_uv.argmin()  # find index of minimum distance

        # find Tm, delta_uv and u,v for 2 points surrounding uv corresponding to idx_min:
        if idx_min == idx_m:
            idx_min_m1 = idx_min
            out_of_lut = True
        else:
            idx_min_m1 = idx_min - 1
        if idx_min == idx_M:
            idx_min_p1 = idx_min
            out_of_lut = True
        else:
            idx_min_p1 = idx_min + 1

        if (out_of_lut == True) & (force_out_of_lut
                                   == True):  # calculate using search-function
            cct_i, Duv_i = xyz_to_cct_search(xyzw[i],
                                             cieobs=cieobs,
                                             wl=wl,
                                             accuracy=accuracy,
                                             out='cct,duv',
                                             upper_cct_max=upper_cct_max,
                                             approx_cct_temp=approx_cct_temp)
            CCT[i] = cct_i
            Duv[i] = Duv_i
            continue
        elif (out_of_lut == True) & (force_out_of_lut == False):
            CCT[i] = np.nan
            Duv[i] = np.nan

        cct_m1 = cct_LUT[idx_min_m1]  # - 2*_EPS
        delta_uv_m1 = delta_uv[idx_min_m1]
        uv_m1 = uv_LUT[idx_min_m1]
        cct_p1 = cct_LUT[idx_min_p1]
        delta_uv_p1 = delta_uv[idx_min_p1]
        uv_p1 = uv_LUT[idx_min_p1]

        cct_0 = cct_LUT[idx_min]
        delta_uv_0 = delta_uv[idx_min]

        # calculate uv distance between Tm_m1 & Tm_p1:
        delta_uv_p1m1 = ((uv_p1[0] - uv_m1[0])**2.0 +
                         (uv_p1[1] - uv_m1[1])**2.0)**0.5

        # Triangular solution:
        x = ((delta_uv_m1**2) - (delta_uv_p1**2) +
             (delta_uv_p1m1**2)) / (2 * delta_uv_p1m1)
        Tx = cct_m1 + ((cct_p1 - cct_m1) * (x / delta_uv_p1m1))
        #uBB = uv_m1[0] + (uv_p1[0] - uv_m1[0]) * (x / delta_uv_p1m1)
        vBB = uv_m1[1] + (uv_p1[1] - uv_m1[1]) * (x / delta_uv_p1m1)

        Tx_corrected_triangular = Tx * 0.99991
        signDuv = np.sign(uv[i][1] - vBB)
        Duv_triangular = signDuv * np.atleast_1d(
            ((delta_uv_m1**2.0) - (x**2.0))**0.5)

        # Parabolic solution:
        a = delta_uv_m1 / (cct_m1 - cct_0 + _EPS) / (cct_m1 - cct_p1 + _EPS)
        b = delta_uv_0 / (cct_0 - cct_m1 + _EPS) / (cct_0 - cct_p1 + _EPS)
        c = delta_uv_p1 / (cct_p1 - cct_0 + _EPS) / (cct_p1 - cct_m1 + _EPS)
        A = a + b + c
        B = -(a * (cct_p1 + cct_0) + b * (cct_p1 + cct_m1) + c *
              (cct_0 + cct_m1))
        C = (a * cct_p1 * cct_0) + (b * cct_p1 * cct_m1) + (c * cct_0 * cct_m1)
        Tx = -B / (2 * A + _EPS)
        Tx_corrected_parabolic = Tx * 0.99991
        Duv_parabolic = signDuv * (A * np.power(Tx_corrected_parabolic, 2) +
                                   B * Tx_corrected_parabolic + C)

        Threshold = 0.002
        if Duv_triangular < Threshold:
            CCT[i] = Tx_corrected_triangular
            Duv[i] = Duv_triangular
        else:
            CCT[i] = Tx_corrected_parabolic
            Duv[i] = Duv_parabolic

    # Regulate output:
    if (out == 'cct') | (out == 1):
        return np2dT(CCT)
    elif (out == 'duv') | (out == -1):
        return np2dT(Duv)
    elif (out == 'cct,duv') | (out == 2):
        return np2dT(CCT), np2dT(Duv)
    elif (out == "[cct,duv]") | (out == -2):
        return np.vstack((CCT, Duv)).T
예제 #12
0
def xyz_to_cct_search(xyzw,
                      cieobs=_CIEOBS,
                      out='cct',
                      wl=None,
                      accuracy=0.1,
                      upper_cct_max=10.0**20,
                      approx_cct_temp=True):
    """
    Convert XYZ tristimulus values to correlated color temperature (CCT) and 
    Duv(distance above (> 0) or below ( < 0) the Planckian locus) by a 
    brute-force search. 

    | The algorithm uses an approximate cct_temp (HA approx., see xyz_to_cct_HA) 
      as starting point or uses the middle of the allowed cct-range 
      (1e2 K - 1e20 K, higher causes overflow) on a log-scale, then constructs 
      a 4-step section of the blackbody (Planckian) locus on which to find the
      minimum distance to the 1960 uv chromaticity of the test source.

    Args:
        :xyzw: 
            | ndarray of tristimulus values
        :cieobs: 
            | luxpy._CIEOBS, optional
            | CMF set used to calculated xyzw.
        :out: 
            | 'cct' (or 1), optional
            | Determines what to return.
            | Other options: 'duv' (or -1), 'cct,duv'(or 2), "[cct,duv]" (or -2)
        :wl: 
            | None, optional
            | Wavelengths used when calculating Planckian radiators.
        :accuracy: 
            | float, optional
            | Stop brute-force search when cct :accuracy: is reached.
        :upper_cct_max: 
            | 10.0**20, optional
            | Limit brute-force search to this cct.
        :approx_cct_temp: 
            | True, optional
            | If True: use xyz_to_cct_HA() to get a first estimate of cct to 
              speed up search.

    Returns:
        :returns: 
            | ndarray with:
            |    cct: out == 'cct' (or 1)
            |    duv: out == 'duv' (or -1)
            |    cct, duv: out == 'cct,duv' (or 2)
            |    [cct,duv]: out == "[cct,duv]" (or -2) 
    
    Notes:
        This program is more accurate, but slower than xyz_to_cct_ohno!
        Note that cct must be between 1e3 K - 1e20 K 
        (very large cct take a long time!!!)
    """

    xyzw = np2d(xyzw)

    if len(xyzw.shape) > 2:
        raise Exception('xyz_to_cct_search(): Input xyzw.shape must be <= 2 !')

    # get 1960 u,v of test source:
    Yuvt = xyz_to_Yuv(np.squeeze(
        xyzw))  # remove possible 1-dim + convert xyzw to CIE 1976 u',v'
    #axis_of_v3t = len(Yuvt.shape)-1 # axis containing color components
    ut = Yuvt[:, 1, None]  #.take([1],axis = axis_of_v3t) # get CIE 1960 u
    vt = (2 / 3) * Yuvt[:, 2,
                        None]  #.take([2],axis = axis_of_v3t) # get CIE 1960 v

    # Initialize arrays:
    ccts = np.ones((xyzw.shape[0], 1)) * np.nan
    duvs = ccts.copy()

    #calculate preliminary solution(s):
    if (approx_cct_temp == True):
        ccts_est = xyz_to_cct_HA(xyzw)
        procent_estimates = np.array([[3000.0, 100000.0, 0.05],
                                      [100000.0, 200000.0, 0.1],
                                      [200000.0, 300000.0, 0.25],
                                      [300000.0, 400000.0, 0.4],
                                      [400000.0, 600000.0, 0.4],
                                      [600000.0, 800000.0, 0.4],
                                      [800000.0, np.inf, 0.25]])
    else:
        upper_cct = np.array(upper_cct_max)
        lower_cct = np.array(10.0**2)
        cct_scale_fun = lambda x: np.log10(x)
        cct_scale_ifun = lambda x: np.power(10.0, x)
        dT = (cct_scale_fun(upper_cct) - cct_scale_fun(lower_cct)) / 2
        ccttemp = np.array([cct_scale_ifun(cct_scale_fun(lower_cct) + dT)])
        ccts_est = np2d(ccttemp * np.ones((xyzw.shape[0], 1)))
        dT_approx_cct_False = dT.copy()

    # Loop through all ccts:
    for i in range(xyzw.shape[0]):

        #initialize CCT search parameters:
        cct = np.nan
        duv = np.nan
        ccttemp = ccts_est[i].copy()

        # Take care of (-1, NaN)'s from xyz_to_cct_HA signifying (CCT < lower, CCT > upper) bounds:
        approx_cct_temp_temp = approx_cct_temp
        if (approx_cct_temp == True):
            cct_scale_fun = lambda x: x
            cct_scale_ifun = lambda x: x
            if (ccttemp != -1) & (
                    np.isnan(ccttemp) == False
            ):  # within validity range of CCT estimator-function
                for ii in range(procent_estimates.shape[0]):
                    if (ccttemp >=
                        (1.0 - 0.05 *
                         (ii == 0)) * procent_estimates[ii, 0]) & (
                             ccttemp < (1.0 + 0.05 *
                                        (ii == 0)) * procent_estimates[ii, 1]):
                        procent_estimate = procent_estimates[ii, 2]
                        break

                dT = np.multiply(
                    ccttemp, procent_estimate
                )  # determines range around CCTtemp (25% around estimate) or 100 K
            elif (ccttemp == -1) & (np.isnan(ccttemp) == False):
                ccttemp = np.array([procent_estimates[0, 0] / 2])
                procent_estimate = 1  # cover 0 K to min_CCT of estimator
                dT = np.multiply(ccttemp, procent_estimate)
            elif (np.isnan(ccttemp) == True):
                upper_cct = np.array(upper_cct_max)
                lower_cct = np.array(10.0**2)
                cct_scale_fun = lambda x: np.log10(x)
                cct_scale_ifun = lambda x: np.power(10.0, x)
                dT = (cct_scale_fun(upper_cct) - cct_scale_fun(lower_cct)) / 2
                ccttemp = np.array(
                    [cct_scale_ifun(cct_scale_fun(lower_cct) + dT)])
                approx_cct_temp = False
        else:
            dT = dT_approx_cct_False

        nsteps = 3
        signduv = 1.0
        ccttemp = ccttemp[0]
        delta_cct = dT
        while ((delta_cct > accuracy)):  # keep converging on CCT

            #generate range of ccts:
            ccts_i = cct_scale_ifun(
                np.linspace(
                    cct_scale_fun(ccttemp) - dT,
                    cct_scale_fun(ccttemp) + dT, nsteps + 1))

            ccts_i[ccts_i < 100.0] = 100.0  # avoid nan's in calculation

            # Generate BB:
            BB = cri_ref(ccts_i, wl3=wl, ref_type=['BB'], cieobs=cieobs)

            # Calculate xyz:
            xyz = spd_to_xyz(BB, cieobs=cieobs)

            # Convert to CIE 1960 u,v:
            Yuv = xyz_to_Yuv(np.squeeze(
                xyz))  # remove possible 1-dim + convert xyz to CIE 1976 u',v'
            #axis_of_v3 = len(Yuv.shape)-1 # axis containing color components
            u = Yuv[:, 1, None]  # get CIE 1960 u
            v = (2.0 / 3.0) * Yuv[:, 2, None]  # get CIE 1960 v

            # Calculate distance between list of uv's and uv of test source:
            dc = ((ut[i] - u)**2 + (vt[i] - v)**2)**0.5
            if np.isnan(dc.min()) == False:
                #eps = _EPS
                q = dc.argmin()

                if np.size(
                        q
                ) > 1:  #to minimize calculation time: only calculate median when necessary
                    cct = np.median(ccts[q])
                    duv = np.median(dc[q])
                    q = np.median(q)
                    q = int(q)  #must be able to serve as index

                else:
                    cct = ccts_i[q]
                    duv = dc[q]

                if (q == 0):
                    ccttemp = cct_scale_ifun(
                        np.array(cct_scale_fun([cct])) + 2 * dT / nsteps)
                    #dT = 2.0*dT/nsteps
                    continue  # look in higher section of planckian locus

                if (q == np.size(ccts_i)):
                    ccttemp = cct_scale_ifun(
                        np.array(cct_scale_fun([cct])) - 2 * dT / nsteps)
                    #dT = 2.0*dT/nsteps
                    continue  # look in lower section of planckian locus

                if (q > 0) & (q < np.size(ccts_i) - 1):
                    dT = 2 * dT / nsteps
                    # get Duv sign:
                    d_p1m1 = ((u[q + 1] - u[q - 1])**2.0 +
                              (v[q + 1] - v[q - 1])**2.0)**0.5

                    x = (dc[q - 1]**2.0 - dc[q + 1]**2.0 +
                         d_p1m1**2.0) / 2.0 * d_p1m1
                    vBB = v[q - 1] + ((v[q + 1] - v[q - 1]) * (x / d_p1m1))
                    signduv = np.sign(vt[i] - vBB)

                #calculate difference with previous intermediate solution:
                delta_cct = abs(cct - ccttemp)

                ccttemp = np.array(cct)  #%set new intermediate CCT
                approx_cct_temp = approx_cct_temp_temp
            else:
                ccttemp = np.nan
                cct = np.nan
                duv = np.nan

        duvs[i] = signduv * abs(duv)
        ccts[i] = cct

    # Regulate output:
    if (out == 'cct') | (out == 1):
        return np2d(ccts)
    elif (out == 'duv') | (out == -1):
        return np2d(duvs)
    elif (out == 'cct,duv') | (out == 2):
        return np2d(ccts), np2d(duvs)
    elif (out == "[cct,duv]") | (out == -2):
        return np.vstack((ccts, duvs)).T
예제 #13
0
파일: test3.py 프로젝트: uhqinli/luxpy
.. codeauthor:: Kevin A.G. Smet (ksmet1977 at gmail.com)
"""
import luxpy as lx  # package for color science calculations
import matplotlib.pyplot as plt  # package for plotting
import numpy as np  # fundamental package for scientific computing
import timeit  # package for timing functions

cieobs = '1964_10'  # set CIE observer, i.e. cmf set
ccts = [3000, 4000, 4500, 6000]  # define M = 4 CCTs
ref_types = ['BB', 'DL', 'cierf', 'DL']  # define reference illuminant types

# calculate reference illuminants:
REF = lx.cri_ref(ccts, ref_type=ref_types, norm_type='lambda', norm_f=600)

TCS8 = lx._CRI_RFL['cie-13.3-1995']['8']  # 8 TCS from CIE 13.3-1995
xyz_TCS8_REF = lx.spd_to_xyz(REF, cieobs=cieobs, rfl=TCS8, relative=True)
xyz_TCS8_REF_2, xyz_REF_2 = lx.spd_to_xyz(REF,
                                          cieobs=cieobs,
                                          rfl=TCS8,
                                          relative=True,
                                          out=2)

Yuv_REF_2 = lx.xyz_to_Yuv(xyz_REF_2)
axh = lx.plotSL(cspace = 'Yuv', cieobs = cieobs, show = False,\
                 BBL = True, DL = True, diagram_colors = True)

# Step 2:
Y, u, v = np.squeeze(lx.asplit(Yuv_REF_2))  # splits array along last axis

# Step 3:
lx.plot_color_data(u, v, formatstr='go', label='Yuv_REF_2')
예제 #14
0
# check spd_to_xyz:
xyz = lx.spd_to_xyz(spds)
xyz = lx.spd_to_xyz(spds, relative=False)
xyz = lx.spd_to_xyz(spds, rfl=rfls)
xyz, xyzw = lx.spd_to_xyz(spds, rfl=rfls, cieobs='1931_2', out=2)

# 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)