Example #1
0
def generate_vector_field(poly_model, pmodel, \
                          axr = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR), \
                          bxr = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR), \
                          make_grid = True, limit_grid_radius = 0,color = 'k'):
    """
    Generates a field of vectors using the base color shift model.
    
    | Has the option to plot vector field.
    
    Args:
        :poly_model: 
            | function handle to model
        :pmodel:
            | ndarray with model parameters.
        :axr: 
            | np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR), optional
            | Ndarray specifying the a-coordinates at which to apply the model.
        :bxr:
            | np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR), optional
            | Ndarray specifying the b-coordinates at which to apply the model.
        :make_grid:
            | True, optional
            | True: generate a 2d-grid from :axr:, :bxr:.
        :limit_grid_radius:
            | 0, optional
            |   A value of zeros keeps grid as specified  by axr,bxr.
            |   A value > 0 only keeps (a,b) coordinates within :limit_grid_radius:
        :color:
            | 'k', optional
            | For plotting the vector field.
            | If :color: == 0, no plot will be generated.
    
    Returns:
        :returns: 
            | If :color: == 0: ndarray of axt,bxt,axr,bxr
            | Else: handle to axes used for plotting.
    """

    # Generate grid from axr, bxr:
    if make_grid == True:
        axr, bxr = generate_grid(ax=axr,
                                 bx=bxr,
                                 out='ax,bx',
                                 limit_grid_radius=limit_grid_radius)

    # Apply model at ref. coordinates:
    axt, bxt, Cxt, hxt, axr, bxr, Cxr, hxr = apply_poly_model_at_x(
        poly_model, pmodel, axr, bxr)

    # Plot vectorfield:
    if color is not 0:
        #plt.plot(axr, bxr,'ro',markersize=2)
        plt.quiver(axr, bxr, axt - axr, bxt - bxr, headlength=1, color=color)
        plt.xlabel("a'")
        plt.ylabel("b'")
        return plt.gca()  #plt.show(plot1)
    else:
        return axt, bxt, axr, bxr
Example #2
0
def plotceruleanline(cieobs=_CIEOBS,
                     cspace=_CSPACE,
                     axh=None,
                     formatstr='ko-',
                     cspace_pars={}):
    """
    Plot cerulean (yellow (577 nm) - blue (472 nm)) line 
    
    | Kuehni, CRA, 2014: 
    |   Table II: spectral lights.
    
    Args: 
        :axh: 
            | None or axes handle, optional
            | Determines axes to plot data in.
            | None: make new figure.
        :cieobs:
            | luxpy._CIEOBS or str, optional
            | Determines CMF set to calculate spectrum locus or other.
        :cspace:
            | luxpy._CSPACE or str, optional
            | Determines color space / chromaticity diagram to plot data in.
            | Note that data is expected to be in specified :cspace:
        :formatstr:
            | 'k-' or str, optional
            | Format str for plotting (see ?matplotlib.pyplot.plot)
        :cspace_pars:
            | {} or dict, optional
            | Dict with parameters required by color space specified in :cspace: 
            | (for use with luxpy.colortf())
        :kwargs:
            | additional keyword arguments for use with matplotlib.pyplot.
    
    Returns:
        :returns:
            | handle to cerulean line
        
    References:
        1. `Kuehni, R. G. (2014). 
        Unique hues and their stimuli—state of the art. 
        Color Research & Application, 39(3), 279–287. 
        <https://doi.org/10.1002/col.21793>`_
        (see Table II, IV)
    """
    if isinstance(cieobs, str):
        cmf = _CMF[cieobs]['bar']
    else:
        cmf = cieobs
    p_y = cmf[0] == 577.0  #Kuehni, CRA 2013 (mean, table IV)
    p_b = cmf[0] == 472.0  #Kuehni, CRA 2013 (mean, table IV)
    xyz_y = cmf[1:, p_y].T
    xyz_b = cmf[1:, p_b].T
    lab = colortf(np.vstack((xyz_b, xyz_y)), tf=cspace, tfa0=cspace_pars)
    if axh is None:
        axh = plt.gca()
    hcerline = axh.plot(lab[:, 1], lab[:, 2], formatstr, label='Cerulean line')
    return hcerline
Example #3
0
 def plot(self, ylabel='Spectrum', wavelength_bar=True, *args, **kwargs):
     """
     Make a plot of the spectral data in SPD instance.
     
     Returns:
         :returns:
             | handle to current axes.
     """
     plt.plot(self.wl, self.value.T, *args, **kwargs)
     if wavelength_bar == True:
         Smax = np.nanmax(self.value)
         axh = plot_spectrum_colors(spd=None,
                                    spdmax=Smax,
                                    axh=plt.gca(),
                                    wavelength_height=-0.05)
     plt.xlabel('Wavelength (nm)')
     plt.ylabel(ylabel)
     return plt.gca()
Example #4
0
    def plot(self, plt_type='3d', ax=None, title=None, **kwargs):
        """
        Plot color coordinates.
        
        Args:
            :plt_type: 
                | '3d' or 3 or '2d or 2, optional
                |   -'3d' or 3: plot all 3 dimensions (lightness and chromaticity)
                |   -'2d' or 2: plot only chromaticity dimensions.
            :ax: 
                | None or axes handles, optional
                | None: create new figure axes, else use :ax: for plotting.
            :title: 
                | None or str, optional
                | Give plot a title.
            :**kwargs:
                | additional arguments for use with 
                | matplotlib.pyplot.scatter
                
        Returns:
            :gca: 
                | handle to current axes.
        """
        L, a, b = self.split_()
        if ax is None:
            fig = plt.figure()

        if (plt_type == '2d') | (plt_type == 2):
            if ax is None:
                ax = fig.add_subplot(111)
            ax.scatter(a, b, **kwargs)
        else:
            if ax is None:
                ax = fig.add_subplot(111, projection='3d')
            ax.scatter(a, b, L, **kwargs)
            ax.set_zlabel(_CSPACE_AXES[self.dtype][0])
        ax.set_xlabel(_CSPACE_AXES[self.dtype][1])
        ax.set_ylabel(_CSPACE_AXES[self.dtype][2])

        if title is not None:
            ax.set_title(title)

        return plt.gca()
Example #5
0
    def plot(self, ax=None, title=None, **kwargs):
        """
        Plot tristimulus or cone fundamental values.
        
        Args:
            :ax: 
                | None or axes handles, optional
                | None: create new figure axes, else use :ax: for plotting.
            :title:
                | None or str, optional
                | Give plot a title.
            :**kwargs: 
                | additional arguments for use with 
                | matplotlib.pyplot.scatter
                
        Returns:
            :gca:
                | handle to current axes.
        """
        X, Y, Z = self.split_()
        if ax is None:
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
        if self.dtype == 'xyz':
            ax.scatter(X, Z, Y, **kwargs)
            ax.set_xlabel(_CSPACE_AXES[self.dtype][0])
            ax.set_ylabel(_CSPACE_AXES[self.dtype][2])
            ax.set_zlabel(_CSPACE_AXES[self.dtype][1])
        elif self.dtype == 'lms':
            ax.scatter(X, Y, Z, **kwargs)
            ax.set_xlabel(_CSPACE_AXES[self.dtype][0])
            ax.set_ylabel(_CSPACE_AXES[self.dtype][1])
            ax.set_zlabel(_CSPACE_AXES[self.dtype][2])
        if title is not None:
            ax.set_title(title)

        return plt.gca()
Example #6
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)
Example #7
0
def plotUH(xyz0=None,
           uhues=[0, 1, 2, 3],
           cieobs=_CIEOBS,
           cspace=_CSPACE,
           axh=None,
           formatstr=['yo-.', 'bo-.', 'ro-.', 'go-.'],
           excludefromlegend='',
           cspace_pars={}):
    """ 
    Plot unique hue lines from color space center point xyz0.
    
    | Kuehni, CRA, 2014: 
    |     uY,uB,uG: Table II: spectral lights; 
    |     uR: Table IV: Xiao data.
    
    Args: 
        :xyz0:
            | None, optional
            | Center of color space (unique hue lines are expected to cross here)
            | None defaults to equi-energy-white.
        :uhues:
            | [0,1,2,3], optional
            | Unique hue lines to plot [0:'yellow',1:'blue',2:'red',3:'green']
        :axh: 
            | None or axes handle, optional
            | Determines axes to plot data in.
            | None: make new figure.
        :cieobs:
            | luxpy._CIEOBS or str, optional
            | Determines CMF set to calculate spectrum locus or other.
        :cspace:
            | luxpy._CSPACE or str, optional
            | Determines color space / chromaticity diagram to plot data in.
            | Note that data is expected to be in specified :cspace:
        :formatstr:
            | ['yo-.','bo-.','ro-.','go-.'] or list[str], optional
            | Format str for plotting the different unique lines 
            | (see also ?matplotlib.pyplot.plot)
        :excludefromlegend:
            | '' or str, optional
            | To exclude certain hues from axes legend.
        :cspace_pars:
            | {} or dict, optional
            | Dict with parameters required by color space specified in :cspace: 
            | (for use with luxpy.colortf())
          
    Returns:
        :returns: 
            | list[handles] to unique hue lines
        
    References:
        1. `Kuehni, R. G. (2014). 
        Unique hues and their stimuli—state of the art. 
        Color Research & Application, 39(3), 279–287. 
        <https://doi.org/10.1002/col.21793>`_
        (see Table II, IV)
    """
    hues = ['yellow', 'blue', 'red', 'green']
    if isinstance(cieobs, str):
        cmf = _CMF[cieobs]['bar']
    else:
        cmf = cieobs
    p_y = cmf[
        0] == 577.0  #unique yellow,#Kuehni, CRA 2013 (mean, table IV: spectral data)
    p_b = cmf[
        0] == 472.0  #unique blue,Kuehni, CRA 2013 (mean, table IV: spectral data)
    p_g = cmf[
        0] == 514.0  #unique green, Kuehni, CRA 2013 (mean, table II: spectral data)
    p_r = cmf[
        0] == 650.0  #unique red, Kuehni, CRA 2013 (Xiao data, table IV: display data)
    xyz_y = 100.0 * cmf[1:, p_y].T
    xyz_b = 100.0 * cmf[1:, p_b].T
    xyz_g = 100.0 * cmf[1:, p_g].T
    xyz_r = 100.0 * cmf[1:, p_r].T
    xyz_uh = np.vstack((xyz_y, xyz_b, xyz_r, xyz_g))
    huniquehues = []
    if xyz0 is None:
        xyz0 = np.array([100.0, 100.0, 100.0])
    if axh is None:
        axh = plt.gca()
    for huenr in uhues:
        lab = colortf(np.vstack((xyz0, xyz_uh[huenr])),
                      tf=cspace,
                      tfa0=cspace_pars)
        huh = axh.plot(lab[:, 1],
                       lab[:, 2],
                       formatstr[huenr],
                       label=excludefromlegend + 'Unique ' + hues[huenr])
        huniquehues = [huniquehues, huh]
    return huniquehues