Esempio n. 1
0
def histogram(a, bins=10, bin_center = False, range=None, normed=False, weights=None, density=None):
    """
    Histogram function that can take as bins either the center (cfr. matlab hist) or bin-edges.
    
    Args: 
        :bin_center:
            | False, optional
            | False: if :bins: int, str or sequence of scalars:
            |       default to numpy.histogram (uses bin edges).
            | True: if :bins: is a sequence of scalars:
            |         bins (containing centers) are transformed to edges
            |         and nump.histogram is run. 
            |         Mimicks matlab hist (uses bin centers).
        
    Note:
        For other armuments and output, see ?numpy.histogram
        
    Returns:
        :returns:
            | ndarray with histogram
    """
    if (isinstance(bins, list) |  isinstance(bins, np.ndarray)) & (bin_center == True):
        if len(bins) == 1:
            edges = np.hstack((bins[0],np.inf))
        else:
            centers = bins
            d = np.diff(centers)/2
            edges = np.hstack((centers[0]-d[0], centers[:-1] + d, centers[-1] + d[-1]))
            edges[1:] = edges[1:] + np.finfo(float).eps
        return np.histogram(a, bins=edges, range=range, normed=normed, weights=weights, density=density)

    else:
        return np.histogram(a, bins=bins, range=range, normed=normed, weights=weights, density=density)
Esempio n. 2
0
def v_to_cik(v, inverse = False):
    """
    Calculate 2x2 '(covariance matrix)^-1' elements cik 
    
    Args:
        :v: 
            | (Nx5) np.ndarray
            | ellipse parameters [Rmax,Rmin,xc,yc,theta]
        :inverse:
            | If True: return inverse of cik.
    
    Returns:
        :cik: 
            | 'Nx2x2' (covariance matrix)^-1
    
    Notes:
        | cik is not actually a covariance matrix,
        | only for a Gaussian or normal distribution!

    """
    v = np.atleast_2d(v)
    g11 = (1/v[:,0]*np.cos(v[:,4]))**2 + (1/v[:,1]*np.sin(v[:,4]))**2
    g22 = (1/v[:,0]*np.sin(v[:,4]))**2 + (1/v[:,1]*np.cos(v[:,4]))**2
    g12 = (1/v[:,0]**2 - 1/v[:,1]**2)*np.sin(v[:,4])*np.cos(v[:,4])
    cik = np.zeros((g11.shape[0],2,2))

    for i in range(g11.shape[0]):
        cik[i,:,:] = np.vstack((np.hstack((g11[i],g12[i])), np.hstack((g12[i],g22[i]))))
        if inverse == True:
            cik[i,:,:] = np.linalg.inv(cik[i,:,:])
    return cik
Esempio n. 3
0
def _plot_DEs_vs_digital_values(DEslab, DEsl, DEsab, rgbcal, avg = lambda x: ((x**2).mean()**0.5), nbit = 8, verbosity = 1):
    """ Make a plot of the lab, l and ab color differences for the different calibration stimulus types. """
    if verbosity > 0:
        p_pure = [(rgbcal[:,1]==0) & (rgbcal[:,2]==0), 
              (rgbcal[:,0]==0) & (rgbcal[:,2]==0), 
              (rgbcal[:,0]==0) & (rgbcal[:,1]==0)] 
        p_grays = (rgbcal[:,0] == rgbcal[:,1]) & (rgbcal[:,0] == rgbcal[:,2])
        p_whites = (rgbcal[:,0] == (2**nbit-1)) & (rgbcal[:,1] == (2**nbit-1)) & (rgbcal[:,2] == (2**nbit-1))
        p_cyans = (rgbcal[:,0]==0) & (rgbcal[:,1]!=0) & (rgbcal[:,2]!=0)
        p_yellows = (rgbcal[:,0]!=0) & (rgbcal[:,1]!=0) & (rgbcal[:,2]==0)
        p_magentas = (rgbcal[:,0]!=0) & (rgbcal[:,1]==0) & (rgbcal[:,2]==0)
        fig,(ax0,ax1,ax2) = plt.subplots(nrows=1,ncols=3, figsize = (15,4))
        rgb_colors='rgb'
        rgb_labels=['red','green','blue']
        marker ='o'
        markersize = 10
        if p_whites.any():
            ax0.plot(rgbcal[p_whites,0], DEslab[p_whites],'ks',markersize = markersize, label='white')
            ax1.plot(rgbcal[p_whites,0], DEsl[p_whites],'ks',markersize = markersize,label='white')
            ax2.plot(rgbcal[p_whites,0], DEsab[p_whites],'ks',markersize = markersize,label='white')
        if p_grays.any():
            ax0.plot(rgbcal[p_grays,0], DEslab[p_grays], color = 'gray', marker = marker,linestyle='none',label='gray')
            ax1.plot(rgbcal[p_grays,0], DEsl[p_grays], color = 'gray', marker = marker,linestyle='none',label='gray')
            ax2.plot(rgbcal[p_grays,0], DEsab[p_grays], color = 'gray', marker = marker,linestyle='none',label='gray')
        for i in range(3):
            if p_pure[i].any():
                ax0.plot(rgbcal[p_pure[i],i], DEslab[p_pure[i]],rgb_colors[i]+marker,label=rgb_labels[i])
                ax1.plot(rgbcal[p_pure[i],i], DEsl[p_pure[i]],rgb_colors[i]+marker,label=rgb_labels[i])
                ax2.plot(rgbcal[p_pure[i],i], DEsab[p_pure[i]],rgb_colors[i]+marker,label=rgb_labels[i])
        if p_cyans.any():
            ax0.plot(rgbcal[p_cyans,1], DEslab[p_cyans],'c'+marker,label='cyan')
            ax1.plot(rgbcal[p_cyans,1], DEsl[p_cyans],'c'+marker,label='cyan')
            ax2.plot(rgbcal[p_cyans,1], DEsab[p_cyans],'c'+marker,label='cyan')
        if p_yellows.any():
            ax0.plot(rgbcal[p_yellows,0], DEslab[p_yellows],'y'+marker,label='yellow')
            ax1.plot(rgbcal[p_yellows,0], DEsl[p_yellows],'y'+marker,label='yellow')
            ax2.plot(rgbcal[p_yellows,0], DEsab[p_yellows],'y'+marker,label='yellow')
        if p_magentas.any():
            ax0.plot(rgbcal[p_magentas,0], DEslab[p_magentas],'m'+marker,label='magenta')
            ax1.plot(rgbcal[p_magentas,0], DEsl[p_magentas],'m'+marker,label='magenta')
            ax2.plot(rgbcal[p_magentas,0], DEsab[p_magentas],'m'+marker,label='magenta')
        ax0.plot(np.array([0,(2**nbit-1)*1.05]),np.hstack((avg(DEslab),avg(DEslab))),color = 'r',linewidth=2,linestyle='--')
        ax0.set_xlabel('digital values')
        ax0.set_ylabel('Color difference DElab')
        ax0.axis([0,(2**nbit-1)*1.05,0,max(DEslab)*1.1])
        ax0.set_title('DElab')
        ax1.plot(np.array([0,(2**nbit-1)*1.05]),np.hstack((avg(DEsl),avg(DEsl))),color = 'r',linewidth=2,linestyle='--')
        ax1.set_xlabel('digital values')
        ax1.set_ylabel('Color difference DEl')
        ax1.axis([0,(2**nbit-1)*1.05,0,max(DEslab)*1.1])
        ax1.set_title('DEl')
        ax2.plot(np.array([0,(2**nbit-1)*1.05]),np.hstack((avg(DEsab),avg(DEsab))),color = 'r',linewidth=2,linestyle='--')
        ax2.set_xlabel('digital values')
        ax2.set_ylabel('Color difference DEab')
        ax2.set_title('DEab')
        ax2.axis([0,(2**nbit-1)*1.05,0,max(DEslab)*1.1])
        ax2.legend(loc='upper left')
Esempio n. 4
0
def plot(v,
         origin=None,
         ax=None,
         color='k',
         marker='.',
         linestyle='-',
         **kwargs):
    """
    Plot a vector from origin.
    
    Args:
        :v:
            | vec3 vector.
        :origin:
            | vec3 vector with same size attributes as in :v:.
        :ax: 
            | None, optional
            | axes handle.
            | If None, create new figure with axes ax.
        :color:
            | 'k', optional
            | color specifier.
        :marker:
            | '.', optional
            | marker specifier.
        :linestyle:
            | '-', optional
            | linestyle specifier
        :**kwargs:
            | other keyword specifiers for plot.
          
    Returns:
        :ax:
            | handle to figure axes.          
    """
    if ax is None:
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
    if origin is None:
        origin = vec3(np.zeros(v.x.shape), np.zeros(v.x.shape),
                      np.zeros(v.x.shape))
    ax.plot(np.hstack([origin.x, v.x]),
            np.hstack([origin.y, v.y]),
            np.hstack([origin.z, v.z]),
            color=color,
            marker=marker,
            **kwargs)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    return ax
Esempio n. 5
0
def plot_rgb_color_patches(rgb,
                           patch_shape=(100, 100),
                           patch_layout=None,
                           ax=None,
                           show=True):
    """
    Create (and plot) an image with patches with specified rgb values.
    
    Args:
        :rgb:
            | ndarray with rgb values for each of the patches
        :patch_shape:
            | (100,100), optional
            | shape of each of the patches in the image
        :patch_layout:
            | None, optional
            | If None: layout is calculated automatically to give a 'good' aspect ratio
        :ax:
            | None, optional
            | Axes to plot the image in. If None: a new axes is created.
        :show:
            | True, optional
            | If True: plot image in axes and return axes handle; else: return ndarray with image.
            
    Return:
        :ax: or :imagae: 
            | Axes is returned if show == True, else: ndarray with rgb image is returned.
    """
    if ax is None:
        fig, ax = plt.subplots(1, 1)

    if patch_layout is None:
        patch_layout = get_subplot_layout(rgb.shape[0])

    image = np.zeros(
        np.hstack((np.array(patch_shape) * np.array(patch_layout), 3)))
    for i in range(rgb.shape[0]):
        r, c = np.unravel_index(i, patch_layout)
        R = int(r * patch_shape[0])
        C = int(c * patch_shape[1])
        image[R:R + patch_shape[0],
              C:C + patch_shape[1], :] = np.ones(np.hstack(
                  (patch_shape, 3))) * rgb[i, None, :]

    if show == False:
        return image
    else:
        ax.imshow(image.astype('uint8'))
        ax.axis('off')
        return ax
Esempio n. 6
0
 def join(self, data):
     """
     Join data along last axis and return instance.
     """
     if data[0].ndim == 2:  #faster implementation
         self.value = np.transpose(
             np.concatenate(data, axis=0).reshape((np.hstack(
                 (len(data), data[0].shape)))), (1, 2, 0))
     elif data[0].ndim == 1:
         self.value = np.concatenate(data, axis=0).reshape((np.hstack(
             (len(data), data[0].shape)))).T
     else:
         self.value = np.hstack(data)[0]
     return self
Esempio n. 7
0
def getwld(wl):
    """
    Get wavelength spacing. 
    
    Args:
        :wl: 
            | ndarray with wavelengths
        
    Returns:
        :returns: 
            | - float:  for equal wavelength spacings
            | - ndarray (.shape = (n,)): for unequal wavelength spacings
    """
    d = np.diff(wl)
    dl = (np.hstack((d[0], d[0:-1] / 2.0, d[-1])) + np.hstack(
        (0.0, d[1:] / 2.0, 0.0)))
    if np.array_equal(dl, dl.mean() * np.ones(dl.shape)): dl = dl[0]
    return dl
Esempio n. 8
0
def plotcircle(center=np.array([[0., 0.]]),
               radii=np.arange(0, 60, 10),
               angles=np.arange(0, 350, 10),
               color='k',
               linestyle='--',
               out=None,
               axh=None,
               **kwargs):
    """
    Plot one or more concentric circles.
    
    Args:
        :center: 
            | np.array([[0.,0.]]) or ndarray with center coordinates, optional
        :radii:
            | np.arange(0,60,10) or ndarray with radii of circle(s), optional
        :angles:
            | np.arange(0,350,10) or ndarray with angles (°), optional
        :color:
            | 'k', optional
            | Color for plotting.
        :linestyle:
            | '--', optional
            | Linestyle of circles.
        :out: 
            | None, optional
            | If None: plot circles, return (x,y) otherwise.
    """
    xs = np.array([0])
    ys = xs.copy()
    if ((out != 'x,y') & (axh is None)):
        fig, axh = plt.subplots(rows=1, ncols=1)
    for ri in radii:
        x = center[:, 0] + ri * np.cos(angles * np.pi / 180)
        y = center[:, 1] + ri * np.sin(angles * np.pi / 180)
        xs = np.hstack((xs, x))
        ys = np.hstack((ys, y))
        if (out != 'x,y'):
            axh.plot(x, y, color=color, linestyle=linestyle, **kwargs)
    if out == 'x,y':
        return xs, ys
    elif out == 'axh':
        return axh
Esempio n. 9
0
def _complete_ies_lid(IES, lamp_h_type='TYPE90'):
    """
    Convert IES LID map with lamp_h_type symmetry to a 'full' map with phi: [0,360] and theta: [0,180].
    """
    # Create full theta (0-180) and phi (0-360) sets
    IES['theta'] = IES['v_angs']
    if IES['lamp_h_type'] == 'TYPE90':
        IES['values'] = np.matlib.repmat(IES['candela_2d'], 4, 1)
        IES['phi'] = np.hstack((IES['h_angs'], IES['h_angs'] + 90,
                                IES['h_angs'] + 180, IES['h_angs'] + 270))
    elif IES['lamp_h_type'] == 'TYPE180':
        IES['values'] = np.matlib.repmat(IES['candela_2d'], 2, 1)
        IES['phi'] = np.hstack((IES['h_angs'], IES['h_angs'] + 180))
    else:
        IES['values'] = IES['candela_2d']
        IES['phi'] = IES['h_angs']
    IES['map']['thetas'] = IES['theta']
    IES['map']['phis'] = IES['phi']
    IES['map']['values'] = IES['values']
    return IES
Esempio n. 10
0
def xtransform(x, params):
    """
    Converts unconstrained variables into their original domains.
    """

    xtrans = np.zeros((params['n']))

    # k allows some variables to be fixed, thus dropped from the optimization.
    k = 0
    for i in range(params['n']):
        if params['BoundClass'][i] == 1:
            # lower bound only
            xtrans[i] = params['LB'][i] + x[k]**2

        elif params['BoundClass'][i] == 2:
            # upper bound only
            xtrans[i] = params['UB'][i] - x[k]**2

        elif params['BoundClass'][i] == 3:
            # lower and upper bounds
            xtrans[i] = (np.sin(x[k]) + 1) / 2
            xtrans[i] = xtrans[i] * (params['UB'][i] -
                                     params['LB'][i]) + params['LB'][i]

            # just in case of any floating point problems
            xtrans[i] = np.hstack(
                (params['LB'][i], np.hstack(
                    (params['UB'][i], xtrans[i])).min())).max()

        elif params['BoundClass'][i] == 4:
            # fixed variable, bounds are equal, set it at either bound
            xtrans[i] = params['LB'][i]

        elif params['BoundClass'][i] == 0:
            # unconstrained variable.
            xtrans[i] = x[k]

        if params['BoundClass'][i] != 4:
            k += 1

    return xtrans
Esempio n. 11
0
def get_discrimination_ellipse(Yxy = np.array([[100,1/3,1/3]]), etype = 'fmc2', nsteps = 10, k_neighbours = 3, average_cik = True, Y = None):
    """
    Get discrimination ellipse(s) in v-format (R,r, xc, yc, theta) for Yxy using an interpolation of the MacAdam ellipses or using FMC-1 or FMC-2.
    
    Args:
        :Yxy:
            | 2D ndarray with [Y,]x,y coordinate centers. 
            | If Yxy.shape[-1]==2: Y is added using the value from the Y-input argument.
        :etype:
            | 'fmc2', optional
            | Type color discrimination ellipse estimation to use.
            | options: 'macadam', 'fmc1', 'fmc2' 
            |  - 'macadam': interpolate covariance matrices of closest MacAdam ellipses (see: get_macadam_ellipse?).
            |  - 'fmc1': use FMC-1 from ref 2 (see get_fmc_discrimination_ellipse?).
            |  - 'fmc2': use FMC-1 from ref 3 (see get_fmc_discrimination_ellipse?).
        :nsteps:
            | 10, optional
            | Set multiplication factor for ellipses 
            | (nsteps=1 corresponds to approximately 1 MacAdam step, 
            | for FMC-2, Y also has to be 10.69, see note below).
        :k_neighbours:
            | 3, optional
            | Only for option 'macadam'.
            | Number of nearest ellipses to use to calculate ellipse at xy 
        :average_cik:
            | True, optional
            | Only for option 'macadam'.
            | If True: take distance weighted average of inverse 
            |   'covariance ellipse' elements cik. 
            | If False: average major & minor axis lengths and 
            |   ellipse orientation angles directly.
        :Y:
            | None, optional
            | Only for option 'fmc2'(see note below).
            | If not None: Y = 10.69 and overrides values in Yxy. 
    
    Note:
        1. FMC-2 is almost identical to FMC-1 is Y = 10.69!; see [3]
    
    References:
       1. MacAdam DL. Visual Sensitivities to Color Differences in Daylight*. J Opt Soc Am. 1942;32(5):247-274.
       2. Chickering, K.D. (1967), Optimization of the MacAdam-Modified 1965 Friele Color-Difference Formula, 57(4):537-541
       3. Chickering, K.D. (1971), FMC Color-Difference Formulas: Clarification Concerning Usage, 61(1):118-122
    """
    if Yxy.shape[-1] == 2:
        Yxy = np.hstack((100*np.ones((Yxy.shape[0],1)),Yxy))
    if Y is not None:
        Yxy[...,0] = Y
    if etype == 'macadam':
        return get_macadam_ellipse(xy = Yxy[...,1:], k_neighbours = k_neighbours, nsteps = nsteps, average_cik = average_cik)
    else:
        return get_fmc_discrimination_ellipse(Yxy = Yxy, etype = etype, nsteps = nsteps, Y = Y)
Esempio n. 12
0
def _create_subjects_index_arr(subjects=None, grouping=None):
    """ Create subjects indexing array"""
    if subjects is None:
        if grouping is None:
            raise Exception('Grouping must be supplied!')
        groups = np.unique(grouping)
        for i, group in enumerate(groups):
            if i == 0:
                subjects = np.arange(((grouping == group) * 1).sum())
            else:
                subjects = np.hstack(
                    (subjects, np.arange(((grouping == group) * 1).sum())))
    return subjects
Esempio n. 13
0
def plotcircle(radii = np.arange(0,60,10), \
               angles = np.arange(0,350,10),\
               color = 'k',linestyle = '--', out = None):
    """
    Plot one or more concentric circles around (0,0).
    
    Args:
        :radii:
            | np.arange(0,60,10) or ndarray with radii of circle(s), optional
        :angles:
            | np.arange(0,350,10) or ndarray with angles (°), optional
        :color: 
            | 'k', optional
            | Color for plotting.
        :linestyle:
            | '--', optional
            | Linestyle of circles.
        :out: 
            | None, optional
            | If None: plot circles, return (x,y) otherwise.
               
     Returns:
          :x,y:
               | ndarrays with circle coordinates (only returned if out is 'x,y')
    """
    x = np.array([0])
    y = x.copy()
    for ri in radii:
        xi = ri * np.cos(angles * np.pi / 180)
        yi = ri * np.sin(angles * np.pi / 180)
        x = np.hstack((x, xi))
        y = np.hstack((y, yi))
        if out != 'x,y':
            plt.plot(xi, yi, color=color, linestyle=linestyle)
    if out == 'x,y':
        return x, y
Esempio n. 14
0
def get_gij_fmc(Yxy, etype = 'fmc2', ellipsoid = True, Y = None, cspace = 'Yxy'):
    """
    Get gij matrices describing the discrimination ellipses/ellipsoids for Yxy or xyz using FMC-1 or FMC-2.
    
    Args:
        :Yxy:
            | 2D ndarray with [Y,]x,y coordinate centers. 
            | If Yxy.shape[-1]==2: Y is added using the value from the Y-input argument.
        :etype:
            | 'fmc2', optional
            | Type of FMC color discrimination equations to use (see references below).
            | options: 'fmc1', fmc2'
        :Y:
            | None, optional
            | Only affects FMC-2 (see note below).
            | If not None: Y = 10.69 and overrides values in Yxy. 
        :ellipsoid:
            | True, optional
            | If True: return ellipsoids, else return ellipses (only if cspace == 'Yxy')!
        :cspace:
            | 'Yxy', optional
            | Return coefficients for Yxy-ellipses/ellipsoids ('Yxy') or XYZ ellipsoids ('xyz')
    
    Note:
        1. FMC-2 is almost identical to FMC-1 is Y = 10.69!; see [2]
    
    References:
        1. Chickering, K.D. (1967), Optimization of the MacAdam-Modified 1965 Friele Color-Difference Formula, 57(4), p.537-541
        2. Chickering, K.D. (1971), FMC Color-Difference Formulas: Clarification Concerning Usage, 61(1), p.118-122
    """
    if Yxy.shape[-1] == 2:
        Yxy = np.hstack((100*np.ones((Yxy.shape[0],1)),Yxy))
    if Y is not None:
        Yxy[...,0] = Y
    xyz = Yxy_to_xyz(Yxy)
    if etype == 'fmc2':
        gij = _get_gij_fmc_2(xyz, cspace = cspace)
    else:
        gij = _get_gij_fmc_1(xyz, cspace = cspace)
    if ellipsoid == True:
        return gij
    else:
        if cspace.lower()=='xyz':
            return gij
        else:
            return gij[:,1:,1:]
Esempio n. 15
0
def crowdingdistance(F):
    """
    Computes the crowding distance of a nondominated front.
    
    | The crowding distance gives a measure of how close the individuals are
    | with regard to its neighbors. The higher this value, the greater the
    | spacing. This is used to promote better diversity in the population.

    Args:
       :F: 
           | an m x mu ndarray with mu individuals and m objectives

    Returns:
       :cdist: 
           | a m-length column vector
    """
    m, mu = F.shape #gets the size of F
    
    if mu == 2:
       cdist = np.vstack((np.inf, np.inf))
       return cdist

    
    #[Fs, Is] = sort(F,2); #sorts the objectives by individuals
    Is = F.argsort(axis = 1)
    Fs = np.sort(F,axis=1)
    
    # Creates the numerator
    C = Fs[:,2:] - Fs[:,:-2]
    C = np.hstack((np.inf*np.ones((m,1)), C, np.inf*np.ones((m,1)))) #complements with inf in the extremes
    
    # Indexing to permute the C matrix in the right ordering
    Aux = np.arange(m).repeat(mu).reshape(m,mu)   
    ind = np.ravel_multi_index((Aux.flatten(),Is.flatten()),(m, mu)) #converts to lin. indexes # ind = sub2ind([m, mu], Aux(:), Is(:));
    C2 = C.flatten().copy()
    C2[ind] = C2.flatten()
    C = C2.reshape((m, mu))

    # Constructs the denominator
    den = np.repeat((Fs[:,-1] - Fs[:,0])[:,None], mu, axis = 1)
    
    # Calculates the crowding distance
    cdist = (C/den).sum(axis=0)
    cdist = cdist.flatten() #assures a column vector
    return cdist
def getUSCensusAgeDist():
    """
    Get US Census Age Distribution
    """
    t_num = _INDVCMF_DATA['USCensus2010population']

    list_AgeCensus = t_num[0]
    freq_AgeCensus = np.round(
        t_num[1] / 1000
    )  # Reduce # of populations to manageable number, this doesn't change probability

    # Remove age < 10 and 70 < age:
    freq_AgeCensus[:10] = 0
    freq_AgeCensus[71:] = 0

    list_Age = []
    for k in range(len(list_AgeCensus)):
        list_Age = np.hstack(
            (list_Age, np.repeat(list_AgeCensus[k], freq_AgeCensus[k])))

    return list_Age
Esempio n. 17
0
def dtlz_range_(fname, M):
    """
    Returns the decision range of a DTLZ function
    
    | The range is simply [0,1] for all variables. What varies is the number 
    | of decision variables in each problem. The equation for that is
    | n = (M-1) + k
    | wherein k = 5 for DTLZ1, 10 for DTLZ2-6, and 20 for DTLZ7.
    
    Args:
        :fname: 
            | a string with the name of the function ('dtlz1', 'dtlz2' etc.)
        :M: 
            | a scalar with the number of objectives
    
       Returns:
          :lim: 
              | a n x 2 matrix wherein the first column is the lower limit 
              |(0), and the second column, the upper limit of search (1)
    """
     #Checks if the string has or not the prefix 'dtlz', or if the number later
     #is greater than 7:
    fname = fname.lower()
    if (len(fname) < 5) or (fname[:4] != 'dtlz') or (float(fname[4]) > 7) :
       raise Exception('Sorry, the function {:s} is not implemented.'.format(fname))


    # If the name is o.k., defines the value of k
    if fname ==  'dtlz1':
       k = 5
    elif fname == 'dtlz7':
       k = 20
    else: #any other function
       k = 10;

    
    n = (M-1) + k #number of decision variables
    
    lim = np.hstack((np.zeros((n,1)), np.ones((n,1))))
    return lim
Esempio n. 18
0
def generate_grid(jab_ranges = None, out = 'grid', \
                  ax = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR),\
                  bx = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR), \
                  jx = None, limit_grid_radius = 0):
    """
    Generate a grid of color coordinates.
    
    Args:
        :out:
            | 'grid' or 'vectors', optional
            |   - 'grid': outputs a single 2d numpy.nd-vector with the grid coordinates
            |   - 'vector': outputs each dimension seperately.
        :jab_ranges:
            | None or ndarray, optional
            | Specifies the pixelization of color space.
            | (ndarray.shape = (3,3), with  first axis: J,a,b, and second 
            | axis: min, max, delta)
        :ax:
            | default ndarray or user defined ndarray, optional
            | default = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR) 
        :bx:
            | default ndarray or user defined ndarray, optional
            | default = np.arange(-_VF_MAXR,_VF_MAXR+_VF_DELTAR,_VF_DELTAR) 
        :jx:
            | None, optional
            | Note that not-None :jab_ranges: override :ax:, :bx: and :jx input.
        :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:
            
    Returns:
        :returns: 
            | single ndarray with ax,bx [,jx] 
            |  or
            | seperate ndarrays for each dimension specified.
    """
    # generate grid from jab_ranges array input, otherwise use ax, bx, jx input:
    if jab_ranges is not None:
        if jab_ranges.shape[0] == 3:
            jx = np.arange(jab_ranges[0][0], jab_ranges[0][1],
                           jab_ranges[0][2])
            ax = np.arange(jab_ranges[1][0], jab_ranges[1][1],
                           jab_ranges[1][2])
            bx = np.arange(jab_ranges[2][0], jab_ranges[2][1],
                           jab_ranges[2][2])
        else:
            jx = None
            ax = np.arange(jab_ranges[0][0], jab_ranges[0][1],
                           jab_ranges[0][2])
            bx = np.arange(jab_ranges[1][0], jab_ranges[1][1],
                           jab_ranges[1][2])

    # Generate grid from (jx), ax, bx:
    Ax, Bx = np.meshgrid(ax, bx)
    grid = np.dstack((Ax, Bx))
    grid = np.reshape(grid, (np.array(grid.shape[:-1]).prod(), grid.ndim - 1))
    if jx is not None:
        for i, v in enumerate(jx):
            gridi = np.hstack((np.ones((grid.shape[0], 1)) * v, grid))
            if i == 0:
                gridwithJ = gridi
            else:
                gridwithJ = np.vstack((gridwithJ, gridi))
        grid = gridwithJ

    if jx is None:
        ax = grid[:, 0:1]
        bx = grid[:, 1:2]
    else:
        jx = grid[:, 0:1]
        ax = grid[:, 1:2]
        bx = grid[:, 2:3]

    if limit_grid_radius > 0:  # limit radius of grid:
        Cr = (ax**2 + bx**2)**0.5
        ax = ax[Cr <= limit_grid_radius, None]
        bx = bx[Cr <= limit_grid_radius, None]
        if jx is not None:
            jx = jx[Cr <= limit_grid_radius, None]

    # create output:
    if out == 'grid':
        if jx is None:
            return np.hstack((ax, bx))
        else:
            return np.hstack((jx, ax, bx))
    else:
        if jx is None:
            return ax, bx
        else:
            return jx, ax, bx
Esempio n. 19
0
def fit_ellipse(xy, center_on_mean_xy = False):
    """
    Fit an ellipse to supplied data points.

    Args:
        :xy: 
            | coordinates of points to fit (Nx2 array)
        :center_on_mean_xy:
            | False, optional
            | Center ellipse on mean of xy 
            | (otherwise it might be offset due to solving 
            | the contrained minization problem: aT*S*a, see ref below.)
            
    Returns:
        :v:
            | vector with ellipse parameters [Rmax,Rmin, xc,yc, theta (rad.)]
            
    Reference:
        1. Fitzgibbon, A.W., Pilu, M., and Fischer R.B., 
        Direct least squares fitting of ellipsees, 
        Proc. of the 13th Internation Conference on Pattern Recognition, 
        pp 253–257, Vienna, 1996.
    """
    # remove centroid:
#    center = xy.mean(axis=0)
#    xy = xy - center
    
    # Fit ellipse:
    x, y = xy[:,0:1], xy[:,1:2]
    D = np.hstack((x * x, x * y, y * y, x, y, np.ones_like(x)))
    S, C = np.dot(D.T, D), np.zeros([6, 6])
    C[0, 2], C[2, 0], C[1, 1] = 2, 2, -1
    U, s, V = np.linalg.svd(np.dot(np.linalg.inv(S), C))
    e = U[:, 0]
#    E, V =  np.linalg.eig(np.dot(np.linalg.inv(S), C))
#    n = np.argmax(np.abs(E))
#    e = V[:,n]
        
    # get ellipse axis lengths, center and orientation:
    b, c, d, f, g, a = e[1] / 2, e[2], e[3] / 2, e[4] / 2, e[5], e[0]
    
    # get ellipse center:
    num = b * b - a * c
    if num == 0:
        xc = 0
        yc = 0
    else:
        xc = ((c * d - b * f) / num) 
        yc = ((a * f - b * d) / num) 
    
    # get ellipse orientation:
    theta = np.arctan2(np.array(2 * b), np.array((a - c))) / 2
#    if b == 0:
#        if a > c:
#            theta = 0
#        else:
#            theta = np.pi/2
#    else:
#        if a > c:
#            theta = np.arctan2(2*b,(a-c))/2
#        else:
#            theta =  np.arctan2(2*b,(a-c))/2 + np.pi/2
        
    # axis lengths:
    up = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g)
    down1 = (b * b - a * c) * ((c - a) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a))
    down2 = (b * b - a * c) * ((a - c) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a))
    a, b  = np.sqrt((up / down1)), np.sqrt((up / down2))


    # assert that a is the major axis (otherwise swap and correct angle)
    if(b > a):
        b, a = a, b
        # ensure the angle is betwen 0 and 2*pi
        theta = fmod(theta, 2.0 * np.pi)
        
    if center_on_mean_xy == True:
        xc,yc = xy.mean(axis=0)

    return np.hstack((a, b, xc, yc, theta))
Esempio n. 20
0
def plot_chromaticity_diagram_colors(diagram_samples = 256, diagram_opacity = 1.0, diagram_lightness = 0.25,\
                                      cieobs = _CIEOBS, cspace = 'Yxy', cspace_pars = {},\
                                      show = True, axh = None,\
                                      show_grid = False, label_fontname = 'Times New Roman', label_fontsize = 12,\
                                      **kwargs):
    """
    Plot the chromaticity diagram colors.
    
    Args:
        :diagram_samples:
            | 256, optional
            | Sampling resolution of color space.
        :diagram_opacity:
            | 1.0, optional
            | Sets opacity of chromaticity diagram
        :diagram_lightness:
            | 0.25, optional
            | Sets lightness of chromaticity diagram
        :axh: 
            | None or axes handle, optional
            | Determines axes to plot data in.
            | None: make new figure.
        :show:
            | True or False, optional
            | Invoke matplotlib.pyplot.show() right after plotting
        :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:
        :cspace_pars:
            | {} or dict, optional
            | Dict with parameters required by color space specified in :cspace: 
            | (for use with luxpy.colortf())
        :show_grid:
            | False, optional
            | Show grid (True) or not (False)
        :label_fontname: 
            | 'Times New Roman', optional
            | Sets font type of axis labels.
        :label_fontsize:
            | 12, optional
            | Sets font size of axis labels.
        :kwargs: 
            | additional keyword arguments for use with matplotlib.pyplot.
        
    Returns:
        
    """

    if isinstance(cieobs, str):
        SL = _CMF[cieobs]['bar'][1:4].T
    else:
        SL = cieobs[1:4].T
    SL = 100.0 * SL / (SL[:, 1, None] + _EPS)
    SL = SL[SL.sum(axis=1) >
            0, :]  # avoid div by zero in xyz-to-Yxy conversion
    SL = colortf(SL, tf=cspace, tfa0=cspace_pars)
    plambdamax = SL[:, 1].argmax()
    SL = np.vstack(
        (SL[:(plambdamax + 1), :], SL[0])
    )  # add lowest wavelength data and go to max of gamut in x (there is a reversal for some cmf set wavelengths >~700 nm!)
    Y, x, y = asplit(SL)
    SL = np.vstack((x, y)).T

    # create grid for conversion to srgb
    offset = _EPS
    min_x = min(offset, x.min())
    max_x = max(1, x.max())
    min_y = min(offset, y.min())
    max_y = max(1, y.max())
    ii, jj = np.meshgrid(
        np.linspace(min_x - offset, max_x + offset, int(diagram_samples)),
        np.linspace(max_y + offset, min_y - offset, int(diagram_samples)))
    ij = np.dstack((ii, jj))
    ij[ij == 0] = offset

    ij2D = ij.reshape((diagram_samples**2, 2))
    ij2D = np.hstack((diagram_lightness * 100 * np.ones(
        (ij2D.shape[0], 1)), ij2D))
    xyz = colortf(ij2D, tf=cspace + '>xyz', tfa0=cspace_pars)

    xyz[xyz < 0] = 0
    xyz[np.isinf(xyz.sum(axis=1)), :] = np.nan
    xyz[np.isnan(xyz.sum(axis=1)), :] = offset

    srgb = xyz_to_srgb(xyz)
    srgb = srgb / srgb.max()
    srgb = srgb.reshape((diagram_samples, diagram_samples, 3))

    if show == True:
        if axh is None:
            fig = plt.figure()
            axh = fig.add_subplot(111)
        polygon = Polygon(SL, facecolor='none', edgecolor='none')
        axh.add_patch(polygon)
        image = axh.imshow(srgb,
                           interpolation='bilinear',
                           extent=(min_x, max_x, min_y - 0.05, max_y),
                           clip_path=None,
                           alpha=diagram_opacity)
        image.set_clip_path(polygon)
        axh.plot(x, y, color='darkgray')
        if (cspace == 'Yxy') & (isinstance(cieobs, str)):
            axh.set_xlim([0, 1])
            axh.set_ylim([0, 1])
        elif (cspace == 'Yuv') & (isinstance(cieobs, str)):
            axh.set_xlim([0, 0.6])
            axh.set_ylim([0, 0.6])
        if (cspace is not None):
            xlabel = _CSPACE_AXES[cspace][1]
            ylabel = _CSPACE_AXES[cspace][2]
            if (label_fontname is not None) & (label_fontsize is not None):
                axh.set_xlabel(xlabel,
                               fontname=label_fontname,
                               fontsize=label_fontsize)
                axh.set_ylabel(ylabel,
                               fontname=label_fontname,
                               fontsize=label_fontsize)

        if show_grid == True:
            axh.grid(True)
        #plt.show()

        return axh
    else:
        return None
Esempio n. 21
0
def plotellipse(v, cspace_in = 'Yxy', cspace_out = None, nsamples = 100, \
                show = True, axh = None, \
                line_color = 'darkgray', line_style = ':', line_width = 1, line_marker = '', line_markersize = 4,\
                plot_center = False, center_marker = 'o', center_color = 'darkgray', center_markersize = 4,\
                show_grid = False, llabel = '', label_fontname = 'Times New Roman', label_fontsize = 12,\
                out = None):
    """
    Plot ellipse(s) given in v-format [Rmax,Rmin,xc,yc,theta].
    
    Args:
        :v: 
            | (Nx5) ndarray
            | ellipse parameters [Rmax,Rmin,xc,yc,theta]
        :cspace_in:
            | 'Yxy', optional
            | Color space of v.
            | If None: no color space assumed. Axis labels assumed ('x','y').
        :cspace_out:
            | None, optional
            | Color space to plot ellipse(s) in.
            | If None: plot in cspace_in.
        :nsamples:
            | 100 or int, optional
            | Number of points (samples) in ellipse boundary
        :show:
            | True or boolean, optional
            | Plot ellipse(s) (True) or not (False)
        :axh: 
            | None, optional
            | Ax-handle to plot ellipse(s) in.
            | If None: create new figure with axes.
        :line_color:
            | 'darkgray', optional
            | Color to plot ellipse(s) in.
        :line_style:
            | ':', optional
            | Linestyle of ellipse(s).
        :line_width':
            | 1, optional
            | Width of ellipse boundary line.
        :line_marker:
            | 'none', optional
            | Marker for ellipse boundary.
        :line_markersize:
            | 4, optional
            | Size of markers in ellipse boundary.
        :plot_center:
            | False, optional
            | Plot center of ellipse: yes (True) or no (False)
        :center_color:
            | 'darkgray', optional
            | Color to plot ellipse center in.
        :center_marker:
            | 'o', optional
            | Marker for ellipse center.
        :center_markersize:
            | 4, optional
            | Size of marker of ellipse center.
        :show_grid:
            | False, optional
            | Show grid (True) or not (False)
        :llabel:
            | None,optional
            | Legend label for ellipse boundary.
        :label_fontname: 
            | 'Times New Roman', optional
            | Sets font type of axis labels.
        :label_fontsize:
            | 12, optional
            | Sets font size of axis labels.
        :out:
            | None, optional
            | Output of function
            | If None: returns None. Can be used to output axh of newly created
            |      figure axes or to return Yxys an ndarray with coordinates of 
            |       ellipse boundaries in cspace_out (shape = (nsamples,3,N)) 
            
        
    Returns:
        :returns: None, or whatever set by :out:.
    """
    Yxys = np.zeros((nsamples, 3, v.shape[0]))
    ellipse_vs = np.zeros((v.shape[0], 5))
    for i, vi in enumerate(v):

        # Set sample density of ellipse boundary:
        t = np.linspace(0, 2 * np.pi, int(nsamples))

        a = vi[0]  # major axis
        b = vi[1]  # minor axis
        xyc = vi[2:4, None]  # center
        theta = vi[-1]  # rotation angle

        # define rotation matrix:
        R = np.hstack((np.vstack((np.cos(theta), np.sin(theta))),
                       np.vstack((-np.sin(theta), np.cos(theta)))))

        # Calculate ellipses:
        Yxyc = np.vstack((1, xyc)).T
        Yxy = np.vstack(
            (np.ones((1, nsamples)),
             xyc + np.dot(R, np.vstack((a * np.cos(t), b * np.sin(t)))))).T
        Yxys[:, :, i] = Yxy

        # Convert to requested color space:
        if (cspace_out is not None) & (cspace_in is not None):
            Yxy = colortf(Yxy, cspace_in + '>' + cspace_out)
            Yxyc = colortf(Yxyc, cspace_in + '>' + cspace_out)
            Yxys[:, :, i] = Yxy

            # get ellipse parameters in requested color space:
            ellipse_vs[i, :] = math.fit_ellipse(Yxy[:, 1:])
            #de = np.sqrt((Yxy[:,1]-Yxyc[:,1])**2 + (Yxy[:,2]-Yxyc[:,2])**2)
            #ellipse_vs[i,:] = np.hstack((de.max(),de.min(),Yxyc[:,1],Yxyc[:,2],np.nan)) # nan because orientation is xy, but request is some other color space. Change later to actual angle when fitellipse() has been implemented

        # plot ellipses:
        if show == True:
            if (axh is None) & (i == 0):
                fig = plt.figure()
                axh = fig.add_subplot(111)

            if (cspace_in is None):
                xlabel = 'x'
                ylabel = 'y'
            else:
                xlabel = _CSPACE_AXES[cspace_in][1]
                ylabel = _CSPACE_AXES[cspace_in][2]

            if (cspace_out is not None):
                xlabel = _CSPACE_AXES[cspace_out][1]
                ylabel = _CSPACE_AXES[cspace_out][2]

            if plot_center == True:
                axh.plot(Yxyc[:, 1],
                         Yxyc[:, 2],
                         color=center_color,
                         linestyle='none',
                         marker=center_marker,
                         markersize=center_markersize)
            if llabel is None:
                axh.plot(Yxy[:, 1],
                         Yxy[:, 2],
                         color=line_color,
                         linestyle=line_style,
                         linewidth=line_width,
                         marker=line_marker,
                         markersize=line_markersize)
            else:
                axh.plot(Yxy[:, 1],
                         Yxy[:, 2],
                         color=line_color,
                         linestyle=line_style,
                         linewidth=line_width,
                         marker=line_marker,
                         markersize=line_markersize,
                         label=llabel)

            axh.set_xlabel(xlabel,
                           fontname=label_fontname,
                           fontsize=label_fontsize)
            axh.set_ylabel(ylabel,
                           fontname=label_fontname,
                           fontsize=label_fontsize)
            if show_grid == True:
                plt.grid(True)
            #plt.show()
    Yxys = np.transpose(Yxys, axes=(0, 2, 1))
    if out is not None:
        return eval(out)
    else:
        return None
Esempio n. 22
0
def plotBB(ccts=None,
           cieobs=_CIEOBS,
           cspace=_CSPACE,
           axh=None,
           cctlabels=True,
           show=True,
           cspace_pars={},
           formatstr='k-',
           **kwargs):
    """
    Plot blackbody locus.
        
    Args: 
        :ccts: 
            | None or list[float], optional
            | None defaults to [1000 to 1e19 K].
            | Range: 
            |     [1000,1500,2000,2500,3000,3500,4000,5000,6000,8000,10000] 
            |    + [15000 K to 1e19 K] in 100 steps on a log10 scale
        :cctlabels:
            | True or False, optional
            | Add cct text labels at various points along the blackbody locus.
        :axh: 
            | None or axes handle, optional
            | Determines axes to plot data in.
            | None: make new figure.
        :show:
            | True or False, optional
            | Invoke matplotlib.pyplot.show() right after plotting
        :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: 
            | None (:show: == True) 
            |  or 
            | handle to current axes (:show: == False)
    """
    if ccts is None:
        ccts1 = np.array([
            1000.0, 1500.0, 2000.0, 2500.0, 3000.0, 3500.0, 4000.0, 5000.0,
            6000.0, 8000.0, 10000.0
        ])
        ccts2 = 10**np.linspace(np.log10(15000.0), np.log10(10.0**19.0), 100)
        ccts = np.hstack((ccts1, ccts2))
    else:
        ccts1 = None

    BB = cri_ref(ccts, ref_type='BB')
    xyz = spd_to_xyz(BB, cieobs=cieobs)
    Yxy = colortf(xyz, tf=cspace, tfa0=cspace_pars)
    Y, x, y = asplit(Yxy)

    axh = plot_color_data(x,
                          y,
                          axh=axh,
                          cieobs=cieobs,
                          cspace=cspace,
                          show=show,
                          formatstr=formatstr,
                          **kwargs)

    if (cctlabels == True) & (ccts1 is not None):
        for i in range(ccts1.shape[0]):
            if ccts1[i] >= 3000.0:
                if i % 2 == 0.0:
                    axh.plot(x[i], y[i], 'k+', color='0.5')
                    axh.text(x[i] * 1.05,
                             y[i] * 0.95,
                             '{:1.0f}K'.format(ccts1[i]),
                             color='0.5')
        axh.plot(x[-1], y[-1], 'k+', color='0.5')
        axh.text(x[-1] * 1.05,
                 y[-1] * 0.95,
                 '{:1.0e}K'.format(ccts[-1]),
                 color='0.5')
    if show == False:
        return axh
Esempio n. 23
0
    axs[1].axis('off')

    px_rmse = ((hrhsi_est - hrhsi)**2).sum(axis=-1)**0.5  # rmse per pixel
    axs[2].set_title(
        'RMSE(ground-truth, estimated) HR-HSI\nRMSE = {:1.4f}, max = {:1.4f}'.
        format((px_rmse**2).mean()**0.5, px_rmse.max()))
    im = axs[2].imshow(px_rmse, cmap='jet', aspect='auto')  # rmse per pixel
    cbar = axs[2].figure.colorbar(im, ax=axs[2])
    cbar.ax.set_ylabel('RMSE', rotation=-90, va="bottom")

    psorted = np.unravel_index(
        np.argsort(px_rmse, axis=None),
        px_rmse.shape)  # index of pixels sorted by px_rmse
    np.random.seed(1)
    pxs = np.random.permutation(min(hrhsi.shape[:2]))[:12].reshape(2, 3, 2)
    iis = np.hstack((pxs[..., 0].ravel(), psorted[0][-3:]))
    jjs = np.hstack((pxs[..., 1].ravel(), psorted[1][-3:]))
    colors = np.array(['m', 'b', 'c', 'g', 'y', 'r', 'k', 'lightgrey', 'grey'])
    for t in range(len(iis)):
        ii, jj = iis[t], jjs[t]
        axs[1].plot(jj, ii, color=colors[t], marker='o', mec='w')
        axs[2].plot(jj, ii, color=colors[t], marker='o', mec='w')
        axs[3].plot(wlr,
                    hrhsi[ii, jj, :],
                    color=colors[t],
                    linestyle='-',
                    label='ground-truth (r{:1.0f},c{:1.0f})'.format(ii, jj))
        axs[3].plot(wlr,
                    hrhsi_est[ii, jj, :],
                    color=colors[t],
                    linestyle='--',
Esempio n. 24
0
def VF_colorshift_model(S, cri_type = _VF_CRI_DEFAULT, model_type = _VF_MODEL_TYPE, \
                        cspace = _VF_CSPACE, sampleset = None, pool = False, \
                        pcolorshift = {'href': np.arange(np.pi/10,2*np.pi,2*np.pi/10),'Cref' : _VF_MAXR, 'sig' : _VF_SIG}, \
                        vfcolor = 'k',verbosity = 0):
    """
    Applies full vector field model calculations to spectral data.
    
    Args:
        :S: 
            | nump.ndarray with spectral data.
        :cri_type:
            | _VF_CRI_DEFAULT or str or dict, optional
            | Specifies type of color fidelity model to use. 
            | Controls choice of ref. ill., sample set, averaging, scaling, etc.
            | See luxpy.cri.spd_to_cri for more info.
        :modeltype:
            | _VF_MODEL_TYPE or 'M6' or 'M5', optional
            | Specifies degree 5 or degree 6 polynomial model in ab-coordinates.
        :cspace:
            | _VF_CSPACE or dict, optional
            | Specifies color space. See _VF_CSPACE_EXAMPLE for example structure.
        :sampleset:
            | None or str or ndarray, optional
            | Sampleset to be used when calculating vector field model.
        :pool: 
            | False, optional
            | If :S: contains multiple spectra, True pools all jab data before 
            | modeling the vector field, while False models a different field 
            | for each spectrum.
        :pcolorshift: 
            | default dict (see below) or user defined dict, optional
            | Dict containing the specification input 
            | for apply_poly_model_at_hue_x().
            | Default dict = {'href': np.arange(np.pi/10,2*np.pi,2*np.pi/10),
            |                 'Cref' : _VF_MAXR, 
            |                 'sig' : _VF_SIG, 
            |                 'labels' : '#'} 
            | The polynomial models of degree 5 and 6 can be fully specified or 
            | summarized by the model parameters themselved OR by calculating the
            | dCoverC and dH at resp. 5 and 6 hues.
        :vfcolor:
            | 'k', optional
            | For plotting the vector fields.
        :verbosity: 
            | 0, optional
            | Report warnings or not.
            
    Returns:
        :returns: 
            | list[dict] (each list element refers to a different test SPD)
            | with the following keys:
            |   - 'Source': dict with ndarrays of the S, cct and duv of source spd.
            |   - 'metrics': dict with ndarrays for:
            |         * Rf (color fidelity: base + metameric shift)
            |         * Rt (metameric uncertainty index) 
            |         * Rfi (specific color fidelity indices)
            |         * Rti (specific metameric uncertainty indices)
            |         * cri_type (str with cri_type)
            |   - 'Jab': dict with with ndarrays for Jabt, Jabr, DEi
            |   - 'dC/C_dH_x_sig' : 
            |           np.vstack((dCoverC_x,dCoverC_x_sig,dH_x,dH_x_sig)).T
            |           See get_poly_model() for more info.
            |   - 'fielddata': dict with dicts containing data on the calculated 
            |      vector-field and circle-fields: 
            |        * 'vectorfield' : {'axt': vfaxt, 'bxt' : vfbxt, 
            |                           'axr' : vfaxr, 'bxr' : vfbxr},
            |        * 'circlefield' : {'axt': cfaxt, 'bxt' : cfbxt, 
            |                           'axr' : cfaxr, 'bxr' : cfbxr}},
            |   - 'modeldata' : dict with model info:
            |                {'pmodel': pmodel, 
            |                'pcolorshift' : pcolorshift, 
            |                  'dab_model' : dab_model, 
            |                  'dab_res' : dab_res,
            |                  'dab_std' : dab_std,
            |                  'modeltype' : modeltype, 
            |                  'fmodel' : poly_model,
            |                  'Jabtm' : Jabtm, 
            |                  'Jabrm' : Jabrm, 
            |                  'DEim' : DEim},
            |   - 'vshifts' :dict with various vector shifts:
            |        * 'Jabshiftvector_r_to_t' : ndarray with difference vectors
            |                                    between jabt and jabr.
            |        * 'vshift_ab_s' : vshift_ab_s: ab-shift vectors of samples 
            |        * 'vshift_ab_s_vf' : vshift_ab_s_vf: ab-shift vectors of 
            |                             VF model predictions of samples.
            |        * 'vshift_ab_vf' : vshift_ab_vf: ab-shift vectors of VF 
            |                            model predictions of vector field grid.
    """

    if type(cri_type) == str:
        cri_type_str = cri_type
    else:
        cri_type_str = None

    # Calculate Rf, Rfi and Jabr, Jabt:
    Rf, Rfi, Jabt, Jabr, cct, duv, cri_type = spd_to_cri(
        S,
        cri_type=cri_type,
        out='Rf,Rfi,jabt,jabr,cct,duv,cri_type',
        sampleset=sampleset)

    # In case of multiple source SPDs, pool:
    if (len(Jabr.shape) == 3) & (Jabr.shape[1] > 1) & (pool == True):
        #Nsamples = Jabr.shape[0]
        Jabr = np.transpose(Jabr, (1, 0, 2))  # set lamps on first dimension
        Jabt = np.transpose(Jabt, (1, 0, 2))
        Jabr = Jabr.reshape(Jabr.shape[0] * Jabr.shape[1],
                            3)  # put all lamp data one after the other
        Jabt = Jabt.reshape(Jabt.shape[0] * Jabt.shape[1], 3)
        Jabt = Jabt[:, None, :]  # add dim = 1
        Jabr = Jabr[:, None, :]

    out = [{} for _ in range(Jabr.shape[1])]  #initialize empty list of dicts
    if pool == False:
        N = Jabr.shape[1]
    else:
        N = 1
    for i in range(N):

        Jabr_i = Jabr[:, i, :].copy()
        Jabr_i = Jabr_i[:, None, :]
        Jabt_i = Jabt[:, i, :].copy()
        Jabt_i = Jabt_i[:, None, :]

        DEi = np.sqrt((Jabr_i[..., 0] - Jabt_i[..., 0])**2 +
                      (Jabr_i[..., 1] - Jabt_i[..., 1])**2 +
                      (Jabr_i[..., 2] - Jabt_i[..., 2])**2)

        # Determine polynomial model:
        poly_model, pmodel, dab_model, dab_res, dCHoverC_res, dab_std, dCHoverC_std = get_poly_model(
            Jabt_i, Jabr_i, modeltype=_VF_MODEL_TYPE)

        # Apply model at fixed hues:
        href = pcolorshift['href']
        Cref = pcolorshift['Cref']
        sig = pcolorshift['sig']
        dCoverC_x, dCoverC_x_sig, dH_x, dH_x_sig = apply_poly_model_at_hue_x(
            poly_model, pmodel, dCHoverC_res, hx=href, Cxr=Cref, sig=sig)

        # Calculate deshifted a,b values on original samples:
        Jt = Jabt_i[..., 0].copy()
        at = Jabt_i[..., 1].copy()
        bt = Jabt_i[..., 2].copy()
        Jr = Jabr_i[..., 0].copy()
        ar = Jabr_i[..., 1].copy()
        br = Jabr_i[..., 2].copy()
        ar = ar + dab_model[:, 0:1]  # deshift reference to model prediction
        br = br + dab_model[:, 1:2]  # deshift reference to model prediction

        Jabtm = np.hstack((Jt, at, bt))
        Jabrm = np.hstack((Jr, ar, br))

        # calculate color differences between test and deshifted ref:
        #        DEim = np.sqrt((Jr - Jt)**2 + (at - ar)**2 + (bt - br)**2)
        DEim = np.sqrt(0 * (Jr - Jt)**2 + (at - ar)**2 +
                       (bt - br)**2)  # J is not used

        # Apply scaling function to convert DEim to Rti:
        scale_factor = cri_type['scale']['cfactor']
        scale_fcn = cri_type['scale']['fcn']
        avg = cri_type['avg']
        Rfi_deshifted = scale_fcn(DEim, scale_factor)
        Rf_deshifted = scale_fcn(avg(DEim, axis=0), scale_factor)

        rms = lambda x: np.sqrt(np.sum(x**2, axis=0) / x.shape[0])
        Rf_deshifted_rms = scale_fcn(rms(DEim), scale_factor)

        # Generate vector field:
        vfaxt, vfbxt, vfaxr, vfbxr = 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),
            limit_grid_radius=_VF_MAXR,
            color=0)
        vfaxt, vfbxt, vfaxr, vfbxr = 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),
            limit_grid_radius=_VF_MAXR,
            color=0)

        # Calculate ab-shift vectors of samples and VF model predictions:
        vshift_ab_s = calculate_shiftvectors(Jabt_i,
                                             Jabr_i,
                                             average=False,
                                             vtype='ab')[:, 0, 0:3]
        vshift_ab_s_vf = calculate_shiftvectors(Jabtm,
                                                Jabrm,
                                                average=False,
                                                vtype='ab')

        # Calculate ab-shift vectors using vector field model:
        Jabt_vf = np.hstack((np.zeros((vfaxt.shape[0], 1)), vfaxt, vfbxt))
        Jabr_vf = np.hstack((np.zeros((vfaxr.shape[0], 1)), vfaxr, vfbxr))
        vshift_ab_vf = calculate_shiftvectors(Jabt_vf,
                                              Jabr_vf,
                                              average=False,
                                              vtype='ab')

        # Generate circle field:
        x, y = plotcircle(radii=np.arange(0, _VF_MAXR + _VF_DELTAR, 10),
                          angles=np.arange(0, 359, 1),
                          out='x,y')
        cfaxt, cfbxt, cfaxr, cfbxr = generate_vector_field(
            poly_model,
            pmodel,
            make_grid=False,
            axr=x[:, None],
            bxr=y[:, None],
            limit_grid_radius=_VF_MAXR,
            color=0)

        out[i] = {
            'Source': {
                'S': S,
                'cct': cct[i],
                'duv': duv[i]
            },
            'metrics': {
                'Rf': Rf[:, i],
                'Rt': Rf_deshifted,
                'Rt_rms': Rf_deshifted_rms,
                'Rfi': Rfi[:, i],
                'Rti': Rfi_deshifted,
                'cri_type': cri_type_str
            },
            'Jab': {
                'Jabt': Jabt_i,
                'Jabr': Jabr_i,
                'DEi': DEi
            },
            'dC/C_dH_x_sig':
            np.vstack((dCoverC_x, dCoverC_x_sig, dH_x, dH_x_sig)).T,
            'fielddata': {
                'vectorfield': {
                    'axt': vfaxt,
                    'bxt': vfbxt,
                    'axr': vfaxr,
                    'bxr': vfbxr
                },
                'circlefield': {
                    'axt': cfaxt,
                    'bxt': cfbxt,
                    'axr': cfaxr,
                    'bxr': cfbxr
                }
            },
            'modeldata': {
                'pmodel': pmodel,
                'pcolorshift': pcolorshift,
                'dab_model': dab_model,
                'dab_res': dab_res,
                'dab_std': dab_std,
                'model_type': model_type,
                'fmodel': poly_model,
                'Jabtm': Jabtm,
                'Jabrm': Jabrm,
                'DEim': DEim
            },
            'vshifts': {
                'Jabshiftvector_r_to_t': np.hstack(
                    (Jt - Jr, at - ar, bt - br)),
                'vshift_ab_s': vshift_ab_s,
                'vshift_ab_s_vf': vshift_ab_s_vf,
                'vshift_ab_vf': vshift_ab_vf
            }
        }

    return out
Esempio n. 25
0
def apply_poly_model_at_hue_x(poly_model, pmodel, dCHoverC_res, \
                              hx = None, Cxr = 40, sig = _VF_SIG):
    """
    Applies base color shift model at (hue,chroma) coordinates
    
    Args:
        :poly_model: 
            | function handle to model
        :pmodel:
            | ndarray with model parameters.
        :dCHoverC_res:
            | ndarray with residuals between 'dCoverC,dH' of samples 
            | and 'dCoverC,dH' predicted by the model.
            | Note: dCoverC = (Ct - Cr)/Cr and dH = ht - hr 
            |      (predicted from model, see notes luxpy.cri.get_poly_model())
        :hx:
            | None or ndarray, optional
            | None defaults to np.arange(np.pi/10.0,2*np.pi,2*np.pi/10.0)
        :Cxr:
            | 40, optional
        :sig: 
            | _VF_SIG or float, optional
            | Determines smooth transition between hue-bin-boundaries (no hard 
            | cutoff at hue bin boundary).
        
    Returns:
        :returns: 
            | ndarrays with dCoverC_x, dCoverC_x_sig, dH_x, dH_x_sig
            | Note '_sig' denotes the uncertainty: 
            |     e.g.  dH_x_sig is the uncertainty of dH at input (hue/chroma).
    """

    if hx is None:
        dh = 2 * np.pi / 10.0
        hx = np.arange(
            dh / 2, 2 * np.pi, dh
        )  #hue angles at which to apply model, i.e. calculate 'average' measures

    # A calculate reference coordinates:
    axr = Cxr * np.cos(hx)
    bxr = Cxr * np.sin(hx)

    # B apply model at reference coordinates to obtain test coordinates:
    axt, bxt, Cxt, hxt, axr, bxr, Cxr, hxr = apply_poly_model_at_x(
        poly_model, pmodel, axr, bxr)

    # C Calculate dC/C, dH for test and ref at fixed hues:
    dCoverC_x = (Cxt - Cxr) / (np.hstack((Cxr + Cxt)).max())
    dH_x = (180 / np.pi) * (hxt - hxr)
    #    dCoverC_x = np.round(dCoverC_x,decimals = 2)
    #    dH_x = np.round(dH_x,decimals = 0)

    # D calculate 'average' noise measures using sig-value:
    href = dCHoverC_res[:, 0:1]
    dCoverC_res = dCHoverC_res[:, 1:2]
    dHoverC_res = dCHoverC_res[:, 2:3]
    dHsigi = np.exp((np.dstack(
        (np.abs(hx - href), np.abs((hx - href - 2 * np.pi)),
         np.abs(hx - href - 2 * np.pi))).min(axis=2)**2) / (-2) / sig)
    dH_x_sig = (180 / np.pi) * (np.sqrt(
        (dHsigi * (dHoverC_res**2)).sum(axis=0, keepdims=True) /
        dHsigi.sum(axis=0, keepdims=True)))
    #dH_x_sig_avg = np.sqrt(np.sum(dH_x_sig**2,axis=1)/hx.shape[0])
    dCoverC_x_sig = (np.sqrt(
        (dHsigi * (dCoverC_res**2)).sum(axis=0, keepdims=True) /
        dHsigi.sum(axis=0, keepdims=True)))
    #dCoverC_x_sig_avg = np.sqrt(np.sum(dCoverC_x_sig**2,axis=1)/hx.shape[0])

    return dCoverC_x, dCoverC_x_sig, dH_x, dH_x_sig
Esempio n. 26
0
def get_poly_model(jabt, jabr, modeltype=_VF_MODEL_TYPE):
    """
    Setup base color shift model (delta_a, delta_b), 
    determine model parameters and accuracy.
    
    | Calculates a base color shift (delta) from the ref. chromaticity ar, br.
    
    Args:
        :jabt: 
            | ndarray with jab color coordinates under the test SPD.
        :jabr: 
            | ndarray with jab color coordinates under the reference SPD.
        :modeltype:
            | _VF_MODEL_TYPE or 'M6' or 'M5', optional
            | Specifies degree 5 or degree 6 polynomial model in ab-coordinates.
            | (see notes below)
            
    Returns:
        :returns: 
            | (poly_model, 
            |       pmodel, 
            |       dab_model, 
            |        dab_res, 
            |        dCHoverC_res, 
            |        dab_std, 
            |        dCHoverC_std)
            |
            | :poly_model: function handle to model
            | :pmodel: ndarray with model parameters
            | :dab_model: ndarray with ab model predictions from ar, br.
            | :dab_res: ndarray with residuals between 'da,db' of samples and 
            |            'da,db' predicted by the model.
            | :dCHoverC_res: ndarray with residuals between 'dCoverC,dH' 
            |                 of samples and 'dCoverC,dH' predicted by the model.
            |     Note: dCoverC = (Ct - Cr)/Cr and dH = ht - hr 
            |         (predicted from model, see notes below)
            | :dab_std: ndarray with std of :dab_res:
            | :dCHoverC_std: ndarray with std of :dCHoverC_res: 

    Notes: 
        1. Model types:
            | poly5_model = lambda a,b,p:         p[0]*a + p[1]*b + p[2]*(a**2) + p[3]*a*b + p[4]*(b**2)
            | poly6_model = lambda a,b,p:  p[0] + p[1]*a + p[2]*b + p[3]*(a**2) + p[4]*a*b + p[5]*(b**2)
        
        2. Calculation of dCoverC and dH:
            | dCoverC = (np.cos(hr)*da + np.sin(hr)*db)/Cr
            | dHoverC = (np.cos(hr)*db - np.sin(hr)*da)/Cr    
    """
    at = jabt[..., 1]
    bt = jabt[..., 2]
    ar = jabr[..., 1]
    br = jabr[..., 2]

    # A. Calculate da, db:
    da = at - ar
    db = bt - br

    # B.1 Calculate model matrix:
    # 5-parameter model:
    M5 = np.array([[
        np.sum(ar * ar),
        np.sum(ar * br),
        np.sum(ar * ar**2),
        np.sum(ar * ar * br),
        np.sum(ar * br**2)
    ],
                   [
                       np.sum(br * ar),
                       np.sum(br * br),
                       np.sum(br * ar**2),
                       np.sum(br * ar * br),
                       np.sum(br * br**2)
                   ],
                   [
                       np.sum((ar**2) * ar),
                       np.sum((ar**2) * br),
                       np.sum((ar**2) * ar**2),
                       np.sum((ar**2) * ar * br),
                       np.sum((ar**2) * br**2)
                   ],
                   [
                       np.sum(ar * br * ar),
                       np.sum(ar * br * br),
                       np.sum(ar * br * ar**2),
                       np.sum(ar * br * ar * br),
                       np.sum(ar * br * br**2)
                   ],
                   [
                       np.sum((br**2) * ar),
                       np.sum((br**2) * br),
                       np.sum((br**2) * ar**2),
                       np.sum((br**2) * ar * br),
                       np.sum((br**2) * br**2)
                   ]])
    #6-parameters model
    M6 = np.array([[
        ar.size,
        np.sum(1.0 * ar),
        np.sum(1.0 * br),
        np.sum(1.0 * ar**2),
        np.sum(1.0 * ar * br),
        np.sum(1.0 * br**2)
    ],
                   [
                       np.sum(ar * 1.0),
                       np.sum(ar * ar),
                       np.sum(ar * br),
                       np.sum(ar * ar**2),
                       np.sum(ar * ar * br),
                       np.sum(ar * br**2)
                   ],
                   [
                       np.sum(br * 1.0),
                       np.sum(br * ar),
                       np.sum(br * br),
                       np.sum(br * ar**2),
                       np.sum(br * ar * br),
                       np.sum(br * br**2)
                   ],
                   [
                       np.sum((ar**2) * 1.0),
                       np.sum((ar**2) * ar),
                       np.sum((ar**2) * br),
                       np.sum((ar**2) * ar**2),
                       np.sum((ar**2) * ar * br),
                       np.sum((ar**2) * br**2)
                   ],
                   [
                       np.sum(ar * br * 1.0),
                       np.sum(ar * br * ar),
                       np.sum(ar * br * br),
                       np.sum(ar * br * ar**2),
                       np.sum(ar * br * ar * br),
                       np.sum(ar * br * br**2)
                   ],
                   [
                       np.sum((br**2) * 1.0),
                       np.sum((br**2) * ar),
                       np.sum((br**2) * br),
                       np.sum((br**2) * ar**2),
                       np.sum((br**2) * ar * br),
                       np.sum((br**2) * br**2)
                   ]])

    # B.2 Define model function:
    poly5_model = lambda a, b, p: p[0] * a + p[1] * b + p[2] * (a**2) + p[
        3] * a * b + p[4] * (b**2)
    poly6_model = lambda a, b, p: p[0] + p[1] * a + p[2] * b + p[3] * (
        a**2) + p[4] * a * b + p[5] * (b**2)

    if modeltype == 'M5':
        M = M5
        poly_model = poly5_model
    else:
        M = M6
        poly_model = poly6_model

    M = np.linalg.inv(M)

    # C.1 Data a,b analysis output:
    if modeltype == 'M5':
        da_model_parameters = np.dot(
            M,
            np.array([
                np.sum(da * ar),
                np.sum(da * br),
                np.sum(da * ar**2),
                np.sum(da * ar * br),
                np.sum(da * br**2)
            ]))
        db_model_parameters = np.dot(
            M,
            np.array([
                np.sum(db * ar),
                np.sum(db * br),
                np.sum(db * ar**2),
                np.sum(db * ar * br),
                np.sum(db * br**2)
            ]))
    else:
        da_model_parameters = np.dot(
            M,
            np.array([
                np.sum(da * 1.0),
                np.sum(da * ar),
                np.sum(da * br),
                np.sum(da * ar**2),
                np.sum(da * ar * br),
                np.sum(da * br**2)
            ]))
        db_model_parameters = np.dot(
            M,
            np.array([
                np.sum(db * 1.0),
                np.sum(db * ar),
                np.sum(db * br),
                np.sum(db * ar**2),
                np.sum(db * ar * br),
                np.sum(db * br**2)
            ]))
    pmodel = np.vstack((da_model_parameters, db_model_parameters))

    # D.1 Calculate model da, db:
    da_model = poly_model(ar, br, pmodel[0])
    db_model = poly_model(ar, br, pmodel[1])
    dab_model = np.hstack((da_model, db_model))

    # D.2 Calculate residuals for da & db:
    da_res = da - da_model
    db_res = db - db_model
    dab_res = np.hstack((da_res, db_res))
    dab_std = np.vstack((np.std(da_res, axis=0), np.std(db_res, axis=0)))

    # E Calculate href, Cref:
    href = np.arctan2(br, ar)
    Cref = (ar**2 + br**2)**0.5

    # F Calculate dC/C, dH/C for data and model and calculate residuals:
    dCoverC = (np.cos(href) * da + np.sin(href) * db) / Cref
    dHoverC = (np.cos(href) * db - np.sin(href) * da) / Cref
    dCoverC_model = (np.cos(href) * da_model + np.sin(href) * db_model) / Cref
    dHoverC_model = (np.cos(href) * db_model - np.sin(href) * da_model) / Cref
    dCoverC_res = dCoverC - dCoverC_model
    dHoverC_res = dHoverC - dHoverC_model
    dCHoverC_std = np.vstack((np.std(dCoverC_res,
                                     axis=0), np.std(dHoverC_res, axis=0)))

    dCHoverC_res = np.hstack((href, dCoverC_res, dHoverC_res))

    return poly_model, pmodel, dab_model, dab_res, dCHoverC_res, dab_std, dCHoverC_std
Esempio n. 27
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)
Esempio n. 28
0
def plot_shift_data(data, fieldtype = 'vectorfield', scalef = _VF_MAXR, color = 'k', \
                    axtype = 'polar', ax = None, \
                    hbins = 10,  start_hue = 0.0, bin_labels = '#', plot_center_lines = True,  \
                    plot_axis_labels = False, plot_edge_lines = False, plot_bin_colors = True, \
                    force_CVG_layout = True):
    """
    Plots vector or circle fields generated by VFcolorshiftmodel() 
    or PXcolorshiftmodel().
     
    Args:
        :data: 
            | dict generated by VFcolorshiftmodel() or PXcolorshiftmodel()
            | Must contain 'fielddata'- key, which is a dict with possible keys:
            |     - key: 'vectorfield': ndarray with vector field data
            |     - key: 'circlefield': ndarray with circle field data
        :color: 
            | 'k', optional
            | Color for plotting the vector-fields.
        :axtype:
            | 'polar' or 'cart', optional
            | Make polar or Cartesian plot.
        :ax: 
            | None or 'new' or 'same', optional
            |   - None or 'new' creates new plot
            |   - 'same': continue plot on same axes.
            |   - axes handle: plot on specified axes.
        :hbins:
            | 16 or ndarray with sorted hue bin centers (°), optional
        :start_hue:
            | _VF_MAXR, optional
            | Scale factor for graphic.
        :plot_axis_labels:
            | False, optional
            | Turns axis ticks on/off (True/False).
        :bin_labels:
            | None or list[str] or '#', optional
            | Plots labels at the bin center hues.
            |   - None: don't plot.
            |   - list[str]: list with str for each bin. 
            |                (len(:bin_labels:) = :nhbins:)
            |   - '#': plots number.
        :plot_edge_lines:
            | True or False, optional
            | Plot grey bin edge lines with '--'.
        :plot_center_lines:
            | False or True, optional
            | Plot colored lines at 'center' of hue bin.
        :plot_bin_colors:
            | True, optional
            | Colorize hue-bins.
        :force_CVG_layout: 
            | False or True, optional
            | True: Force plot of basis of CVG.
    
    Returns:
        :returns:
            | figCVG, hax, cmap
          
            |   :figCVG: handle to CVG figure
            |   :hax: handle to CVG axes
            |   :cmap: list with rgb colors for hue bins 
            |          (for use in other plotting fcns)
   
    """

    # Plot basis of CVG:
    figCVG, hax, cmap = plot_hue_bins(hbins=hbins,
                                      axtype=axtype,
                                      ax=ax,
                                      plot_center_lines=plot_center_lines,
                                      plot_edge_lines=plot_edge_lines,
                                      plot_bin_colors=plot_bin_colors,
                                      scalef=scalef,
                                      force_CVG_layout=force_CVG_layout,
                                      bin_labels=bin_labels)

    # plot vector field:
    if data is not None:
        if fieldtype is not None:
            vf = data['fielddata'][fieldtype]
            if axtype == 'polar':
                if fieldtype == 'vectorfield':
                    vfrtheta = math.positive_arctan(vf['axr'],
                                                    vf['bxr'],
                                                    htype='rad')
                    vfrr = np.sqrt(vf['axr']**2 + vf['bxr']**2)
                    hax.quiver(vfrtheta,
                               vfrr,
                               vf['axt'] - vf['axr'],
                               vf['bxt'] - vf['bxr'],
                               headlength=3,
                               color=color,
                               angles='uv',
                               scale_units='y',
                               scale=2,
                               linewidth=0.5)
                else:
                    vfttheta = math.positive_arctan(vf['axt'],
                                                    vf['bxt'],
                                                    htype='rad')
                    vfrtheta = math.positive_arctan(vf['axr'],
                                                    vf['bxr'],
                                                    htype='rad')
                    vftr = np.sqrt(vf['axt']**2 + vf['bxt']**2)
                    dh = (math.angle_v1v2(np.hstack((vf['axt'], vf['bxt'])),
                                          np.hstack((vf['axr'], vf['bxr'])),
                                          htype='deg')[:, None])  #hue shift
                    dh = dh / np.nanmax(dh)
                    plt.set_cmap('jet')
                    hax.scatter(vfttheta,
                                vftr,
                                s=100 * dh,
                                c=dh,
                                linestyle='None',
                                marker='o',
                                norm=None)
                hax.set_ylim([0, 1.1 * scalef])
            else:
                if fieldtype == 'vectorfield':
                    hax.quiver(vf['axr'],
                               vf['bxr'],
                               vf['axt'] - vf['axr'],
                               vf['bxt'] - vf['bxr'],
                               headlength=1,
                               color=color,
                               angles='uv',
                               scale_units='xy',
                               scale=1,
                               linewidth=0.5)
                else:
                    hax.plot(vf['axr'],
                             vf['bxr'],
                             color=color,
                             marker='.',
                             linestyle='None')

    return figCVG, hax, cmap
Esempio n. 29
0
def get_pixel_coordinates(jab,
                          jab_ranges=None,
                          jab_deltas=None,
                          limit_grid_radius=0):
    """
    Get pixel coordinates corresponding to array of jab color coordinates.
    
    Args:
        :jab: 
            | ndarray of color coordinates
        :jab_ranges:
            | None or ndarray, optional
            | Specifies the pixelization of color space.
            |    (ndarray.shape = (3,3), with  first axis: J,a,b, and second 
                 axis: min, max, delta)
        :jab_deltas:
            | float or ndarray, optional
            | Specifies the sampling range. 
            | A float uses jab_deltas as the maximum Euclidean distance to select
            | samples around each pixel center. A ndarray of 3 deltas, uses
            | a city block sampling around each pixel center.
        :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: 
    
    Returns:
        :returns:
            | gridp, idxp, jabp, samplenrs, samplesIDs
            |   - :gridp: ndarray with coordinates of all pixel centers.
            |   - :idxp: list[int] with pixel index for each non-empty pixel
            |   - :jabp: ndarray with center color coordinates of non-empty pixels
            |   - :samplenrs: list[list[int]] with sample numbers belong to each 
            |                 non-empty pixel
            |   - :sampleIDs: summarizing list, 
            |                 with column order: 'idxp, jabp, samplenrs'
    """
    if jab_deltas is None:
        jab_deltas = np.array([_VF_DELTAR, _VF_DELTAR, _VF_DELTAR])
    if jab_ranges is None:
        jab_ranges = np.vstack(
            ([0, 100, jab_deltas[0]
              ], [-_VF_MAXR, _VF_MAXR + jab_deltas[1], jab_deltas[1]],
             [-_VF_MAXR, _VF_MAXR + jab_deltas[2], jab_deltas[2]]))

    # Get pixel grid:
    gridp = generate_grid(jab_ranges=jab_ranges,
                          limit_grid_radius=limit_grid_radius)

    # determine pixel coordinates of each sample in jab:
    samplesIDs = []
    for idx in range(gridp.shape[0]):

        # get pixel coordinates:
        jp = gridp[idx, 0]
        ap = gridp[idx, 1]
        bp = gridp[idx, 2]
        #Cp = np.sqrt(ap**2+bp**2)

        if type(jab_deltas) == np.ndarray:
            sampleID = np.where(
                ((np.abs(jab[..., 0] - jp) <= jab_deltas[0] / 2) &
                 (np.abs(jab[..., 1] - ap) <= jab_deltas[1] / 2) &
                 (np.abs(jab[..., 2] - bp) <= jab_deltas[2] / 2)))
        else:
            sampleID = np.where(
                (np.sqrt((jab[..., 0] - jp)**2 + (jab[..., 1] - ap)**2 +
                         (jab[..., 2] - bp)**2) <= jab_deltas / 2))

        if (sampleID[0].shape[0] > 0):
            samplesIDs.append(
                np.hstack((idx, np.array([jp, ap, bp]), sampleID[0])))

    idxp = [np.int(samplesIDs[i][0]) for i in range(len(samplesIDs))]
    jabp = np.vstack([samplesIDs[i][1:4] for i in range(len(samplesIDs))])
    samplenrs = [
        np.array(samplesIDs[i][4:], dtype=int).tolist()
        for i in range(len(samplesIDs))
    ]

    return gridp, idxp, jabp, samplenrs, samplesIDs
def cie2006cmfsEx(age = 32,fieldsize = 10, wl = None,\
                  var_od_lens = 0, var_od_macula = 0, \
                  var_od_L = 0, var_od_M = 0, var_od_S = 0,\
                  var_shft_L = 0, var_shft_M = 0, var_shft_S = 0,\
                  out = 'LMS', allow_negative_values = False):
    """
    Generate Individual Observer CMFs (cone fundamentals) 
    based on CIE2006 cone fundamentals and published literature 
    on observer variability in color matching and in physiological parameters.
    
    Args:
        :age: 
            | 32 or float or int, optional
            | Observer age
        :fieldsize:
            | 10, optional
            | Field size of stimulus in degrees (between 2° and 10°).
        :wl: 
            | None, optional
            | Interpolation/extraplation of :LMS: output to specified wavelengths.
            | None: output original _WL = np.array([390,780,5])
        :var_od_lens:
            | 0, optional
            | Std Dev. in peak optical density [%] of lens.
        :var_od_macula:
            | 0, optional
            | Std Dev. in peak optical density [%] of macula.
        :var_od_L:
            | 0, optional
            | Std Dev. in peak optical density [%] of L-cone.
        :var_od_M:
            | 0, optional
            | Std Dev. in peak optical density [%] of M-cone.
        :var_od_S:
            | 0, optional
            | Std Dev. in peak optical density [%] of S-cone.
        :var_shft_L:
            | 0, optional
            | Std Dev. in peak wavelength shift [nm] of L-cone. 
        :var_shft_L:
            | 0, optional
            | Std Dev. in peak wavelength shift [nm] of M-cone.  
        :var_shft_S:
            | 0, optional
            | Std Dev. in peak wavelength shift [nm] of S-cone. 
        :out: 
            | 'LMS' or , optional
            | Determines output.
        :allow_negative_values:
            | False, optional
            | Cone fundamentals or color matching functions 
              should not have negative values.
            |     If False: X[X<0] = 0.
            
    Returns:
        :returns: 
            | - 'LMS' : ndarray with individual observer area-normalized 
            |           cone fundamentals. Wavelength have been added.
                
            | [- 'trans_lens': ndarray with lens transmission 
            |      (no wavelengths added, no interpolation)
            |  - 'trans_macula': ndarray with macula transmission 
            |      (no wavelengths added, no interpolation)
            |  - 'sens_photopig' : ndarray with photopigment sens. 
            |      (no wavelengths added, no interpolation)]
            
    References:
         1. `Asano Y, Fairchild MD, and Blondé L (2016). 
         Individual Colorimetric Observer Model. 
         PLoS One 11, 1–19. 
         <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0145671>`_
        
         2. `Asano Y, Fairchild MD, Blondé L, and Morvan P (2016). 
         Color matching experiment for highlighting interobserver variability. 
         Color Res. Appl. 41, 530–539. 
         <https://onlinelibrary.wiley.com/doi/abs/10.1002/col.21975>`_
         
         3. `CIE, and CIE (2006). 
         Fundamental Chromaticity Diagram with Physiological Axes - Part I 
         (Vienna: CIE). 
         <http://www.cie.co.at/publications/fundamental-chromaticity-diagram-physiological-axes-part-1>`_ 
         
         4. `Asano's Individual Colorimetric Observer Model 
         <https://www.rit.edu/cos/colorscience/re_AsanoObserverFunctions.php>`_
    """
    fs = fieldsize
    rmd = _INDVCMF_DATA['rmd'].copy()
    LMSa = _INDVCMF_DATA['LMSa'].copy()
    docul = _INDVCMF_DATA['docul'].copy()

    # field size corrected macular density:
    pkOd_Macula = 0.485 * np.exp(-fs / 6.132) * (
        1 + var_od_macula / 100)  # varied peak optical density of macula
    corrected_rmd = rmd * pkOd_Macula

    # age corrected lens/ocular media density:
    if (age <= 60):
        correct_lomd = docul[:1] * (1 + 0.02 * (age - 32)) + docul[1:2]
    else:
        correct_lomd = docul[:1] * (1.56 + 0.0667 * (age - 60)) + docul[1:2]
    correct_lomd = correct_lomd * (1 + var_od_lens / 100
                                   )  # varied overall optical density of lens

    # Peak Wavelength Shift:
    wl_shifted = np.empty(LMSa.shape)
    wl_shifted[0] = _WL + var_shft_L
    wl_shifted[1] = _WL + var_shft_M
    wl_shifted[2] = _WL + var_shft_S

    LMSa_shft = np.empty(LMSa.shape)
    kind = 'cubic'
    LMSa_shft[0] = sp.interpolate.interp1d(wl_shifted[0],
                                           LMSa[0],
                                           kind=kind,
                                           bounds_error=False,
                                           fill_value="extrapolate")(_WL)
    LMSa_shft[1] = sp.interpolate.interp1d(wl_shifted[1],
                                           LMSa[1],
                                           kind=kind,
                                           bounds_error=False,
                                           fill_value="extrapolate")(_WL)
    LMSa_shft[2] = sp.interpolate.interp1d(wl_shifted[2],
                                           LMSa[2],
                                           kind=kind,
                                           bounds_error=False,
                                           fill_value="extrapolate")(_WL)
    #    LMSa[2,np.where(_WL >= _WL_CRIT)] = 0 #np.nan # Not defined above 620nm
    #    LMSa_shft[2,np.where(_WL >= _WL_CRIT)] = 0

    ssw = np.hstack(
        (0, np.sign(np.diff(LMSa_shft[2, :]))
         ))  #detect poor interpolation (sign switch due to instability)
    LMSa_shft[2, np.where((ssw >= 0) & (_WL > 560))] = np.nan

    # corrected LMS (no age correction):
    pkOd_L = (0.38 + 0.54 * np.exp(-fs / 1.333)) * (
        1 + var_od_L / 100)  # varied peak optical density of L-cone
    pkOd_M = (0.38 + 0.54 * np.exp(-fs / 1.333)) * (
        1 + var_od_M / 100)  # varied peak optical density of M-cone
    pkOd_S = (0.30 + 0.45 * np.exp(-fs / 1.333)) * (
        1 + var_od_S / 100)  # varied peak optical density of S-cone

    alpha_lms = 0. * LMSa_shft
    alpha_lms[0] = 1 - 10**(-pkOd_L * (10**LMSa_shft[0]))
    alpha_lms[1] = 1 - 10**(-pkOd_M * (10**LMSa_shft[1]))
    alpha_lms[2] = 1 - 10**(-pkOd_S * (10**LMSa_shft[2]))

    # this fix is required because the above math fails for alpha_lms[2,:]==0
    alpha_lms[2, np.where(_WL >= _WL_CRIT)] = 0

    # Corrected to Corneal Incidence:
    lms_barq = alpha_lms * (10**(-corrected_rmd - correct_lomd)) * np.ones(
        alpha_lms.shape)

    # Corrected to Energy Terms:
    lms_bar = lms_barq * _WL

    # Set NaN values to zero:
    lms_bar[np.isnan(lms_bar)] = 0

    # normalized:
    LMS = 100 * lms_bar / np.nansum(lms_bar, axis=1, keepdims=True)

    # Output extra:
    trans_lens = 10**(-correct_lomd)
    trans_macula = 10**(-corrected_rmd)
    sens_photopig = alpha_lms * _WL

    # Add wavelengths:
    LMS = np.vstack((_WL, LMS))

    if ('xyz' in out.lower().split(',')):
        LMS = lmsb_to_xyzb(LMS,
                           fieldsize,
                           out='xyz',
                           allow_negative_values=allow_negative_values)
        out = out.replace('xyz', 'LMS').replace('XYZ', 'LMS')
    if ('lms' in out.lower().split(',')):
        out = out.replace('lms', 'LMS')

    # Interpolate/extrapolate:
    if wl is None:
        interpolation = None
    else:
        interpolation = 'cubic'
    LMS = spd(LMS, wl=wl, interpolation=interpolation, norm_type='area')

    if (out == 'LMS'):
        return LMS
    elif (out == 'LMS,trans_lens,trans_macula,sens_photopig'):
        return LMS, trans_lens, trans_macula, sens_photopig
    elif (out == 'LMS,trans_lens,trans_macula,sens_photopig,LMSa'):
        return LMS, trans_lens, trans_macula, sens_photopig, LMSa
    else:
        return eval(out)