Пример #1
0
def spd_to_tm30(St):
    
    # calculate CIE 1931 2° white point xyz:
    xyzw_cct, _ = spd_to_xyz(St, cieobs = '1931_2', relative = True, out = 2)
    
    # calculate cct, duv:
    cct, duv = xyz_to_cct(xyzw_cct, cieobs = '1931_2', out = 'cct,duv')
    
    # calculate ref illuminant:
    Sr = _cri_ref(cct, mix_range = [4000, 5000], cieobs = '1931_2', wl3 = St[0])

    # calculate CIE 1964 10° sample and white point xyz under test and ref. illuminants:
    xyz, xyzw = spd_to_xyz(np.vstack((St,Sr[1:])), cieobs = '1964_10', 
                           rfl = _TM30_SAMPLE_SET, relative = True, out = 2)
    N = St.shape[0]-1
    
    xyzt, xyzr =  xyz[:,:N,:], xyz[:,N:,:]
    xyzwt, xyzwr =  xyzw[:N,:], xyzw[N:,:]

    
    # calculate CAM02-UCS coordinates 
    # (standard conditions = {'La':100.0,'Yb':20.0,'surround':'avg','D':1.0):
    jabt = _xyz_to_jab_cam02ucs(xyzt, xyzw = xyzwt)
    jabr = _xyz_to_jab_cam02ucs(xyzr, xyzw = xyzwr)
   
    
    # calculate DEi, Rfi:
    DEi = (((jabt-jabr)**2).sum(axis=-1,keepdims=True)**0.5)[...,0]
    Rfi = log_scale(DEi, scale_factor = [6.73])
    
    # calculate Rf
    DEa = DEi.mean(axis = 0,keepdims = True)
    Rf = log_scale(DEa, scale_factor = [6.73])
        
    # calculate hue-bin data:
    hue_bin_data = _get_hue_bin_data(jabt, jabr, start_hue = 0, nhbins = 16)       

    # calculate Rg:
    Rg = _hue_bin_data_to_rg(hue_bin_data)                 
        
    # calculate local color fidelity values, Rfhj,
    # local hue shift, Rhshj and local chroma shifts, Rcshj:
    Rcshj, Rhshj, Rfhj, DEhj = _hue_bin_data_to_Rxhj(hue_bin_data, 
                                                    scale_factor = [6.73])
    
    # Fit ellipse to gamut shape of samples under test source:
    gamut_ellipse_fit = _hue_bin_data_to_ellipsefit(hue_bin_data)
    hue_bin_data['gamut_ellipse_fit'] = gamut_ellipse_fit
    
    # return output dict:
    return {'St' : St, 'Sr' : Sr, 
            'xyzw_cct' : xyzw_cct, 'xyzwt' : xyzwt, 'xyzwr' : xyzwr,
            'xyzt' : xyzt, 'xyzr' : xyzr, 
            'cct': cct.T, 'duv': duv.T,
            'jabt' : jabt, 'jabr' : jabr, 
            'DEi' : DEi, 'DEa' : DEa, 'Rfi' : Rfi, 'Rf' : Rf,
            'hue_bin_data' : hue_bin_data, 'Rg' : Rg,
            'DEhj' : DEhj, 'Rfhj' : Rfhj,
            'Rcshj': Rcshj,'Rhshj':Rhshj,
            'hue_bin_data' : hue_bin_data}
Пример #2
0
def spd_to_mcri(SPD, D=0.9, E=None, Yb=20.0, out='Rm', wl=None):
    """
    Calculates the MCRI or Memory Color Rendition Index, Rm
    
    Args: 
        :SPD: 
            | ndarray with spectral data (can be multiple SPDs, 
              first axis are the wavelengths)
        :D: 
            | 0.9, optional
            | Degree of adaptation.
        :E: 
            | None, optional
            | Illuminance in lux 
            |  (used to calculate La = (Yb/100)*(E/pi) to then calculate D 
            |  following the 'cat02' model). 
            | If None: the degree is determined by :D:
            |  If (:E: is not None) & (:Yb: is None):  :E: is assumed to contain 
               the adapting field luminance La (cd/m²).
        :Yb: 
            | 20.0, optional
            | Luminance factor of background. (used when calculating La from E)
            | If None, E contains La (cd/m²).
        :out: 
            | 'Rm' or str, optional
            | Specifies requested output (e.g. 'Rm,Rmi,cct,duv') 
        :wl: 
            | None, optional
            | Wavelengths (or [start, end, spacing]) to interpolate the SPDs to. 
            | None: default to no interpolation   
    
    Returns:
        :returns: 
            | float or ndarray with MCRI Rm for :out: 'Rm'
            | Other output is also possible by changing the :out: str value.        
          
    References:
        1. `K.A.G. Smet, W.R. Ryckaert, M.R. Pointer, G. Deconinck, P. Hanselaer,(2012)
        “A memory colour quality metric for white light sources,” 
        Energy Build., vol. 49, no. C, pp. 216–225.
        <http://www.sciencedirect.com/science/article/pii/S0378778812000837>`_
    """
    SPD = np2d(SPD)

    if wl is not None:
        SPD = spd(data=SPD, interpolation=_S_INTERP_TYPE, kind='np', wl=wl)

    # unpack metric default values:
    avg, catf, cieobs, cri_specific_pars, cspace, ref_type, rg_pars, sampleset, scale = [
        _MCRI_DEFAULTS[x] for x in sorted(_MCRI_DEFAULTS.keys())
    ]
    similarity_ai = cri_specific_pars['similarity_ai']
    Mxyz2lms = cspace['Mxyz2lms']
    scale_fcn = scale['fcn']
    scale_factor = scale['cfactor']
    sampleset = eval(sampleset)

    # A. calculate xyz:
    xyzti, xyztw = spd_to_xyz(SPD, cieobs=cieobs['xyz'], rfl=sampleset, out=2)
    if 'cct' in out.split(','):
        cct, duv = xyz_to_cct(xyztw,
                              cieobs=cieobs['cct'],
                              out='cct,duv',
                              mode='lut')

    # B. perform chromatic adaptation to adopted whitepoint of ipt color space, i.e. D65:
    if catf is not None:
        Dtype_cat, F, Yb_cat, catmode_cat, cattype_cat, mcat_cat, xyzw_cat = [
            catf[x] for x in sorted(catf.keys())
        ]

        # calculate degree of adaptationn D:
        if E is not None:
            if Yb is not None:
                La = (Yb / 100.0) * (E / np.pi)
            else:
                La = E
            D = cat.get_degree_of_adaptation(Dtype=Dtype_cat, F=F, La=La)
        else:
            Dtype_cat = None  # direct input of D

        if (E is None) and (D is None):
            D = 1.0  # set degree of adaptation to 1 !
        if D > 1.0: D = 1.0
        if D < 0.6: D = 0.6  # put a limit on the lowest D

        # apply cat:
        xyzti = cat.apply(xyzti,
                          cattype=cattype_cat,
                          catmode=catmode_cat,
                          xyzw1=xyztw,
                          xyzw0=None,
                          xyzw2=xyzw_cat,
                          D=D,
                          mcat=[mcat_cat],
                          Dtype=Dtype_cat)
        xyztw = cat.apply(xyztw,
                          cattype=cattype_cat,
                          catmode=catmode_cat,
                          xyzw1=xyztw,
                          xyzw0=None,
                          xyzw2=xyzw_cat,
                          D=D,
                          mcat=[mcat_cat],
                          Dtype=Dtype_cat)

    # C. convert xyz to ipt and split:
    ipt = xyz_to_ipt(
        xyzti, cieobs=cieobs['xyz'], M=Mxyz2lms
    )  #input matrix as published in Smet et al. 2012, Energy and Buildings
    I, P, T = asplit(ipt)

    # D. calculate specific (hue dependent) similarity indicators, Si:
    if len(xyzti.shape) == 3:
        ai = np.expand_dims(similarity_ai, axis=1)
    else:
        ai = similarity_ai
    a1, a2, a3, a4, a5 = asplit(ai)
    mahalanobis_d2 = (a3 * np.power((P - a1), 2.0) + a4 * np.power(
        (T - a2), 2.0) + 2.0 * a5 * (P - a1) * (T - a2))
    if (len(mahalanobis_d2.shape) == 3) & (mahalanobis_d2.shape[-1] == 1):
        mahalanobis_d2 = mahalanobis_d2[:, :, 0].T
    Si = np.exp(-0.5 * mahalanobis_d2)

    # E. calculate general similarity indicator, Sa:
    Sa = avg(Si, axis=0, keepdims=True)

    # F. rescale similarity indicators (Si, Sa) with a 0-1 scale to memory color rendition indices (Rmi, Rm) with a 0 - 100 scale:
    Rmi = scale_fcn(np.log(Si), scale_factor=scale_factor)
    Rm = np2d(scale_fcn(np.log(Sa), scale_factor=scale_factor))

    # G. calculate Rg (polyarea of test / polyarea of memory colours):
    if 'Rg' in out.split(','):
        I = I[
            ...,
            None]  #broadcast_shape(I, target_shape = None,expand_2d_to_3d = 0)
        a1 = a1[:, None] * np.ones(
            I.shape
        )  #broadcast_shape(a1, target_shape = None,expand_2d_to_3d = 0)
        a2 = a2[:, None] * np.ones(
            I.shape
        )  #broadcast_shape(a2, target_shape = None,expand_2d_to_3d = 0)
        a12 = np.concatenate(
            (a1, a2), axis=2
        )  #broadcast_shape(np.hstack((a1,a2)), target_shape = ipt.shape,expand_2d_to_3d = 0)
        ipt_mc = np.concatenate((I, a12), axis=2)
        nhbins, normalize_gamut, normalized_chroma_ref, start_hue = [
            rg_pars[x] for x in sorted(rg_pars.keys())
        ]

        Rg = jab_to_rg(ipt,
                       ipt_mc,
                       ordered_and_sliced=False,
                       nhbins=nhbins,
                       start_hue=start_hue,
                       normalize_gamut=normalize_gamut)

    if (out != 'Rm'):
        return eval(out)
    else:
        return Rm
Пример #3
0
rfls_np_cp = rfls_np.copy()
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='area', norm_f=2)
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='lambda', norm_f=560)
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='max', norm_f=1)
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='pu', norm_f=1)
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='ru', norm_f=1)
rfls_norm = lx.spd_normalize(rfls_np_cp, norm_type='qu', norm_f=1)

# 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, :])
Пример #4
0
 def test_xyz_to_cct(self, getvars):
     # check xyz_to_cct():
     S, spds, rfls, xyz, xyzw = getvars
     cct, duv = lx.xyz_to_cct(xyzw, cieobs='1931_2', out=2)
Пример #5
0
            -0.679 * np.log(cct / 2194)**2) - 0.0172
        D = np.exp(-(3912 * ((1 / cct) -
                             (1 / 6795)))**2)  # degree of adaptation
    else:
        raise Exception('Unrecognized nlocitype')

    if out == 'duv,D':
        return duv, D
    elif out == 'duv':
        return duv
    elif out == 'D':
        return D
    else:
        raise Exception('smet_white_loci(): Requested output unrecognized.')


if __name__ == '__main__':
    ccts = np.array([6605, 6410, 6800])
    BBs = cri_ref(ccts, ref_type=['BB', 'BB', 'BB'])
    xyz10 = spd_to_xyz(BBs, cieobs='1964_10')
    ccts_calc = xyz_to_cct(xyz10, cieobs='1964_10')

    Dn_uw = xyz_to_neutrality_smet2018(xyz10, nlocitype='uw')
    Dn_ca = xyz_to_neutrality_smet2018(xyz10, nlocitype='ca')
    Duv10_uw, Dn_uw2 = cct_to_neutral_loci_smet2018(ccts,
                                                    nlocitype='uw',
                                                    out='duv,D')
    Duv10_ca, Dn_ca2 = cct_to_neutral_loci_smet2018(ccts,
                                                    nlocitype='ca',
                                                    out='duv,D')
        N_components = 4  #if not None, spd model parameters (peakwl, fwhm, ...) are optimized
        component_spds = None
        #component_spds= {}; # if empty dict, then generate using initialize_spd_model_pars and overwrite with function args: peakwl and fwhm. N_components must match length of either peakwl or fwhm
        allow_nongaussianbased_mono_spds = False
        S3, _ = spd_optimizer(target, tar_type = tar_type, cspace_bwtf = {'cieobs' : cieobs, 'mode' : 'search'},\
                              optimizer_type = '3mixer', N_components = N_components,component_spds = component_spds,\
                              allow_nongaussianbased_mono_spds = allow_nongaussianbased_mono_spds,\
                              peakwl = peakwl, fwhm = fwhm, obj_fcn = obj_fcn, obj_tar_vals = obj_tar_vals,\
                              obj_fcn_weights = obj_fcn_weights, decimals = decimals,\
                              use_piecewise_fcn=False, verbosity = 1)
        #bw_order=[-2],bw_order_min=-2,bw_order_max=-1.5) # to test use of pure lorentzian mono spds.

        # Check output agrees with target:
        xyz = spd_to_xyz(S3, relative=False, cieobs=cieobs)
        cct = xyz_to_cct(xyz, cieobs=cieobs, mode='lut')
        Rf = obj_fcn1(S3)
        Rg = obj_fcn2(S3)
        Ra = obj_fcn3(S3)
        print('\nS3: Optimization results:')
        print("S3: Optim / target cct: {:1.1f} K / {:1.1f} K".format(
            cct[0, 0], target))
        print("S3: Optim / target Rf: {:1.3f} / {:1.3f}".format(
            Rf[0, 0], obj_tar_vals[0]))
        print("S3: Optim / target Rg: {:1.3f} / {:1.3f}".format(
            Rg[0, 0], obj_tar_vals[1]))
        print("S3: Optim / target Ra: {:1.3f} / {:1.3f}".format(
            Ra[0, 0], obj_tar_vals[2]))

        #plot spd:
        plt.figure()
Пример #7
0
def spd_to_COI_ASNZS1680(S=None,
                         tf=_COI_CSPACE,
                         cieobs=_COI_CIEOBS,
                         out='COI,cct',
                         extrapolate_rfl=False):
    """
    Calculate the Cyanosis Observation Index (COI) [ASNZS 1680.2.5-1995].
    
    Args:
        :S:
            | ndarray with light source spectrum (first column are wavelengths).
        :tf:
            | _COI_CSPACE, optional
            | Color space in which to calculate the COI.
            | Default is CIELAB.
        :cieobs: 
            | _COI_CIEOBS, optional
            | CMF set to use. 
            | Default is '1931_2'.
        :out: 
            | 'COI,cct' or str, optional
            | Determines output.
        :extrapolate_rfl:
            | False, optional
            | If False: 
            |  limit the wavelength range of the source to that of the standard
            |  reflectance spectra for the 50% and 100% oxygenated blood.
            
    Returns:
        :COI:
            | ndarray with cyanosis indices for input sources.
        :cct:
            | ndarray with correlated color temperatures.
            
    Note:
        Clause 7.2 of the ASNZS 1680.2.5-1995. standard mentions the properties
        demanded of the light source used in region where visual conditions 
        suitable to the detection of cyanosis should be provided:
        
            1. The correlated color temperature (CCT) of the source should be from 
            3300 to 5300 K.
                
            2. The cyanosis observation index should not exceed 3.3

    """

    if S is None:  #use default
        S = _CIE_ILLUMINANTS['F4']

    if extrapolate_rfl == False:  # _COI_RFL do not cover the full 360-830nm range.
        wl_min = _COI_RFL_BLOOD[0].min()
        wl_max = _COI_RFL_BLOOD[0].max()
        S = S[:, np.where((S[0] >= wl_min) & (S[0] <= wl_max))[0]]

    # Calculate reference spd:
    Sr = blackbody(4000, wl3=S[0])  # same wavelength range

    # Calculate xyz of blood under test source and ref. source:
    xyzt, xyzwt = spd_to_xyz(S,
                             rfl=_COI_RFL_BLOOD,
                             relative=True,
                             cieobs=cieobs,
                             out=2)
    xyzr, xyzwr = spd_to_xyz(Sr,
                             rfl=_COI_RFL_BLOOD,
                             relative=True,
                             cieobs=cieobs,
                             out=2)

    # Calculate color difference between blood under test and ref.
    DEi = deltaE.DE_cspace(xyzt, xyzr, xyzwt=xyzwt, xyzwr=xyzwr, tf=tf)

    # Calculate Cyanosis Observation Index:
    COI = np.nanmean(DEi, axis=0)[:, None]

    # Calculate cct, if requested:
    if 'cct' in out.split(','):
        cct, duv = xyz_to_cct(xyzwt, cieobs=cieobs, out=2)

    # manage output:
    if out == 'COI':
        return COI
    elif out == 'COI,cct':
        return COI, cct
    else:
        return eval(out)