Пример #1
0
def test_equality_input():
    # get TC197 data:
    vd_197 = VisualData()
    in_197 = {
        'LMSa': vd_197.absorbance.T.copy(),
        'rmd': vd_197.macula_rel.T.copy(),
        'docul2': vd_197.docul2.T.copy(),
        'docul1_fine': vd_197.docul1_fine.T.copy(),
        'docul2_fine': vd_197.docul2_fine.T.copy(),
        'xyz1931': vd_197.XYZ31.T.copy(),
        'xyz1964': vd_197.XYZ64.T.copy()
    }

    # Get luxpy data:
    data = ic.init(wl=None,
                   dsrc_std='matlab',
                   dsrc_lms_odens='cietc197',
                   lms_to_xyz_method='cietc197',
                   use_sign_figs=True,
                   use_my_round=True,
                   use_chop=True,
                   out=1)
    in_lx = data['odata']
    wls = in_lx['wls']
    in_lx['docul1_fine'] = in_lx['docul'][1, :]
    in_lx['docul2_fine'] = in_lx['docul'][2, :]
    in_lx['xyz1931'] = lx.cie_interp(lx._CMF['1931_2']['bar'], wls, kind='cmf')
    in_lx['xyz1964'] = lx.cie_interp(lx._CMF['1964_10']['bar'],
                                     wls,
                                     kind='cmf')
    return in_197, in_lx
Пример #2
0
def hsi_to_rgb(hsi,
               spd=None,
               cieobs=_CIEOBS,
               srgb=False,
               linear_rgb=False,
               CSF=None,
               wl=[380, 780, 1]):
    """ 
    Convert HyperSpectral Image to rgb.
    
    Args:
        :hsi:
            | ndarray with hyperspectral image [M,N,L]
        :spd:
            | None, optional
            | ndarray with illumination spectrum
        :cieobs:
            | _CIEOBS, optional
            | CMF set to convert spectral data to xyz tristimulus values.
        :srgb:
            | False, optional
            | If False: Use xyz_to_srgb(spd_to_xyz(...)) to convert to srgb values
            | If True: use camera sensitivity functions.
        :linear_rgb:
            | False, optional
            | If False: use gamma = 2.4 in xyz_to_srgb, if False: use gamma = 1.
        :CSF:
            | None, optional
            | ndarray with camera sensitivity functions 
            | If None: use Nikon D700
        :wl:
            | [380,780,1], optional
            | Wavelength range and spacing or ndarray with wavelengths of HSI image.
    
    Returns:
        :rgb:
            | ndarray with rgb image [M,N,3]
    """
    if spd is None:
        spd = _CIE_E.copy()
    wlr = getwlr(wl)
    spd = cie_interp(spd, wl, kind='linear')

    hsi_2d = np.reshape(hsi, (hsi.shape[0] * hsi.shape[1], hsi.shape[2]))

    if srgb:
        xyz = spd_to_xyz(spd,
                         cieobs=cieobs,
                         relative=True,
                         rfl=np.vstack((wlr, hsi_2d)))
        gamma = 1 if linear_rgb else 2.4
        rgb = xyz_to_srgb(xyz, gamma=gamma) / 255
    else:
        if CSF is None: CSF = _CSF_NIKON_D700
        rgb = rfl_to_rgb(hsi_2d, spd=spd, CSF=CSF, wl=wl)
    return np.reshape(rgb, (hsi.shape[0], hsi.shape[1], 3))
Пример #3
0
 def spdBB(CCT=5500, wl=[400, 700, 5], Lw=25000, cieobs='1964_10'):
     wl = getwlr(wl)
     dl = wl[1] - wl[0]
     spd = 2 * np.pi * 6.626068E-34 * (299792458**2) / (
         (wl * 0.000000001)**
         5) / (np.exp(6.626068E-34 * 299792458 /
                      (wl * 0.000000001) / 1.3806503E-23 / CCT) - 1)
     spd = Lw * spd / (dl * 683 * (spd * cie_interp(
         _CMF[cieobs]['bar'].copy(), wl, kind='cmf')[2, :]).sum())
     return np.vstack((wl, spd))
Пример #4
0
def _convert_to_wlr(entries=rgb2spec_entries, wlr=_WL3):
    wlr = getwlr(wlr)
    for entry in entries:
        if entry != 'wlr':
            for (k, v) in entries[entry].items():
                if k != 'scalefactor':
                    entries[entry][k] = cie_interp(_addwlr(entries[entry][k]),
                                                   wlr,
                                                   kind=entry)[1]
    entries['wlr'] = wlr
    return entries
Пример #5
0
def rfl_to_rgb(rfl, spd=None, CSF=None, wl=None, normalize_to_white=True):
    """ 
    Convert spectral reflectance functions (illuminated by spd) to Camera Sensitivity Functions.
    
    Args:
        :rfl:
            | ndarray with spectral reflectance functions (1st row is wavelengths if wl is None).
        :spd:
            | None, optional
            | ndarray with illumination spectrum
        :CSF:
            | None, optional
            | ndarray with camera sensitivity functions 
            | If None: use Nikon D700
        :normalize_to_white:
            | True, optional
            | If True: white-balance output rgb to a perfect white diffuser.
    
    Returns:
        :rgb:
            | ndarray with rgb values for each spectral reflectance functions
    """
    rfl_cp = rfl.copy()
    if (wl is None):
        wl = rfl_cp[0]
        rfl_cp = rfl_cp[1:]
    wlr = getwlr(wl)
    if spd is not None:
        spd = cie_interp(spd, wlr, kind='linear')[1:]
    else:
        spd = np.ones_like(wlr)
    if CSF is None: CSF = _CSF_NIKON_D700
    CSF = cie_interp(CSF, wlr, kind='linear')
    CSF[1:] = CSF[1:] * spd
    rgb = rfl_cp @ CSF[1:].T
    if normalize_to_white:
        white = np.ones_like(spd)
        rgbw = white @ CSF[1:].T
        rgb = rgb / rgbw.max(axis=0, keepdims=True)

    return rgb
Пример #6
0
def interpolate_efficiency_functions(wl, cs_cl_lrs):
    """
    Interpolate all spectral data in dict cs_cl_lrs to new wavelength range.
    """
    
    for key in cs_cl_lrs:
        if key[-1] == 'l': #signifies l for spectral data
            temp = np.vstack((cs_cl_lrs['WL'],cs_cl_lrs[key])) # construct [wl,S] data
            cs_cl_lrs[key] = cie_interp(temp,wl, kind = 'cmf', negative_values_allowed=True)[1:] # interpolate and store in dict
    cs_cl_lrs['WL'] = wl # store new wavelength range
    
    return  cs_cl_lrs
Пример #7
0
 def cie_interp(self,
                wl_new,
                kind='auto',
                negative_values_allowed=False,
                extrap_values=None):
     """
     Interpolate / extrapolate spectral data following standard CIE15-2018.
     
     | The interpolation type depends on the spectrum type defined in :kind:. 
     | Extrapolation is always done by replicate the closest known values.
     
     Args:
         :wl_new: 
             | ndarray with new wavelengths
         :kind:
             | 'auto', optional
             | If :kind: is None, return original data.
             | If :kind: is a spectrum type (see _INTERP_TYPES), the correct 
             |     interpolation type if automatically chosen.
             | If kind = 'auto': use self.dtype
             | Or :kind: can be any interpolation type supported 
               by scipy.interpolate.interp1d
         :negative_values_allowed:
             | False, optional
             | If False: negative values are clipped to zero
         :extrap_values:
             | None, optional
             | float or list or ndarray with values to extrapolate
             | If None: use CIE recommended 'closest value' approach.
     
     Returns:
         :returns:
             | ndarray of interpolated spectral data.
             | (.shape = (number of spectra+1, number of wavelength in wl_new))
     """
     if (kind == 'auto') & (self.dtype is not None):
         kind = self.dtype
     spd = cie_interp(self.get_(),
                      wl_new,
                      kind=kind,
                      negative_values_allowed=negative_values_allowed,
                      extrap_values=extrap_values)
     self.wl = spd[0]
     self.value = spd[1:]
     return self
Пример #8
0

if __name__ == '__main__':

    #--------------------------------------------------------------------------
    # Code test
    #--------------------------------------------------------------------------

    import luxpy as lx
    from luxpy import np

    # Prepare some illuminant data:
    C = _CIE_ILLUMINANTS['C'].copy()
    Ill1 = C
    Ill2 = np.vstack(
        (C, lx.cie_interp(_CIE_ILLUMINANTS['D65'], C[0],
                          kind='spd')[1:], C[1:, :] * 2, C[1:, :] * 3))

    # Prepare some sample data:
    rflM = lx._MUNSELL['R'].copy()
    rflM = lx.cie_interp(rflM, C[0], kind='rfl')

    # Setup some model parameters:
    cieobs = '2006_10'
    Lw = 400

    # Create Lw normalized data:
    # Normalize to Lw:
    def normalize_to_Lw(Ill, Lw, cieobs, rflM):
        xyzw = lx.spd_to_xyz(Ill, cieobs=cieobs, relative=False)
        for i in range(Ill.shape[0] - 1):
            Ill[i + 1] = Lw * Ill[i + 1] / xyzw[i, 1]
Пример #9
0
    Yuv_t = Yuv_t[:-1, ...]
    Yuv_r = Yuv_r[:-1, ...]

    # Define preferred chromaticity shifts for 8 CIE CRI samples:
    # (*5, because Thorton uses full preferred shifts unlike Judd's Flattery Index)
    uv_shifts = np.array([[0.0020, 0.0008], [0.0000, 0.0000], [
        -0.0020, 0.0008
    ], [-0.0020, 0.0010], [-0.0020, -0.0004], [-0.0012, -0.0020],
                          [0.0008, -0.0020], [0.0020, -0.0010]]) * 5

    # Calculate chromaticity difference between test and shifted ref coordinates:
    DE = 800 * ((
        (Yuv_t[..., 1:] -
         (Yuv_r[..., 1:] + uv_shifts[:, None, :]))**2).sum(axis=-1))**0.5

    # Calculate CPI:
    CPI = 156 - 7.317 * DE.mean(
        axis=0)  # in Thornton 1974 we find 7.18, but then CPI(D65)!=100
    return CPI


if __name__ == '__main__':
    import luxpy as lx
    F4 = lx.cie_interp(lx._CIE_F4, wl_new=lx.getwlr([360, 830, 1]), kind='spd')
    D65 = lx.cie_interp(lx._CIE_D65,
                        wl_new=lx.getwlr([360, 830, 1]),
                        kind='spd')
    spds = np.vstack((F4, D65[1:, :]))

    cpi1 = spd_to_thornton_cpi(F4)
    cpi2 = spd_to_thornton_cpi(spds)
Пример #10
0
def xyz_to_rfl(xyz, CSF = None, rfl = None, out = 'rfl_est', \
                 refspd = None, D = None, cieobs = _CIEOBS, \
                 cspace = 'xyz', cspace_tf = {},\
                 interp_type = 'nd', k_neighbours = 4, verbosity = 0):
    """
    Approximate spectral reflectance of xyz values based on nd-dimensional linear interpolation 
    or k nearest neighbour interpolation of samples from a standard reflectance set.
    
    Args:
        :xyz: 
            | ndarray with xyz values of target points.
        :CSF:
            | None, optional
            | RGB camera response functions.
            | If None: input :xyz: contains raw rgb (float) values. Override :cspace:
            | argument and perform estimation directly in raw rgb space!!!
        :rfl: 
            | ndarray, optional
            | Reflectance set for color coordinate to rfl mapping.
        :out: 
            | 'rfl_est' or str, optional
        :refspd: 
            | None, optional
            | Refer ence spectrum for color coordinate to rfl mapping.
            | None defaults to D65.
        :cieobs:
            | _CIEOBS, optional
            | CMF set used for calculation of xyz from spectral data.
        :cspace:
            | 'xyz',  optional
            | Color space for color coordinate to rfl mapping.
            | Tip: Use linear space (e.g. 'xyz', 'Yuv',...) for (interp_type == 'nd'),
            |      and perceptually uniform space (e.g. 'ipt') for (interp_type == 'nearest')
        :cspace_tf:
            | {}, optional
            | Dict with parameters for xyz_to_cspace and cspace_to_xyz transform.
        :interp_type:
            | 'nd', optional
            | Options:
            | - 'nd': perform n-dimensional linear interpolation using Delaunay triangulation.
            | - 'nearest': perform nearest neighbour interpolation. 
        :k_neighbours:
            | 4 or int, optional
            | Number of nearest neighbours for reflectance spectrum interpolation.
            | Neighbours are found using scipy.spatial.cKDTree
        :verbosity:
            | 0, optional
            | If > 0: make a plot of the color coordinates of original and 
            | rendered image pixels.

    Returns:
        :returns: 
            | :rfl_est:
            | ndarrays with estimated reflectance spectra.
    """

    # get rfl set:
    if rfl is None:  # use IESTM30['4880'] set
        rfl = _CRI_RFL['ies-tm30']['4880']['5nm']

    wlr = rfl[0]

    # get Ref spd:
    if refspd is None:
        refspd = _CIE_ILLUMINANTS['D65'].copy()
    refspd = cie_interp(
        refspd, wlr,
        kind='linear')  # force spd to same wavelength range as rfl

    # Calculate rgb values of standard rfl set under refspd:
    if CSF is None:
        # Calculate lab coordinates:
        xyz_rr, xyz_wr = spd_to_xyz(refspd,
                                    relative=True,
                                    rfl=rfl,
                                    cieobs=cieobs,
                                    out=2)
        cspace_tf_copy = cspace_tf.copy()
        cspace_tf_copy[
            'xyzw'] = xyz_wr  # put correct white point in param. dict
        lab_rr = colortf(xyz_rr,
                         tf=cspace,
                         fwtf=cspace_tf_copy,
                         bwtf=cspace_tf_copy)[:, 0, :]
    else:
        # Calculate rgb coordinates from camera sensitivity functions
        rgb_rr = rfl_to_rgb(rfl, spd=refspd, CSF=CSF, wl=None)
        lab_rr = rgb_rr
        xyz = xyz
        lab_rr = np.round(lab_rr, _ROUNDING)  # speed up search

    # Convert xyz to lab-type values under refspd:
    if CSF is None:
        lab = colortf(xyz, tf=cspace, fwtf=cspace_tf_copy, bwtf=cspace_tf_copy)
    else:
        lab = xyz  # xyz contained rgb values !!!
        rgb = xyz
        lab = np.round(lab, _ROUNDING)  # speed up search

    if interp_type == 'nearest':
        # Find rfl (cfr. lab_rr) from rfl set that results in 'near' metameric
        # color coordinates for each value in lab_ur (i.e. smallest DE):
        # Construct cKDTree:
        tree = sp.spatial.cKDTree(lab_rr, copy_data=True)

        # Interpolate rfls using k nearest neightbours and inverse distance weigthing:
        d, inds = tree.query(lab, k=k_neighbours)
        if k_neighbours > 1:
            d += _EPS
            w = (1.0 / d**2)[:, :, None]  # inverse distance weigthing
            rfl_est = np.sum(w * rfl[inds + 1, :], axis=1) / np.sum(w, axis=1)
        else:
            rfl_est = rfl[inds + 1, :].copy()
    elif interp_type == 'nd':

        rfl_est = math.ndinterp1_scipy(lab_rr, rfl[1:], lab)

        _isnan = np.isnan(rfl_est[:, 0])

        if (
                _isnan.any()
        ):  #do nearest neigbour method for those that fail using Delaunay (i.e. ndinterp1_scipy)

            # Find rfl (cfr. lab_rr) from rfl set that results in 'near' metameric
            # color coordinates for each value in lab_ur (i.e. smallest DE):
            # Construct cKDTree:
            tree = sp.spatial.cKDTree(lab_rr, copy_data=True)

            # Interpolate rfls using k nearest neightbours and inverse distance weigthing:
            d, inds = tree.query(lab[_isnan, ...], k=k_neighbours)

            if k_neighbours > 1:
                d += _EPS
                w = (1.0 / d**2)[:, :, None]  # inverse distance weigthing
                rfl_est_isnan = np.sum(w * rfl[inds + 1, :], axis=1) / np.sum(
                    w, axis=1)
            else:
                rfl_est_isnan = rfl[inds + 1, :].copy()
            rfl_est[_isnan, :] = rfl_est_isnan

    else:
        raise Exception('xyz_to_rfl(): unsupported interp_type!')

    rfl_est[
        rfl_est <
        0] = 0  #can occur for points outside convexhull of standard rfl set.

    rfl_est = np.vstack((rfl[0], rfl_est))

    if ((verbosity > 0) | ('xyz_est' in out.split(',')) |
        ('lab_est' in out.split(',')) | ('DEi_ab' in out.split(',')) |
        ('DEa_ab' in out.split(','))) & (CSF is None):
        xyz_est, _ = spd_to_xyz(refspd,
                                rfl=rfl_est,
                                relative=True,
                                cieobs=cieobs,
                                out=2)
        cspace_tf_copy = cspace_tf.copy()
        cspace_tf_copy[
            'xyzw'] = xyz_wr  # put correct white point in param. dict
        lab_est = colortf(xyz_est, tf=cspace, fwtf=cspace_tf_copy)[:, 0, :]
        DEi_ab = np.sqrt(((lab_est[:, 1:3] - lab[:, 1:3])**2).sum(axis=1))
        DEa_ab = DEi_ab.mean()
    elif ((verbosity > 0) | ('xyz_est' in out.split(',')) |
          ('rgb_est' in out.split(',')) | ('DEi_rgb' in out.split(',')) |
          ('DEa_rgb' in out.split(','))) & (CSF is not None):
        rgb_est = rfl_to_rgb(rfl_est[1:], spd=refspd, CSF=CSF, wl=wlr)
        xyz_est = rgb_est
        DEi_rgb = np.sqrt(((rgb_est - rgb)**2).sum(axis=1))
        DEa_rgb = DEi_rgb.mean()

    if verbosity > 0:
        if CSF is None:
            ax = plot_color_data(lab[...,1], lab[...,2], z = lab[...,0], \
                            show = False, cieobs = cieobs, cspace = cspace, \
                            formatstr = 'ro', label = 'Original')
            plot_color_data(lab_est[...,1], lab_est[...,2], z = lab_est[...,0], \
                            show = True, axh = ax, cieobs = cieobs, cspace = cspace, \
                            formatstr = 'bd', label = 'Rendered')
        else:
            n = 100  #min(rfl.shape[0]-1,rfl_est.shape[0]-1)
            s = np.random.permutation(rfl.shape[0] -
                                      1)[:min(n, rfl.shape[0] - 1)]
            st = np.random.permutation(rfl_est.shape[0] -
                                       1)[:min(n, rfl_est.shape[0] - 1)]
            fig = plt.figure()
            ax = np.zeros((3, ), dtype=np.object)
            ax[0] = fig.add_subplot(131)
            ax[1] = fig.add_subplot(132)
            ax[2] = fig.add_subplot(133, projection='3d')
            ax[0].plot(rfl[0], rfl[1:][s].T, linestyle='-')
            ax[0].set_title('Original RFL set (random selection of all)')
            ax[0].set_ylim([0, 1])
            ax[1].plot(rfl_est[0], rfl_est[1:][st].T, linestyle='--')
            ax[0].set_title('Estimated RFL set (random selection of targets)')
            ax[1].set_ylim([0, 1])
            ax[2].plot(rgb[st, 0],
                       rgb[st, 1],
                       rgb[st, 2],
                       'ro',
                       label='Original')
            ax[2].plot(rgb_est[st, 0],
                       rgb_est[st, 1],
                       rgb_est[st, 2],
                       'bd',
                       label='Rendered')
            ax[2].legend()
    if out == 'rfl_est':
        return rfl_est
    elif out == 'rfl_est,xyz_est':
        return rfl_est, xyz_est
    else:
        return eval(out)
Пример #11
0
def render_image(img = None, spd = None, rfl = None, out = 'img_hyp', \
                 refspd = None, D = None, cieobs = _CIEOBS, \
                 cspace = 'xyz', cspace_tf = {}, CSF = None,\
                 interp_type = 'nd', k_neighbours = 4, show = True,
                 verbosity = 0, show_ref_img = True,\
                 stack_test_ref = 12,\
                 write_to_file = None):
    """
    Render image under specified light source spd.
    
    Args:
        :img: 
            | None or str or ndarray with float (max = 1) rgb image.
            | None load a default image.
        :spd: 
            | ndarray, optional
            | Light source spectrum for rendering
            | If None: use CIE illuminant F4
        :rfl: 
            | ndarray, optional
            | Reflectance set for color coordinate to rfl mapping.
        :out: 
            | 'img_hyp' or str, optional
            |  (other option: 'img_ren': rendered image under :spd:)
        :refspd:
            | None, optional
            | Reference spectrum for color coordinate to rfl mapping.
            | None defaults to D65 (srgb has a D65 white point)
        :D: 
            | None, optional
            | Degree of (von Kries) adaptation from spd to refspd. 
        :cieobs:
            | _CIEOBS, optional
            | CMF set for calculation of xyz from spectral data.
        :cspace:
            | 'xyz',  optional
            | Color space for color coordinate to rfl mapping.
            | Tip: Use linear space (e.g. 'xyz', 'Yuv',...) for (interp_type == 'nd'),
            |      and perceptually uniform space (e.g. 'ipt') for (interp_type == 'nearest')
        :cspace_tf:
            | {}, optional
            | Dict with parameters for xyz_to_cspace and cspace_to_xyz transform.
        :CSF:
            | None, optional
            | RGB camera response functions.
            | If None: input :xyz: contains raw rgb values. Override :cspace:
            | argument and perform estimation directly in raw rgb space!!!
        :interp_type:
            | 'nd', optional
            | Options:
            | - 'nd': perform n-dimensional linear interpolation using Delaunay triangulation.
            | - 'nearest': perform nearest neighbour interpolation. 
        :k_neighbours:
            | 4 or int, optional
            | Number of nearest neighbours for reflectance spectrum interpolation.
            | Neighbours are found using scipy.spatial.cKDTree
        :show: 
            | True, optional
            |  Show images.
        :verbosity:
            | 0, optional
            | If > 0: make a plot of the color coordinates of original and 
              rendered image pixels.
        :show_ref_img:
            | True, optional
            | True: shows rendered image under reference spd. False: shows
            |  original image.
        :write_to_file:
            | None, optional
            | None: do nothing, else: write to filename(+path) in :write_to_file:
        :stack_test_ref: 
            | 12, optional
            |   - 12: left (test), right (ref) format for show and imwrite
            |   - 21: top (test), bottom (ref)
            |   - 1: only show/write test
            |   - 2: only show/write ref
            |   - 0: show both, write test

    Returns:
        :returns: 
            | img_hyp, img_ren, 
            | ndarrays with float hyperspectral image and rendered images 
    """

    # Get image:
    #imread = lambda x: plt.imread(x) #matplotlib.pyplot

    if img is not None:
        if isinstance(img, str):
            img = plt.imread(img)  # use matplotlib.pyplot's imread
    else:
        img = plt.imread(_HYPSPCIM_DEFAULT_IMAGE)
    if isinstance(img, np.uint8):
        img = img / 255
    elif isinstance(img, np.uint16):
        img = img / (2**16 - 1)

    # Convert to 2D format:
    rgb = img.reshape(img.shape[0] * img.shape[1], 3)  # *1.0: make float
    rgb[rgb == 0] = _EPS  # avoid division by zero for pure blacks.

    # Get unique rgb values and positions:
    rgb_u, rgb_indices = np.unique(rgb, return_inverse=True, axis=0)

    # get rfl set:
    if rfl is None:  # use IESTM30['4880'] set
        rfl = _CRI_RFL['ies-tm30']['4880']['5nm']
    wlr = rfl[
        0]  # spectral reflectance set determines wavelength range for estimation (xyz_to_rfl())

    # get Ref spd:
    if refspd is None:
        refspd = _CIE_ILLUMINANTS['D65'].copy()
    refspd = cie_interp(
        refspd, wlr,
        kind='linear')  # force spd to same wavelength range as rfl

    # Convert rgb_u to xyz and lab-type values under assumed refspd:
    if CSF is None:
        xyz_wr = spd_to_xyz(refspd, cieobs=cieobs, relative=True)
        xyz_ur = colortf(rgb_u * 255, tf='srgb>xyz')
    else:
        xyz_ur = rgb_u  # for input in xyz_to_rfl (when CSF is not None: this functions assumes input is indeed rgb !!!)

    # Estimate rfl's for xyz_ur:
    rfl_est, xyzri = xyz_to_rfl(xyz_ur, rfl = rfl, out = 'rfl_est,xyz_est', \
                 refspd = refspd, D = D, cieobs = cieobs, \
                 cspace = cspace, cspace_tf = cspace_tf, CSF = CSF,\
                 interp_type = interp_type, k_neighbours = k_neighbours,
                 verbosity = verbosity)

    # Get default test spd if none supplied:
    if spd is None:
        spd = _CIE_ILLUMINANTS['F4']

    if CSF is None:
        # calculate xyz values under test spd:
        xyzti, xyztw = spd_to_xyz(spd, rfl=rfl_est, cieobs=cieobs, out=2)

        # Chromatic adaptation from test spd to refspd:
        if D is not None:
            xyzti = cat.apply(xyzti, xyzw1=xyztw, xyzw2=xyz_wr, D=D)

        # Convert xyzti under test spd to srgb:
        rgbti = colortf(xyzti, tf='srgb') / 255
    else:
        # Calculate rgb coordinates from camera sensitivity functions under spd:
        rgbti = rfl_to_rgb(rfl_est, spd=spd, CSF=CSF, wl=None)

        # Chromatic adaptation from test spd to refspd:
        if D is not None:
            white = np.ones_like(spd)
            white[0] = spd[0]
            rgbwr = rfl_to_rgb(white, spd=refspd, CSF=CSF, wl=None)
            rgbwt = rfl_to_rgb(white, spd=spd, CSF=CSF, wl=None)
            rgbti = cat.apply_vonkries2(rgbti,
                                        rgbwt,
                                        rgbwr,
                                        xyzw0=np.array([[1.0, 1.0, 1.0]]),
                                        in_='rgb',
                                        out_='rgb',
                                        D=1)

    # Reconstruct original locations for rendered image rgbs:
    img_ren = rgbti[rgb_indices]
    img_ren.shape = img.shape  # reshape back to 3D size of original
    img_ren = img_ren

    # For output:
    if show_ref_img == True:
        rgb_ref = colortf(xyzri, tf='srgb') / 255 if (
            CSF is None
        ) else xyzri  # if CSF not None: xyzri contains rgbri !!!
        img_ref = rgb_ref[rgb_indices]
        img_ref.shape = img.shape  # reshape back to 3D size of original
        img_str = 'Rendered (under ref. spd)'
        img = img_ref
    else:
        img_str = 'Original'
        img = img

    if (stack_test_ref > 0) | show == True:
        if stack_test_ref == 21:
            img_original_rendered = np.vstack(
                (img_ren, np.ones((4, img.shape[1], 3)), img))
            img_original_rendered_str = 'Rendered (under test spd)\n ' + img_str
        elif stack_test_ref == 12:
            img_original_rendered = np.hstack(
                (img_ren, np.ones((img.shape[0], 4, 3)), img))
            img_original_rendered_str = 'Rendered (under test spd) | ' + img_str
        elif stack_test_ref == 1:
            img_original_rendered = img_ren
            img_original_rendered_str = 'Rendered (under test spd)'
        elif stack_test_ref == 2:
            img_original_rendered = img
            img_original_rendered_str = img_str
        elif stack_test_ref == 0:
            img_original_rendered = img_ren
            img_original_rendered_str = 'Rendered (under test spd)'

    if write_to_file is not None:
        # Convert from RGB to BGR formatand write:
        #print('Writing rendering results to image file: {}'.format(write_to_file))
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            imsave(write_to_file, img_original_rendered)

    if show == True:
        # show images using pyplot.show():
        plt.figure()

        plt.imshow(img_original_rendered)
        plt.title(img_original_rendered_str)
        plt.gca().get_xaxis().set_ticklabels([])
        plt.gca().get_yaxis().set_ticklabels([])

        if stack_test_ref == 0:
            plt.figure()
            plt.imshow(img)
            plt.title(img_str)
            plt.axis('off')

    if 'img_hyp' in out.split(','):
        # Create hyper_spectral image:
        rfl_image_2D = rfl_est[
            rgb_indices +
            1, :]  # create array with all rfls required for each pixel
        img_hyp = rfl_image_2D.reshape(img.shape[0], img.shape[1],
                                       rfl_image_2D.shape[1])

    # Setup output:
    if out == 'img_hyp':
        return img_hyp
    elif out == 'img_ren':
        return img_ren
    else:
        return eval(out)
Пример #12
0
    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


if __name__ == '__main__':
    import luxpy as lx
    F6 = lx.cie_interp(lx._CIE_ILLUMINANTS['F6'],
                       wl_new=lx.getwlr([360, 830, 1]),
                       kind='spd')
    F4 = lx.cie_interp(lx._CIE_F4, wl_new=lx.getwlr([360, 830, 1]), kind='spd')
    D65 = lx.cie_interp(lx._CIE_D65,
                        wl_new=lx.getwlr([360, 830, 1]),
                        kind='spd')
    spds = np.vstack((F6, F4[1:, :], D65[1:, :]))

    fci1a = spd_to_fci(F6, True)
    print(fci1a)
    fci1b = spd_to_fci(F6, False)
    print(fci1b)
    fci2 = spd_to_fci(spds)
    print(fci2)
Пример #13
0
    Wrapper function for cam15u inverse mode with 'Q,aW,bW' input.
    
    | For help on parameter details: ?luxpy.cam.cam15u
    """
    return cam15u(qab,
                  fov=fov,
                  direction='inverse',
                  inputtype='xyz',
                  outin='Q,aW,bW',
                  parameters=parameters)


#------------------------------------------------------------------------------
if __name__ == '__main__':
    C = _CIE_ILLUMINANTS['C'].copy()
    C = np.vstack((C, cie_interp(_CIE_ILLUMINANTS['D65'], C[0],
                                 kind='spd')[1:]))
    M = _MUNSELL.copy()
    rflM = M['R']
    cieobs = '2006_10'

    # Normalize to Lw:
    Lw = 100
    xyzw2 = spd_to_xyz(C, cieobs=cieobs, relative=False)
    for i in range(C.shape[0] - 1):
        C[i + 1] = Lw * C[i + 1] / xyzw2[i, 1]

    xyz, xyzw = spd_to_xyz(C, cieobs=cieobs, relative=True, rfl=rflM, out=2)
    qab = xyz_to_qabW_cam15u(xyzw, fov=10.0)
    qab2 = cam15u(C,
                  fov=10.0,
                  direction='forward',
Пример #14
0
def spd_to_CS_CLa_lrc(El = None, E = None, \
                          sum_sources = False, interpolate_sources = True):
    """
    Calculate Circadian Stimulus (CS) and Circadian Light [LRC: Rea et al 2012].
    
    
    Args:
        :El:
            | ndarray, optional
            | Defaults to D65
            | light source spectral irradiance distribution
        :E: 
            | None, float or ndarray, optional
            | Illuminance of light sources.
            | If None: El is used as is, otherwise El is renormalized to have
              an illuminance equal to E.
        :sum_sources:
            | False, optional
            |   - False: calculate CS and CLa for all sources in El array.
            |   - True: sum sources in El to a single source and perform calc.
        :interpolate_sources:
            | True, optional
            |  - True: El is interpolated to wavelength range of efficiency 
            |          functions (as in LRC calculator). 
            |  - False: interpolate efficiency functions to source range. 
            |           Source interpolation is not recommended due to possible
            |           errors for peaky spectra. 
            |           (see CIE15-2004, "Colorimetry").
            
    Returns:
        :CS:
            | ndarray with Circadian stimulus values
        :CLa:
            | ndarray with Circadian Light values
            
    Notes:
        1. The original 2012 (E.q. 1) had set the peak wavelength of the 
        melanopsin at 480 nm. Rea et al. later published a corrigendum with 
        updated model parameters for k, a_{b-y} and a_rod. The comparison table
        between showing values calculated for a number of sources with the old
        and updated parameters were very close (~1 unit voor CLa). 
        
        2. In that corrrection paper they did not mention a change in the
        factor (1622) that multiplies the (sum of) the integral(s) in Eq. 1. 
        HOWEVER, the excel calculator released in 2017 and the online 
        calculator show that factor to have a value of 1547.9. The change in
        values due to the new factor is much larger than their the updated 
        mentioned in note 1!
        
        3. For reasons of consistency the calculator uses the latest model 
        parameters, as could be read from the excel calculator. They values 
        adopted are: multiplier 1547.9, k = 0.2616, a_{b-y} = 0.7 and 
        a_rod = 3.3. 
        
        4. The parameter values to convert CLa to CS were also taken from the 
        2017 excel calculator.
        
    References:
        
        1. `LRC Online Circadian stimulus calculator <http://www.lrc.rpi.edu/cscalculator/>`_
        
        2. `LRC Excel based Circadian stimulus calculator. <http://www.lrc.rpi.edu/resources/CSCalculator_2017_10_03_Mac.xlsm>`_
        
        3. `Rea MS, Figueiro MG, Bierman A, and Hamner R (2012). 
        Modelling the spectral sensitivity of the human circadian system. 
        Light. Res. Technol. 44, 386–396.  
        <https://doi.org/10.1177/1477153511430474>`_
            
        4. `Rea MS, Figueiro MG, Bierman A, and Hamner R (2012). 
        Erratum: Modeling the spectral sensitivity of the human circadian system 
        (Lighting Research and Technology (2012) 44:4 (386-396)). 
        Light. Res. Technol. 44, 516.
        <https://doi.org/10.1177/1477153512467607>`_
    """
    # Create copy of dict with model parameters and spectral data:
    cs_cl_lrs = _LRC_CLA_CS_CONST['CLa'].copy()

    # Interpolate efficiency functions to light source wl-range:
    if interpolate_sources is False:
        cs_cl_lrs = interpolate_efficiency_functions(El[0], cs_cl_lrs)
    else:
        El = cie_interp(El, cs_cl_lrs['WL'], kind='spd')

    # Get wavelength spacing:
    dl = getwld(El[0])

    # Separate wavelengths and data:
    wl = El[0]
    Elv = El[1:].copy()

    # define integral function:
    integral = lambda x: integrate.trapz(x, x=wl, axis=-1)
    #integral = lambda x: np.sum(x,  axis = -1)

    # Rescale El to E (if not None):
    if E is not None:

        # Calculate current E value of El:
        E_cv = np.atleast_2d(683.002 *
                             integral(cs_cl_lrs['Vphotl'] * Elv * dl))

        # Rescale El to supplied E:
        Elv = (E / E_cv).T * Elv

    # Sum all sources in array if requested:
    if sum_sources == True:
        Elv = Elv.sum(axis=0, keepdims=True)

    # Calculate Circadian light using model param. and spectral data:
    CLa = fCLa(wl, Elv, integral, **cs_cl_lrs)

    # Calculate Circadian stimulus:
    CS = 0.7 * (1 - (1 / (1 + (CLa / 355.7)**1.1026)))

    return CS, CLa
Пример #15
0
                    relative=True,
                    inputtype='xyz',
                    direction='forward',
                    parameters=parameters,
                    cieobs='2006_10',
                    match_to_conversionmatrix_to_cieobs=True)


#------------------------------------------------------------------------------
if __name__ == '__main__0':
    test_model()

if __name__ == '__main__':

    C = _CIE_ILLUMINANTS['C'].copy()
    C = np.vstack((C, cie_interp(_CIE_ILLUMINANTS['D65'], C[0],
                                 kind='spd')[1:], C[1:, :] * 2, C[1:, :] * 3))
    M = _MUNSELL.copy()
    rflM = M['R']
    rflM = cie_interp(rflM, C[0], kind='rfl')
    cieobs = '2006_10'
    Lw = 400
    Yb = 20

    # Normalize to Lw:
    xyzw2 = spd_to_xyz(C, cieobs=cieobs, relative=False)
    for i in range(C.shape[0] - 1):
        C[i + 1] = Lw * C[i + 1] / xyzw2[i, 1]
    CM = []
    for i in range(C.shape[0] - 1):
        CM.append(np.vstack((C[0], C[i + 1] * rflM[1:, :])))
    CM = np.transpose(np.array(CM), (1, 0, 2))
Пример #16
0
    """
    return cam18sl(xyz, datab = xyzb, Lb = Lb, fov = fov, direction = 'forward', inputtype = 'xyz', outin = 'Q,aS,bS', parameters = parameters)
                
def qabS_cam18sl_to_xyz(qab, xyzb = None, Lb = [100], fov = 10.0, parameters = None, **kwargs):
    """
    Wrapper function for cam18sl inverse mode with 'Q,aS,bS' input.
    
    | For help on parameter details: ?luxpy.cam.cam18sl
    """
    return cam18sl(qab, datab = xyzb, Lb = Lb, fov = fov, direction = 'inverse', inputtype = 'xyz', outin = 'Q,aS,bS', parameters = parameters)


#------------------------------------------------------------------------------
if __name__ == '__main__':
    C = _CIE_ILLUMINANTS['C'].copy()
    C = np.vstack((C,cie_interp(_CIE_ILLUMINANTS['D65'],C[0],kind='spd')[1:]))
    M = _MUNSELL.copy()
    rflM = M['R']
    cieobs = '2006_10'
    
    # Normalize to Lw:
    Lw = 100
    xyzw2 = spd_to_xyz(C, cieobs = cieobs, relative = False)
    for i in range(C.shape[0]-1):
        C[i+1] = Lw*C[i+1]/xyzw2[i,1]

    
    xyz, xyzw = spd_to_xyz(C, cieobs = cieobs, relative = True, rfl = rflM, out = 2)
    qab = xyz_to_qabW_cam18sl(xyzw, xyzb = None, Lb = [100], fov = 10.0)
    print('qab: ',qab)
    qab2 = cam18sl(C, datab = None, Lb = [100], fov = 10.0, direction = 'forward', inputtype = 'spd', outin = 'Q,aW,bW', parameters = None)
Пример #17
0
def test_model():

    import pandas as pd
    import luxpy as lx

    # Read selected set of Munsell samples and LMS10(lambda):
    M = pd.read_csv('Munsell_LMS_nonlin_Nov18_2015_version.dat',
                    header=None,
                    sep='\t').values
    YLMS10_ = pd.read_csv('YLMS10_LMS_nonlin_Nov18_2015_version.dat',
                          header=None,
                          sep='\t').values
    Y10_ = YLMS10_[[0, 1], :].copy()
    LMS10_ = YLMS10_[[0, 2, 3, 4], :].copy()

    # Calculate lms:
    Y10 = cie_interp(_CMF['1964_10']['bar'].copy(),
                     getwlr([400, 700, 5]),
                     kind='cmf')[[0, 2], :]
    XYZ10_lx = _CMF['2006_10']['bar'].copy()
    XYZ10_lx = cie_interp(XYZ10_lx, getwlr([400, 700, 5]), kind='cmf')
    LMS10_lx = np.vstack(
        (XYZ10_lx[:1, :],
         np.dot(
             math.normalize_3x3_matrix(_CMF['2006_10']['M'],
                                       np.array([[1, 1, 1]])),
             XYZ10_lx[1:, :])))
    LMS10 = cie_interp(LMS10_lx, getwlr([400, 700, 5]), kind='cmf')

    #LMS10 = np.vstack((XYZ10[:1,:],np.dot(lx.math.normalize_3x3_matrix(_CMF['2006_10']['M'],np.array([[1,1,1]])),XYZ10_lx[1:,:])))

    #LMS10[1:,:] = LMS10[1:,:]/LMS10[1:,:].sum(axis=1,keepdims=True)*Y10[1:,:].sum()

    # test python model vs excel calculator:
    def spdBB(CCT=5500, wl=[400, 700, 5], Lw=25000, cieobs='1964_10'):
        wl = getwlr(wl)
        dl = wl[1] - wl[0]
        spd = 2 * np.pi * 6.626068E-34 * (299792458**2) / (
            (wl * 0.000000001)**
            5) / (np.exp(6.626068E-34 * 299792458 /
                         (wl * 0.000000001) / 1.3806503E-23 / CCT) - 1)
        spd = Lw * spd / (dl * 683 * (spd * cie_interp(
            _CMF[cieobs]['bar'].copy(), wl, kind='cmf')[2, :]).sum())
        return np.vstack((wl, spd))

    # Create long term and applied spds:
    spd5500 = spdBB(5500, Lw=25000, wl=[400, 700, 5], cieobs='1964_10')
    spd6500 = spdBB(6500, Lw=400, wl=[400, 700, 5], cieobs='1964_10')

    # Calculate lms0 as a check:
    clms = np.array(
        [0.98446776, 0.98401909, 0.98571412]
    )  # correction factor for slight differences in _CMF and the cmfs from the excel calculator
    lms0 = 5 * 683 * (spd5500[1:] * LMS10[1:, :] * 0.2).sum(axis=1).T

    # Full excel parameters for testing:
    parameters = {
        'cLMS':
        np.array([1, 1, 1]),
        'lms0':
        np.array([4985.02802565, 5032.49518502, 4761.27272226]) * 1,
        'Cc':
        0.251617118325755,
        'Cf':
        -0.4,
        'clambda': [0.5, 0.5, 0.0],
        'calpha': [1.0, -1.0, 0.0],
        'cbeta': [0.5, 0.5, -1.0],
        'cga1': [26.1047711317923, 33.9721745703298],
        'cgb1': [6.76038379211498, 10.9220216677629],
        'cga2': [0.587271269247578],
        'cgb2': [-0.952412544980473],
        'cl_int': [14.0035243121804, 1.0],
        'cab_int': [4.99218965716342, 65.7869547646456],
        'cab_out': [-0.1, -1.0],
        'Ccwb':
        None,
        'Mxyz2lms': [[0.21701045, 0.83573367, -0.0435106],
                     [-0.42997951, 1.2038895, 0.08621089],
                     [0., 0., 0.46579234]]
    }

    # Note cLMS is a relative scaling factor between CIE2006 10° and 1964 10°:
    #    clms = np.array([1.00164919, 1.00119269, 1.0029173 ]) = (Y10[1:,:].sum(axis=1)/LMS10[1:,:].sum(axis=1))*(406.98099078/400)

    #parameters =_CAM_SWW16_PARAMETERS['JOSA']
    # Calculate Munsell spectra multiplied with spd6500:
    spd6500xM = np.vstack((spd6500[:1, :], spd6500[1:, :] * M[1:, :]))

    # Test spectral input:
    print('SPD INPUT -----')
    jab = cam_sww16(spd6500xM,
                    dataw=spd6500,
                    Yb=20.0,
                    Lw=400.0,
                    Ccwb=1,
                    relative=True,
                    inputtype='spd',
                    direction='forward',
                    parameters=parameters,
                    cieobs='2006_10',
                    match_to_conversionmatrix_to_cieobs=True)

    #    # Test xyz input:
    print('\nXYZ INPUT -----')
    xyz = lx.spd_to_xyz(spd6500xM, cieobs='2006_10', relative=False)
    xyzw = lx.spd_to_xyz(spd6500, cieobs='2006_10', relative=False)
    xyz2, xyzw2 = lx.spd_to_xyz(spd6500,
                                cieobs='2006_10',
                                relative=False,
                                rfl=M,
                                out=2)

    print(xyzw)
    jab = cam_sww16(xyz,
                    dataw=xyzw,
                    Yb=20.0,
                    Lw=400,
                    Ccwb=1,
                    relative=True,
                    inputtype='xyz',
                    direction='forward',
                    parameters=parameters,
                    cieobs='2006_10',
                    match_to_conversionmatrix_to_cieobs=True)
Пример #18
0
    # For comparison
    _CRI_TYPE_TM30 = copy.deepcopy(lx.cri._CRI_DEFAULTS['iesrf-tm30-18'])
    _CRI_TYPE_TM30['sampleset'] = "_CRI_RFL['ies-tm30-18']['99']['1nm']"
    def _spd_to_tm30(spd, cieobs = '1931_2', mixer_type = '3mixer'):
        # Call function that calculates ref.illuminant and jabt & jabr only once to obtain Rf & Rg:
    
        data = lx.cri._tm30_process_spd(spd, cri_type = _CRI_TYPE_TM30) # use 1nm samples to avoid interpolation
        Rf, Rg = data['Rf'], data['Rg']
        
        thetad = data['hue_bin_data']['gamut_ellipse_fit']['thetad']
        ecc = data['hue_bin_data']['gamut_ellipse_fit']['a/b']
        
        xyzw = lx.spd_to_xyz(spd, cieobs = cieobs, relative = False) # set K = 1 to avoid overflow when _FLOAT_TYPE = np.float16
        data['xyzw'] = xyzw
        return data
    
    spds = lx._IESTM3018['S']['data'].copy()
    spds = lx.cie_interp(spds,wl_new = _WL,kind='spd')
    spds = spds[:202,:]
    data = spd_to_tm30(spds[[0,104],:])
    # data = spd_to_tm30(lx._CIE_F4)
    jabr = data['hue_bin_data']['jabrn_hj_closed']
    jabt = data['hue_bin_data']['jabtn_hj_closed']
    plt.plot(jabt[...,1],jabt[...,2],'b+-')
    plt.plot(jabr[...,1],jabr[...,2],'rx-')
    
        



Пример #19
0
    (Rfi_vf, DEi_vf) = _hue_bin_data_to_rfi(hue_bin_data_vf, 
                                            cri_type = cri_type,
                                            scale_factor = scale_factor,
                                            scale_fcn = scale_fcn)
    # Store in dict:
    _data_vf.update({'Rfi' : Rfi_vf, 'DEi' : DEi_vf,
                     'Rcshj' : Rcshj_vf, 'Rhshj' : Rhshj_vf,
                     'Rfhj' : Rfhj_vf, 'DEhj': DEhj_vf,
                     'dataVF' : dataVF, 'hue_bin_data' : hue_bin_data_vf})
    
    # Add to main dictionary:
    data['vf'] = _data_vf
    return data

if __name__ == '__main__':
    
    # for testing:
    import luxpy as lx
    F4 = lx.cie_interp(lx._CIE_F4,wl_new=[360,830,1],kind='spd')
    D65 = lx.cie_interp(lx._CIE_D65,wl_new=[360,830,1],kind='spd')
    spds = lx._IESTM3018['S']['data'].copy()
    spds = lx.cie_interp(spds,wl_new = [360,830,1],kind='spd')
    
    spd = np.vstack((F4,D65[1:]))
    d = spd_to_ies_tm30_metrics(spd, cri_type = None, \
                                hbins = 16, start_hue = 0.0,\
                                scalef = 100, \
                                vf_model_type = _VF_MODEL_TYPE, \
                                vf_pcolorshift = _VF_PCOLORSHIFT,\
                                scale_vf_chroma_to_sample_chroma = False)