Exemple #1
0
def plot_xy_color_swatch(xy, fig=None, ax=None):
    """Plot a color swatch for a given pair of xy chromaticity coordinates.

    Parameters
    ----------
    xy : `numpy.ndarray`
        ndarray of x,y chromaticity coordinates
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    xyz = xy_to_XYZ(xy)
    rgb = XYZ_to_sRGB(xyz)
    imgarr = np.ones(
        (2, 2, 3)
    ) * rgb  # * 3.975  # 100 transmission => peak of 0.25, * 4 rescales values to fill [0,1]
    imgarr = (imgarr * 3.95 * 255).astype(np.uint8)
    fig, ax = share_fig_ax(fig, ax)
    ax.imshow(imgarr, interpolation=None)
    ax.xaxis.set_major_locator(NullLocator())
    ax.yaxis.set_major_locator(NullLocator())
    return fig, ax
Exemple #2
0
def plot_cmf(cmf, fig=None, ax=None):
    fig, ax = share_fig_ax(fig, ax)

    ax.plot(cmf['wvl'], cmf['X'])
    ax.plot(cmf['wvl'], cmf['Y'])
    ax.plot(cmf['wvl'], cmf['Z'])
    return fig, ax
Exemple #3
0
def cie_1976_wavelength_annotations(wavelengths, fig=None, ax=None):
    """Draw lines normal to the spectral locust on a CIE 1976 diagram and writes the text for each wavelength.

    Parameters
    ----------
    wavelengths : `iterable`
        set of wavelengths to annotate
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
    Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Notes
    -----
    see SE:
    https://stackoverflow.com/questions/26768934/annotation-along-a-curve-in-matplotlib

    """
    # some tick parameters
    tick_length = 0.025
    text_offset = 0.06

    # convert wavelength to u' v' coordinates
    wavelengths = np.asarray(wavelengths)
    idx = np.arange(1, len(wavelengths) - 1, dtype=int)
    wvl_lbl = wavelengths[idx]
    uv = XYZ_to_uvprime(wavelength_to_XYZ(wavelengths))
    u, v = uv[..., 0][idx], uv[..., 1][idx]
    u_last, v_last = uv[..., 0][idx - 1], uv[..., 1][idx - 1]
    u_next, v_next = uv[..., 0][idx + 1], uv[..., 1][idx + 1]

    angle = atan2(v_next - v_last, u_next - u_last) + pi / 2
    cos_ang, sin_ang = cos(angle), sin(angle)
    u1, v1 = u + tick_length * cos_ang, v + tick_length * sin_ang
    u2, v2 = u + text_offset * cos_ang, v + text_offset * sin_ang

    fig, ax = share_fig_ax(fig, ax)
    tick_lines = LineCollection(np.c_[u, v, u1, v1].reshape(-1, 2, 2),
                                color='0.25',
                                lw=1.25)
    ax.add_collection(tick_lines)
    for i in range(len(idx)):
        ax.text(u2[i],
                v2[i],
                str(wvl_lbl[i]),
                va="center",
                ha="center",
                clip_on=True)

    return fig, ax
Exemple #4
0
def plot_spectrum(spectrum_dict,
                  xrange=(380, 730),
                  yrange=(0, 100),
                  smoothing=None,
                  fig=None,
                  ax=None):
    """Plot a spectrum.

    Parameters
    ----------
    spectrum_dict : `dict`
        with keys wvl, values
    xrange : `iterable`
        pair of lower and upper x bounds
    yrange : `iterable`
        pair of lower and upper y bounds
    smoothing : `float`
        number of nanometers to smooth data by.  If None, do no smoothing
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    wvl, values = spectrum_dict['wvl'], spectrum_dict['values']
    if smoothing is not None:
        dx = wvl[1] - wvl[0]
        window_width = int(smoothing / dx)
        values = smooth(values, window_width, window='flat')

    bg = render_plot_spectrum_background(xrange[0], xrange[1])
    fig, ax = share_fig_ax(fig, ax)
    ax.imshow(bg,
              extent=[*xrange, *yrange],
              interpolation='lanczos',
              aspect='auto')
    ax.plot(wvl, values, lw=3)
    ax.fill_between(wvl,
                    values,
                    yrange[1] * len(values),
                    facecolor='w',
                    alpha=0.5)
    ax.set(xlim=xrange,
           xlabel=r'Wavelength $\lambda$ [nm]',
           ylim=yrange,
           ylabel='Transmission [%]')

    return fig, ax
Exemple #5
0
def cct_duv_diagram(samples=100, fig=None, ax=None):
    """Create a CCT-Duv diagram.

    For more information see Calculation of CCT and Duv and Practical Conversion Formulae, Yoshi Ohno, 2011.

    Parameters
    ----------
    samples : `int`
        number of samples on the background, total #pix will be samples^2
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    xlim = (2000, 10000)
    ylim = (-0.03, 0.03)

    cct = np.linspace(xlim[0], xlim[1],
                      samples)  # todo: even sampling along log, not linear
    duv = np.linspace(ylim[0], ylim[1], samples)

    upvp = multi_cct_duv_to_uvprime(cct, duv)
    cct, duv = np.meshgrid(cct, duv)

    xy = uvprime_to_xy(upvp)
    xyz = xy_to_XYZ(xy)
    dat = XYZ_to_sRGB(xyz)

    maximum = np.max(dat, axis=-1)
    dat /= maximum[..., np.newaxis]
    dat = np.clip(dat, 0, 1)

    fig, ax = share_fig_ax(fig, ax)

    ax.imshow(dat,
              extent=[*xlim, *ylim],
              interpolation='bilinear',
              origin='lower',
              aspect='auto')

    ax.set(xlim=xlim, xlabel='CCT [K]', ylim=ylim, ylabel='Duv [a.u.]')

    return fig, ax
Exemple #6
0
def cie_1976_plankian_locust(trange=(2000, 10000),
                             num_points=100,
                             isotemperature_lines_at=None,
                             isotemperature_du=0.025,
                             fig=None,
                             ax=None):
    """Draw the plankian locust on the CIE 1976 color diagram.

    Parameters
    ----------
    trange : `iterable`
        (min,max) color temperatures
    num_points : `int`
        number of points to compute
    isotemperature_lines_at : `iterable`
        CCTs to plot isotemperature lines at, defaults to [2000, 3000, 4000, 5000, 6500, 10000] if None.
        set to False to not plot lines
    isotemperature_du : `float`
        delta-u, parameter, length in x of the isotemperature lines
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    # compute the u', v' coordinates of the temperatures
    temps = np.linspace(trange[0], trange[1], num_points)
    interpf_u, interpf_v = prepare_robertson_interpfs(values=('u', 'v'),
                                                      vs='K')
    u = interpf_u(temps)
    v = interpf_v(temps) * 1.5  # x1.5 converts 1960 uv to 1976 u' v'

    # if plotting isotemperature lines, compute the upper and lower points of
    # each line and connect them.
    plot_isotemp = True
    if isotemperature_lines_at is None:
        isotemperature_lines_at = np.asarray(
            [2000, 3000, 4000, 5000, 6500, 10000])
        u_iso = interpf_u(isotemperature_lines_at)
        v_iso = interpf_v(isotemperature_lines_at)
        interpf_dvdu = prepare_robertson_interpfs(values='dvdu', vs='u')

        dvdu = interpf_dvdu(u_iso)
        du = isotemperature_du / dvdu

        u_high = u_iso + du / 2
        u_low = u_iso - du / 2
        v_high = (v_iso + du / 2 *
                  dvdu) * 1.5  # factors of 1.5 convert from uv to u'v'
        v_low = (v_iso - du / 2 * dvdu) * 1.5
    elif isotemperature_lines_at is False:
        plot_isotemp = False

    fig, ax = share_fig_ax(fig, ax)
    ax.plot(u, v, c='0.15')
    if plot_isotemp is True:
        for ul, uh, vl, vh in zip(u_low, u_high, v_low, v_high):
            ax.plot([ul, uh], [vl, vh], c='0.15')

    return fig, ax
Exemple #7
0
def cie_1976_plot(xlim=(-0.09, 0.68),
                  ylim=None,
                  samples=400,
                  annotate_wvl=True,
                  draw_plankian_locust=False,
                  fig=None,
                  ax=None):
    """Create a CIE 1976 plot.

    Parameters
    ----------
    xlim : `iterable`
        left and right bounds of the plot
    ylim : `iterable`
        lower and upper bounds of the plot.  If `None`, the y bounds will be chosen to match the x bounds
    samples : `int`
        number of 1D samples within the region of interest, total pixels will be samples^2
    annotate_wvl : `bool`
        whether to plot wavelength annotations
    draw_plankian_locust : `bool`
        whether to draw the plankian locust
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    # duplicate xlim if ylim not set
    if ylim is None:
        ylim = xlim

    # don't compute over dead space
    xlim_bg = list(xlim)
    ylim_bg = list(ylim)
    if xlim[0] < 0:
        xlim_bg[0] = 0
    if xlim[1] > 0.65:
        xlim_bg[1] = 0.65
    if ylim[0] < 0:
        ylim_bg[0] = 0
    if ylim[1] > 0.6:
        ylim_bg[1] = 0.6

    # create lists of wavelengths and map them to uv for the border line and annotation.
    wvl_line = np.arange(400, 700, 2)
    wvl_line_uv = XYZ_to_uvprime(wavelength_to_XYZ(wvl_line))
    # duplicate the lowest wavelength so that the boundary line is closed
    wvl_line_uv = np.vstack((wvl_line_uv, wvl_line_uv[0, :]))

    background = render_cie_1976_background(*xlim_bg, *ylim_bg, samples)

    fig, ax = share_fig_ax(fig, ax)
    ax.imshow(background,
              extent=[*xlim_bg, *ylim_bg],
              interpolation='bilinear',
              origin='lower')
    ax.plot(wvl_line_uv[:, 0], wvl_line_uv[:, 1], ls='-', c='0.25', lw=2.5)
    if annotate_wvl:
        wvl_annotate = [
            360, 400, 455, 470, 480, 490, 500, 510, 520, 540, 555, 570, 580,
            590, 600, 610, 625, 700, 830
        ]
        fig, ax = cie_1976_wavelength_annotations(wvl_annotate, fig=fig, ax=ax)
    if draw_plankian_locust:
        fig, ax = cie_1976_plankian_locust(fig=fig, ax=ax)
    ax.set(xlim=xlim, xlabel='CIE u\'', ylim=ylim, ylabel='CIE v\'')

    return fig, ax
Exemple #8
0
def cie_1931_plot(xlim=(0, 0.9), ylim=None, samples=300, fig=None, ax=None):
    """Create a CIE 1931 plot.

    Parameters
    ----------
    xlim : `iterable`
        left and right bounds of the plot
    ylim : `iterable`
        lower and upper bounds of the plot.  If `None`, the y bounds will be chosen to match the x bounds
    samples : `int`
        number of 1D samples within the region of interest, total pixels will be samples^2
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    Returns
    -------
    fig : `matplotlib.figure.Figure`
        Figure to draw plot in
    ax : `matplotlib.axes.Axis`
        Axis to draw plot in

    """
    # duplicate xlim if ylim not set
    if ylim is None:
        ylim = xlim

    # don't compute over dead space
    xlim_bg = list(xlim)
    ylim_bg = list(ylim)
    if xlim[0] < 0:
        xlim_bg[0] = 0
    if xlim[1] > 0.75:
        xlim_bg[1] = 0.75
    if ylim[0] < 0:
        ylim_bg[0] = 0
    if ylim[1] > 0.85:
        ylim_bg[1] = 0.85

    # create lists of wavelengths and map them to uv,
    # a reduced set for a faster mask and
    # yet another set for annotation.
    wvl_line = np.arange(400, 700, 2.5)
    wvl_line_xy = XYZ_to_xy(wavelength_to_XYZ(wvl_line))

    wvl_annotate = [
        360, 400, 455, 470, 480, 490, 500, 510, 520, 540, 555, 570, 580, 590,
        600, 615, 630, 700, 830
    ]

    data = render_cie_1931_background(*xlim_bg, *ylim_bg, samples)

    # duplicate the lowest wavelength so that the boundary line is closed
    wvl_line_xy = np.vstack((wvl_line_xy, wvl_line_xy[0, :]))

    fig, ax = share_fig_ax(fig, ax)
    ax.imshow(data,
              extent=[*xlim_bg, *ylim_bg],
              interpolation='bilinear',
              origin='lower')
    ax.plot(wvl_line_xy[:, 0], wvl_line_xy[:, 1], ls='-', c='0.25', lw=2)
    fig, ax = cie_1931_wavelength_annotations(wvl_annotate, fig=fig, ax=ax)
    ax.set(xlim=xlim, xlabel='CIE x', ylim=ylim, ylabel='CIE y')

    return fig, ax