コード例 #1
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
コード例 #2
0
ファイル: CDATA.py プロジェクト: simongr2/luxpy
    def plot(self, plt_type='3d', ax=None, title=None, **kwargs):
        """
        Plot color coordinates.
        
        Args:
            :plt_type: 
                | '3d' or 3 or '2d or 2, optional
                |   -'3d' or 3: plot all 3 dimensions (lightness and chromaticity)
                |   -'2d' or 2: plot only chromaticity dimensions.
            :ax: 
                | None or axes handles, optional
                | None: create new figure axes, else use :ax: for plotting.
            :title: 
                | None or str, optional
                | Give plot a title.
            :**kwargs:
                | additional arguments for use with 
                | matplotlib.pyplot.scatter
                
        Returns:
            :gca: 
                | handle to current axes.
        """
        L, a, b = self.split_()
        if ax is None:
            fig = plt.figure()

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

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

        return plt.gca()
コード例 #3
0
ファイル: CDATA.py プロジェクト: simongr2/luxpy
    def plot(self, ax=None, title=None, **kwargs):
        """
        Plot tristimulus or cone fundamental values.
        
        Args:
            :ax: 
                | None or axes handles, optional
                | None: create new figure axes, else use :ax: for plotting.
            :title:
                | None or str, optional
                | Give plot a title.
            :**kwargs: 
                | additional arguments for use with 
                | matplotlib.pyplot.scatter
                
        Returns:
            :gca:
                | handle to current axes.
        """
        X, Y, Z = self.split_()
        if ax is None:
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
        if self.dtype == 'xyz':
            ax.scatter(X, Z, Y, **kwargs)
            ax.set_xlabel(_CSPACE_AXES[self.dtype][0])
            ax.set_ylabel(_CSPACE_AXES[self.dtype][2])
            ax.set_zlabel(_CSPACE_AXES[self.dtype][1])
        elif self.dtype == 'lms':
            ax.scatter(X, Y, Z, **kwargs)
            ax.set_xlabel(_CSPACE_AXES[self.dtype][0])
            ax.set_ylabel(_CSPACE_AXES[self.dtype][1])
            ax.set_zlabel(_CSPACE_AXES[self.dtype][2])
        if title is not None:
            ax.set_title(title)

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

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

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

    wlr = rfl[0]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if verbosity > 0:
        if CSF is None:
            ax = plot_color_data(lab[...,1], lab[...,2], z = lab[...,0], \
                            show = False, cieobs = cieobs, cspace = cspace, \
                            formatstr = 'ro', label = 'Original')
            plot_color_data(lab_est[...,1], lab_est[...,2], z = lab_est[...,0], \
                            show = True, axh = ax, cieobs = cieobs, cspace = cspace, \
                            formatstr = 'bd', label = 'Rendered')
        else:
            n = 100  #min(rfl.shape[0]-1,rfl_est.shape[0]-1)
            s = np.random.permutation(rfl.shape[0] -
                                      1)[:min(n, rfl.shape[0] - 1)]
            st = np.random.permutation(rfl_est.shape[0] -
                                       1)[:min(n, rfl_est.shape[0] - 1)]
            fig = plt.figure()
            ax = np.zeros((3, ), dtype=np.object)
            ax[0] = fig.add_subplot(131)
            ax[1] = fig.add_subplot(132)
            ax[2] = fig.add_subplot(133, projection='3d')
            ax[0].plot(rfl[0], rfl[1:][s].T, linestyle='-')
            ax[0].set_title('Original RFL set (random selection of all)')
            ax[0].set_ylim([0, 1])
            ax[1].plot(rfl_est[0], rfl_est[1:][st].T, linestyle='--')
            ax[0].set_title('Estimated RFL set (random selection of targets)')
            ax[1].set_ylim([0, 1])
            ax[2].plot(rgb[st, 0],
                       rgb[st, 1],
                       rgb[st, 2],
                       'ro',
                       label='Original')
            ax[2].plot(rgb_est[st, 0],
                       rgb_est[st, 1],
                       rgb_est[st, 2],
                       'bd',
                       label='Rendered')
            ax[2].legend()
    if out == 'rfl_est':
        return rfl_est
    elif out == 'rfl_est,xyz_est':
        return rfl_est, xyz_est
    else:
        return eval(out)
コード例 #5
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)
コード例 #6
0
def calibrate(rgbcal, xyzcal, L_type = 'lms', tr_type = 'lut', cieobs = '1931_2', 
              nbit = 8, cspace = 'lab', avg = lambda x: ((x**2).mean()**0.5), ensure_increasing_lut_at_low_rgb = 0.2,
              verbosity = 1, sep=',',header=None): 
    """
    Calculate TR parameters/lut and conversion matrices.
    
    Args:
        :rgbcal:
            | ndarray [Nx3] or string with filename of RGB values 
            | rgcal must contain at least the following type of settings:
            | - pure R,G,B: e.g. for pure R: (R != 0) & (G==0) & (B == 0)
            | - white(s): R = G = B = 2**nbit-1
            | - gray(s): R = G = B
            | - black(s): R = G = B = 0
            | - binary colors: cyan (G = B, R = 0), yellow (G = R, B = 0), magenta (R = B, G = 0)
        :xyzcal:
            | ndarray [Nx3] or string with filename of measured XYZ values for 
            | the RGB settings in rgbcal.
        :L_type:
            | 'lms', optional
            | Type of response to use in the derivation of the Tone-Response curves.
            | options:
            |  - 'lms': use cone fundamental responses: L vs R, M vs G and S vs B 
            |           (reduces noise and generally leads to more accurate characterization) 
            |  - 'Y': use the luminance signal: Y vs R, Y vs G, Y vs B
        :tr_type:
            | 'lut', optional
            | options:
            |  - 'lut': Derive/specify Tone-Response as a look-up-table
            |  - 'gog': Derive/specify Tone-Response as a gain-offset-gamma function
        :cieobs:
            | '1931_2', optional
            | CIE CMF set used to determine the XYZ tristimulus values
            | (needed when L_type == 'lms': determines the conversion matrix to
            | convert xyz to lms values)
        :nbit:
            | 8, optional
            | RGB values in nbit format (e.g. 8, 16, ...)
        :cspace:
            | color space or chromaticity diagram to calculate color differences in
            | when optimizing the xyz_to_rgb and rgb_to_xyz conversion matrices.
        :avg:
            | lambda x: ((x**2).mean()**0.5), optional
            | Function used to average the color differences of the individual RGB settings
            | in the optimization of the xyz_to_rgb and rgb_to_xyz conversion matrices.
        :ensure_increasing_lut_at_low_rgb:
            | 0.2 or float (max = 1.0) or None, optional
            | Ensure an increasing lut by setting all values below the RGB with the maximum
            | zero-crossing of np.diff(lut) and RGB/RGB.max() values of :ensure_increasing_lut_at_low_rgb:
            | (values of 0.2 are a good rule of thumb value)
            | Non-strictly increasing lut values can be caused at low RGB values due
            | to noise and low measurement signal. 
            | If None: don't force lut, but keep as is.
        :verbosity:
            | 1, optional
            | > 0: print and plot optimization results
        :sep:
            | ',', optional
            | separator in files with rgbcal and xyzcal data
        :header:
            | None, optional
            | header specifier for files with rgbcal and xyzcal data 
            | (see pandas.read_csv)
            
    Returns:
        :M:
            | linear rgb to xyz conversion matrix
        :N:
            | xyz to linear rgb conversion matrix
        :tr:
            | Tone Response function parameters or lut
        :xyz_black:
            | ndarray with XYZ tristimulus values of black
        :xyz_white:
            | ndarray with tristimlus values of white
    """
    
    # process rgb, xyzcal inputs:
    rgbcal, xyzcal = _parse_rgbxyz_input(rgbcal, xyz = xyzcal, sep = sep, header=header)
    
    # get black-positions and average black xyz (flare):
    p_blacks = (rgbcal[:,0]==0) & (rgbcal[:,1]==0) & (rgbcal[:,2]==0)
    xyz_black = xyzcal[p_blacks,:].mean(axis=0,keepdims=True)
    
    # Calculate flare corrected xyz:
    xyz_fc = xyzcal - xyz_black
    
    # get positions of pure r, g, b values:
    p_pure = [(rgbcal[:,1]==0) & (rgbcal[:,2]==0), 
              (rgbcal[:,0]==0) & (rgbcal[:,2]==0), 
              (rgbcal[:,0]==0) & (rgbcal[:,1]==0)] 
    
    # set type of L-response to use: Y for R,G,B or L,M,S for R,G,B:
    if L_type == 'Y':
        L = np.array([xyz_fc[:,1] for i in range(3)]).T
    elif L_type == 'lms':
        lms = (math.normalize_3x3_matrix(_CMF[cieobs]['M'].copy()) @ xyz_fc.T).T
        L = np.array([lms[:,i] for i in range(3)]).T
        
    # Get rgb linearizer parameters or lut and apply to all rgb's:
    if tr_type == 'gog':
        par = np.array([sp.optimize.curve_fit(TR, rgbcal[p_pure[i],i], L[p_pure[i],i]/L[p_pure[i],i].max(), p0=[1,0,1])[0] for i in range(3)]) # calculate parameters of each TR
        tr = par
    elif tr_type == 'lut':
        dac = np.arange(2**nbit)
        # lut = np.array([cie_interp(np.vstack((rgbcal[p_pure[i],i],L[p_pure[i],i]/L[p_pure[i],i].max())), dac, kind ='cubic')[1,:] for i in range(3)]).T
        lut = np.array([sp.interpolate.PchipInterpolator(rgbcal[p_pure[i],i],L[p_pure[i],i]/L[p_pure[i],i].max())(dac) for i in range(3)]).T # use this one to avoid potential overshoot with cubic spline interpolation (but slightly worse performance)
        lut[lut<0] = 0
          
        # ensure monotonically increasing lut values for low signal:
        if ensure_increasing_lut_at_low_rgb is not None:
            #ensure_increasing_lut_at_low_rgb = 0.2 # anything below that has a zero-crossing for diff(lut) will be set to zero
            for i in range(3):
                p0 = np.where((np.diff(lut[dac/dac.max() < ensure_increasing_lut_at_low_rgb,i])<=0))[0]
                if p0.any():
                    p0 = range(0,p0[-1])
                    lut[p0,i] = 0
        tr = lut

    
    # plot:
    if verbosity > 0:
        colors = 'rgb'
        linestyles = ['-','--',':']
        rgball = np.repeat(np.arange(2**8)[:,None],3,axis=1)
        Lall = _rgb_linearizer(rgball, tr, tr_type = tr_type)
        plt.figure()
        for i in range(3):
            plt.plot(rgbcal[p_pure[i],i],L[p_pure[i],i]/L[p_pure[i],i].max(),colors[i]+'o')
            plt.plot(rgball[:,i],Lall[:,i],colors[i]+linestyles[i],label=colors[i])
        plt.xlabel('Display RGB')
        plt.ylabel('Linear RGB')
        plt.legend()
        plt.title('Tone response curves')
    
    # linearize all rgb values and clamp to 0
    rgblin = _rgb_linearizer(rgbcal, tr, tr_type = tr_type) 
 
    # get rgblin to xyz_fc matrix:
    M = np.linalg.lstsq(rgblin, xyz_fc, rcond=None)[0].T 
    
    # get xyz_fc to rgblin matrix:
    N = np.linalg.inv(M)
    
    # get better approximation for conversion matrices:
    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))
    xyz_white = xyzcal[p_whites,:].mean(axis=0,keepdims=True) # get xyzw for input into xyz_to_lab() or colortf()
    def optfcn(x, rgbcal, xyzcal, tr, xyz_black, cspace, p_grays, p_whites,out,verbosity):
        M = x.reshape((3,3))
        xyzest = rgb_to_xyz(rgbcal, M, tr, xyz_black, tr_type)
        xyzw = xyzcal[p_whites,:].mean(axis=0) # get xyzw for input into xyz_to_lab() or colortf()
        labcal, labest = colortf(xyzcal,tf=cspace,xyzw=xyzw), colortf(xyzest,tf=cspace,xyzw=xyzw) # calculate lab coord. of cal. and est.
        DEs = ((labcal-labest)**2).sum(axis=1)**0.5
        DEg = DEs[p_grays]
        DEw = DEs[p_whites]
        F = (avg(DEs)**2 + avg(DEg)**2 + avg(DEw**2))**0.5
        if verbosity > 1:
            print('\nPerformance of TR + rgb-to-xyz conversion matrix M:')
            print('all: DE(jab): avg = {:1.4f}, std = {:1.4f}'.format(avg(DEs),np.std(DEs)))
            print('grays: DE(jab): avg = {:1.4f}, std = {:1.4f}'.format(avg(DEg),np.std(DEg)))
            print('whites(s) DE(jab): avg = {:1.4f}, std = {:1.4f}'.format(avg(DEw),np.std(DEw)))
        if out == 'F':
            return F
        else:
            return eval(out)
    x0 = M.ravel()
    res = math.minimizebnd(optfcn, x0, args =(rgbcal, xyzcal, tr, xyz_black, cspace, p_grays, p_whites,'F',0), use_bnd=False)
    xf = res['x_final']
    M = optfcn(xf, rgbcal, xyzcal, tr, xyz_black, cspace, p_grays, p_whites,'M',verbosity)
    N = np.linalg.inv(M)
    return M, N, tr, xyz_black, xyz_white
コード例 #7
0
ファイル: camjabz.py プロジェクト: simongr2/luxpy
                                rfl=rflM,
                                out=2)
    xyz2 = xyz2[:n, :, :]
    Ill2M = Ill2M[:(n + 1), :, :]

    # Module output plot:
    import matplotlib.pyplot as plt
    _cam_o = lambda xyz, xyzw, forward: lx.xyz_to_jabz(xyz)
    xyz, xyzw = lx.spd_to_xyz(Ill1,
                              cieobs=cieobs,
                              relative=True,
                              rfl=rflM,
                              out=2)
    jabch = _cam(xyz, xyzw=xyzw, forward=True, outin='J,aM,bM')
    out_ = _cam_o(xyz, xyzw=xyzw, forward=True)
    plt.figure()
    plt.plot(jabch[..., 1], jabch[..., 2], 'c.')
    plt.plot(out_[..., 1], out_[..., 2], 'r.')
    plt.axis('equal')

    out = 'J,aM,bM,M,C,s,h'.split(',')
    # Single data for sample and illuminant:
    # test input to _simple_cam():
    print('\n\n1: xyz in:')
    out_1 = _cam(xyz1, xyzw=xyzw1, forward=True, outin=out)
    xyz_1 = _cam(out_1[..., :3], xyzw=xyzw1, forward=False, outin=out[:3])
    print((xyz1 - xyz_1).sum())

    # Multiple data for sample and illuminants:
    print('\n\n2: xyz in:')
    out_2 = _cam(xyz2, xyzw=xyzw2, forward=True, outin=out)
コード例 #8
0
ファイル: plotters.py プロジェクト: simongr2/luxpy
def plot_spectrum_colors(spd = None, spdmax = None,\
                         wavelength_height = -0.05, wavelength_opacity = 1.0, wavelength_lightness = 1.0,\
                         cieobs = _CIEOBS, show = True, axh = None,\
                         show_grid = False,ylabel = 'Spectral intensity (a.u.)',xlim=None,\
                         **kwargs):
    """
    Plot the spectrum colors.
    
    Args:
        :spd:
            | None, optional
            | Spectrum
        :spdmax:
            | None, optional
            | max ylim is set at 1.05 or (1+abs(wavelength_height)*spdmax)
        :wavelength_opacity:
            | 1.0, optional
            | Sets opacity of wavelength rectangle.
        :wavelength_lightness:
            | 1.0, optional
            | Sets lightness of wavelength rectangle.
        :wavelength_height:
            | -0.05 or 'spd', optional
            | Determine wavelength bar height 
            | if not 'spd': x% of spd.max()
        :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.
        :show_grid:
            | False, optional
            | Show grid (True) or not (False)
        :ylabel:
            | 'Spectral intensity (a.u.)' or str, optional
            | Set y-axis label.
        :xlim:
            | None, optional
            | list or ndarray with xlimits.
        :kwargs: 
            | additional keyword arguments for use with matplotlib.pyplot.
        
    Returns:
        
    """

    if isinstance(cieobs, str):
        cmfs = _CMF[cieobs]['bar']
    else:
        cmfs = cieobs
    cmfs = cmfs[:, cmfs[1:].sum(axis=0) >
                0]  # avoid div by zero in xyz-to-Yxy conversion

    wavs = cmfs[0:1].T
    SL = cmfs[1:4].T

    srgb = xyz_to_srgb(wavelength_lightness * 100 * SL)
    srgb = srgb / srgb.max()

    if show == True:
        if axh is None:
            fig = plt.figure()
            axh = fig.add_subplot(111)

        if (wavelength_height == 'spd') & (spd is not None):
            if spdmax is None:
                spdmax = np.nanmax(spd[1:, :])
            y_min, y_max = 0.0, spdmax * (1.05)
            if xlim is None:
                x_min, x_max = spd[0, :].min(), spd[0, :].max()
            else:
                x_min, x_max = xlim

            SLrect = np.vstack([
                (x_min, 0.0),
                spd.T,
                (x_max, 0.0),
            ])
            wavelength_height = y_max
            spdmax = 1
        else:
            if (spdmax is None) & (spd is not None):
                spdmax = np.nanmax(spd[1:, :])
                y_min, y_max = wavelength_height * spdmax, spdmax * (
                    1 + np.abs(wavelength_height))

            elif (spdmax is None) & (spd is None):
                spdmax = 1
                y_min, y_max = wavelength_height, 0

            elif (spdmax is not None):
                y_min, y_max = wavelength_height * spdmax, spdmax  #*(1 + np.abs(wavelength_height))

            if xlim is None:
                x_min, x_max = wavs.min(), wavs.max()
            else:
                x_min, x_max = xlim

            SLrect = np.vstack([
                (x_min, 0.0),
                (x_min, wavelength_height * spdmax),
                (x_max, wavelength_height * spdmax),
                (x_max, 0.0),
            ])

        axh.set_xlim([x_min, x_max])
        axh.set_ylim([y_min, y_max])

        polygon = Polygon(SLrect, facecolor=None, edgecolor=None)
        axh.add_patch(polygon)
        padding = 0.1
        axh.bar(x=wavs - padding,
                height=wavelength_height * spdmax,
                width=1 + padding,
                color=srgb,
                align='edge',
                linewidth=0,
                clip_path=polygon)

        if spd is not None:
            axh.plot(spd[0:1, :].T, spd[1:, :].T, color='k', label='spd')

        if show_grid == True:
            plt.grid(True)
        axh.set_xlabel('Wavelength (nm)', kwargs)
        axh.set_ylabel(ylabel, kwargs)

        #plt.show()

        return axh
    else:
        return None
コード例 #9
0
ファイル: plotters.py プロジェクト: simongr2/luxpy
def plot_color_data(x,y,z=None, axh=None, show = True, cieobs =_CIEOBS, \
                    cspace = _CSPACE,  formatstr = 'k-', legend_loc = None, **kwargs):
    """
    Plot color data from x,y [,z].
    
    Args: 
        :x: 
            | float or ndarray with x-coordinate data
        :y: 
            | float or ndarray with y-coordinate data
        :z: 
            | None or float or ndarray with Z-coordinate data, optional
            | If None: make 2d plot.
        :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 or None, optional
            | Determines color space / chromaticity diagram to plot data in.
            | Note that data is expected to be in specified :cspace:
            | If None: don't do any formatting of x,y [z] axes.
        :formatstr: 
            | 'k-' or str, optional
            | Format str for plotting (see ?matplotlib.pyplot.plot)
        :kwargs:
            | additional keyword arguments for use with matplotlib.pyplot.
    
    Returns:
        :returns: 
            | None (:show: == True) 
            |  or 
            | handle to current axes (:show: == False)
    """
    x = np.atleast_1d(x)
    y = np.atleast_1d(y)

    if z is not None:
        z = np.atleast_1d(z)
        if axh is None:
            fig = plt.figure()
            axh = plt.axes(projection='3d')
        if 'grid' in kwargs.keys():
            axh.grid(kwargs['grid'])
            kwargs.pop('grid')
        axh.plot3D(x, y, z, formatstr, linewidth=2, **kwargs)
        axh.set_zlabel(_CSPACE_AXES[cspace][0], kwargs)
    else:
        if axh is None:
            fig = plt.figure()
            axh = plt.axes()
        if 'grid' in kwargs.keys():
            axh.grid(kwargs['grid'])
            kwargs.pop('grid')
        axh.plot(x, y, formatstr, linewidth=2, **kwargs)
        axh.set_xlabel(_CSPACE_AXES[cspace][1], kwargs)
        axh.set_ylabel(_CSPACE_AXES[cspace][2], kwargs)
    if 'label' in kwargs.keys():
        axh.legend(loc=legend_loc)
    if show == True:
        plt.show()
    else:
        return axh
コード例 #10
0
ファイル: plotters.py プロジェクト: simongr2/luxpy
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
コード例 #11
0
ファイル: plotters.py プロジェクト: simongr2/luxpy
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
コード例 #12
0
ファイル: graphics.py プロジェクト: simongr2/luxpy
def plot_hue_bins(hbins = 16, start_hue = 0.0, scalef = 100, \
        plot_axis_labels = False, bin_labels = '#', plot_edge_lines = True, \
        plot_center_lines = False, plot_bin_colors = True, \
        plot_10_20_circles = False,\
        axtype = 'polar', ax = None, force_CVG_layout = False):
    """
    Makes basis plot for Color Vector Graphic (CVG).
    
    Args:
        :hbins:
            | 16 or ndarray with sorted hue bin centers (°), optional
        :start_hue:
            | 0.0, optional
        :scalef:
            | 100, 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.
        :plot_10_20_circles:
            | False, optional
            | If True and :axtype: == 'cart': Plot white circles at 
            | 80%, 90%, 100%, 110% and 120% of :scalef: 
        :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.
        :force_CVG_layout:
            | False or True, optional
            | True: Force plot of basis of CVG on first encounter.
            
    Returns:
        :returns: 
            | gcf(), gca(), list with rgb colors for hue bins (for use in 
              other plotting fcns)
        
    """

    # Setup hbincenters and hsv_hues:
    if isinstance(hbins, float) | isinstance(hbins, int):
        nhbins = hbins
        dhbins = 360 / (nhbins)  # hue bin width
        hbincenters = np.arange(start_hue + dhbins / 2, 360, dhbins)
        hbincenters = np.sort(hbincenters)

    else:
        hbincenters = hbins
        idx = np.argsort(hbincenters)
        if isinstance(bin_labels, list) | isinstance(bin_labels, np.ndarray):
            bin_labels = bin_labels[idx]
        hbincenters = hbincenters[idx]
        nhbins = hbincenters.shape[0]
    hbincenters = hbincenters * np.pi / 180

    # Setup hbin labels:
    if bin_labels is '#':
        bin_labels = ['#{:1.0f}'.format(i + 1) for i in range(nhbins)]
    elif isinstance(bin_labels, str):
        bin_labels = [
            bin_labels + '{:1.0f}'.format(i + 1) for i in range(nhbins)
        ]

    # initializing the figure
    cmap = None
    if (ax is None) or (ax == 'new'):
        fig = plt.figure()
        newfig = True
    else:
        fig = plt.gcf()
        newfig = False
    rect = [0.1, 0.1, 0.8,
            0.8]  # setting the axis limits in [left, bottom, width, height]

    if axtype == 'polar':
        # the polar axis:
        if newfig == True:
            ax = fig.add_axes(rect, polar=True, frameon=False)
    else:
        #cartesian axis:
        if newfig == True:
            ax = fig.add_axes(rect)

    if (newfig == True) | (force_CVG_layout == True):

        # Calculate hue-bin boundaries:
        r = np.vstack((np.zeros(hbincenters.shape),
                       1. * scalef * np.ones(hbincenters.shape)))
        theta = np.vstack((np.zeros(hbincenters.shape), hbincenters))
        #t = hbincenters.copy()
        dU = np.roll(hbincenters.copy(), -1)
        dL = np.roll(hbincenters.copy(), 1)
        dtU = dU - hbincenters
        dtL = hbincenters - dL
        dtU[dtU < 0] = dtU[dtU < 0] + 2 * np.pi
        dtL[dtL < 0] = dtL[dtL < 0] + 2 * np.pi
        dL = hbincenters - dtL / 2
        dU = hbincenters + dtU / 2
        dt = (dU - dL)
        dM = dL + dt / 2

        # Setup color for plotting hue bins:
        hsv_hues = hbincenters - 30 * np.pi / 180
        hsv_hues = hsv_hues / hsv_hues.max()

        edges = np.vstack(
            (np.zeros(hbincenters.shape), dL))  # setup hue bin edges array

        if axtype == 'cart':
            if plot_center_lines == True:
                hx = r * np.cos(theta) * 1.2
                hy = r * np.sin(theta) * 1.2
            if bin_labels is not None:
                hxv = np.vstack((np.zeros(hbincenters.shape),
                                 1.4 * scalef * np.cos(hbincenters)))
                hyv = np.vstack((np.zeros(hbincenters.shape),
                                 1.4 * scalef * np.sin(hbincenters)))
            if plot_edge_lines == True:
                #hxe = np.vstack((np.zeros(hbincenters.shape),1.2*scalef*np.cos(dL)))
                #hye = np.vstack((np.zeros(hbincenters.shape),1.2*scalef*np.sin(dL)))
                hxe = np.vstack(
                    (0.1 * scalef * np.cos(dL), 1.5 * scalef * np.cos(dL)))
                hye = np.vstack(
                    (0.1 * scalef * np.sin(dL), 1.5 * scalef * np.sin(dL)))

        # Plot hue-bins:
        for i in range(nhbins):

            # Create color from hue angle:
            #c = np.abs(np.array(colorsys.hsv_to_rgb(hsv_hues[i], 0.75, 0.85)))
            c = np.abs(np.array(colorsys.hls_to_rgb(hsv_hues[i], 0.45, 0.5)))
            if i == 0:
                cmap = [c]
            else:
                cmap.append(c)

            if axtype == 'polar':
                if plot_edge_lines == True:
                    ax.plot(edges[:, i],
                            r[:, i] * 1.,
                            color='grey',
                            marker='None',
                            linestyle='--',
                            linewidth=1,
                            markersize=2)
                if plot_center_lines == True:
                    if np.mod(i, 2) == 1:
                        ax.plot(theta[:, i],
                                r[:, i],
                                color=c,
                                marker=None,
                                linestyle='--',
                                linewidth=1)
                    else:
                        ax.plot(theta[:, i],
                                r[:, i],
                                color=c,
                                marker=None,
                                linestyle='--',
                                linewidth=1,
                                markersize=10)
                if plot_bin_colors == True:
                    bar = ax.bar(dM[i],
                                 r[1, i],
                                 width=dt[i],
                                 color=c,
                                 alpha=0.25)
                if bin_labels is not None:
                    ax.text(hbincenters[i],
                            1.3 * scalef,
                            bin_labels[i],
                            fontsize=10,
                            horizontalalignment='center',
                            verticalalignment='center',
                            color=np.array([1, 1, 1]) * 0.45)
                if plot_axis_labels == False:
                    ax.set_xticklabels([])
                    ax.set_yticklabels([])
            else:
                axis_ = 1. * np.array(
                    [-scalef * 1.5, scalef * 1.5, -scalef * 1.5, scalef * 1.5])
                if plot_edge_lines == True:
                    ax.plot(hxe[:, i],
                            hye[:, i],
                            color='grey',
                            marker='None',
                            linestyle='--',
                            linewidth=1,
                            markersize=2)

                if plot_center_lines == True:
                    if np.mod(i, 2) == 1:
                        ax.plot(hx[:, i],
                                hy[:, i],
                                color=c,
                                marker=None,
                                linestyle='--',
                                linewidth=1)
                    else:
                        ax.plot(hx[:, i],
                                hy[:, i],
                                color=c,
                                marker=None,
                                linestyle='--',
                                linewidth=1,
                                markersize=10)
                if bin_labels is not None:
                    ax.text(hxv[1, i],
                            hyv[1, i],
                            bin_labels[i],
                            fontsize=10,
                            horizontalalignment='center',
                            verticalalignment='center',
                            color=np.array([1, 1, 1]) * 0.45)
                ax.axis(axis_)

        if plot_axis_labels == False:
            ax.set_xticklabels([])
            ax.set_yticklabels([])
        else:
            ax.set_xlabel("a'")
            ax.set_ylabel("b'")

        ax.plot(0, 0, color='grey', marker='+', linestyle=None, markersize=6)

        if (axtype != 'polar') & (plot_10_20_circles == True):
            r = np.array([
                0.8, 0.9, 1.1, 1.2
            ]) * scalef  # plot circles at 80, 90, 100, 110, 120 % of scale f
            plotcircle(radii=r,
                       angles=np.arange(0, 365, 5),
                       color='w',
                       linestyle='-',
                       axh=ax,
                       linewidth=0.5)
            plotcircle(radii=[scalef],
                       angles=np.arange(0, 365, 5),
                       color='k',
                       linestyle='-',
                       axh=ax,
                       linewidth=1)
            ax.text(0,
                    -0.75 * scalef,
                    '-20%',
                    fontsize=8,
                    horizontalalignment='center',
                    verticalalignment='center',
                    color='w')
            ax.text(0,
                    -1.25 * scalef,
                    '+20%',
                    fontsize=8,
                    horizontalalignment='center',
                    verticalalignment='center',
                    color='w')

        if (axtype != 'polar') & (plot_bin_colors == True) & (_CVG_BG
                                                              is not None):
            ax.imshow(_CVG_BG, origin='upper', extent=axis_)

    return fig, ax, cmap
コード例 #13
0
def plot_tm30_report(spd, cri_type = 'ies-tm30',
                     source = '', manufacturer = '',
                     date = '', model = '', 
                     notes = '', max_len_notes_line = 40,
                     figsize = (7,12),
                     save_fig_name = None, dpi = 300,
                     plot_report_top = True, plot_report_bottom = True,
                     suptitle = 'ANSI/IES TM-30-18 Color Rendition Report',
                     font_size = _TM30_FONT_SIZE, **kwargs):
    """
    Create TM30 Color Rendition Report.
    
    Args:
        :spd:
            | ndarray or dict
            | If ndarray: single spectral power distribution.
            | If dict: dictionary with pre-computed parameters (using _tm30_process_spd()).
            |  required keys:
            |   'Rf','Rg','cct','duv','Sr','cri_type','xyzri','xyzrw',
            |   'hbinnrs','Rfi','Rfhi','Rcshi','Rhshi',
            |   'jabt_binned','jabr_binned',
            |   'nhbins','start_hue','normalize_gamut','normalized_chroma_ref'
            | see cri.spd_to_cri() for more info on parameters.
        :cri_type:
            | _CRI_TYPE_DEFAULT or str or dict, optional
            |   -'str: specifies dict with default cri model parameters 
            |     (for supported types, see luxpy.cri._CRI_DEFAULTS['cri_types'])
            |   - dict: user defined model parameters 
            |     (see e.g. luxpy.cri._CRI_DEFAULTS['cierf'] 
            |     for required structure)
            | Note that any non-None input arguments (in kwargs) 
            | to the function will override default values in cri_type dict.
        :source:
            | string with source name.
        :manufacturer:
            | string with source manufacturer.
        :model:
            | string with source model.
        :date:
            | string with source measurement date.
        :notes:
            | string to be split
        :max_len_notes_line:
            | 40, optional
            | Maximum length of a single line when splitting the string.
        :figsize:
            | (7,12), optional
            | Figure size of pyplot figure.
        :save_fig_name:
            | None, optional
            | Filename (+path) to which the report will be saved as an image (png).
            | If None: don't save, just display.
        :dpi:
            | 300, optional
            | Dots-Per-Inch of image file (PNG).
        :plot_report_top:
            | execute _plot_tm30_report_top()
        :plot_report_bottom:
            | execute _plot_tm30_report_bottom()
        :suptitle:
            | 'ANSI/IES TM-30-18 Color Rendition Report' or str, optional
            | report title (input for plt.suptitle).  
        :font_size:
            | _TM30_FONT_SIZE, optional
            | Font size of text, axis labels and axis values.
        :kwargs:
            | Additional optional keyword arguments, 
            | the same as in cri.spd_to_cri()
            
    Returns:
        :axs:
            | dictionary with handles to each axes.
        :data:
            | dictionary with required parameters for plotting functions.      
    """
    # Set up subplots:
    fig = plt.figure(constrained_layout=True, figsize = figsize)  
    nrows = int(4 + 1*(plot_report_top) + 1*(plot_report_bottom))
    gs = fig.add_gridspec(nrows, 3,height_ratios=[0.1,0.5,0.5,0.5,0.6,0.3], width_ratios=[1,1,1.5])
    if plot_report_top == True: 
        f_ax_top = fig.add_subplot(gs[0, :])
    else:
        f_ax_top = None
    if plot_report_bottom == True:
        f_ax_bottom = fig.add_subplot(gs[-1, :]) 
    else:
        f_ax_bottom = None
    f_ax_spd = fig.add_subplot(gs[int(0 + 1*(plot_report_top)), 0:2])
    f_ax_cvg = fig.add_subplot(gs[int(1 + 1*(plot_report_top)):int(3 + 1*(plot_report_top)), 0:2])
    f_ax_cshj = fig.add_subplot(gs[int(0 + 1*(plot_report_top)), 2:])
    f_ax_hshj = fig.add_subplot(gs[int(1 + 1*(plot_report_top)), 2:])
    f_ax_fhj = fig.add_subplot(gs[int(2 + 1*(plot_report_top)), 2:])
    f_ax_fi = fig.add_subplot(gs[int(3 + 1*(plot_report_top)),:])
    
    # Get required parameter values from spd:
    data = _tm30_process_spd(spd, cri_type = cri_type,**kwargs)
    
    # Create all subplots:
    if plot_report_top == True:
        _plot_tm30_report_top(f_ax_top, source = source, manufacturer = manufacturer,
                   date = date, model = model)
    if plot_report_bottom == True:
        _plot_tm30_report_bottom(f_ax_bottom, spd, 
                          notes = notes, max_len_notes_line = max_len_notes_line)

    plot_tm30_spd(data, axh = f_ax_spd, font_size = font_size)
    plot_tm30_cvg(data, axh = f_ax_cvg, font_size = font_size)
    plot_tm30_Rfhj(data, axh = f_ax_fhj, y_offset = 2, font_size = font_size)
    plot_tm30_Rcshj(data, axh = f_ax_cshj, xlabel = False, y_offset = 0.03, font_size = font_size)
    plot_tm30_Rhshj(data, axh = f_ax_hshj, xlabel = False, y_offset = 0.05, font_size = font_size)
    plot_tm30_Rfi(data, axh = f_ax_fi, font_size = font_size)
    fig.suptitle(suptitle, fontsize = 14, fontweight= 'bold')
    
    # Save to file:
    if save_fig_name is not None:
        fig.savefig(save_fig_name, dpi = dpi)
    
    axs = {'fig': fig, 'top' : f_ax_top, 'bottom': f_ax_bottom, 'cvg':f_ax_cvg,
           'rfi': f_ax_fi, 'rfhj':f_ax_fhj, 'rcshj':f_ax_cshj, 'rhshj':f_ax_hshj}
    
    
    return axs, data