colour-science / colour

Colour Science for Python
https://www.colour-science.org
BSD 3-Clause "New" or "Revised" License
2.08k stars 259 forks source link

Implement support for plotting colours in "Lab" colourspace. #241

Open Ron024 opened 8 years ago

Ron024 commented 8 years ago

It would be nice if there was a Lab plot generator.

Ron024 commented 8 years ago

I am not well versed at using git so I will post my code here. It doesn't autoscale axes but it does have dynamic colors for the markers.

import matplotlib.pyplot as plt
from pylab import get_cmap

names = ['4H','20H','48H']

L = [67.39181818,68.07888889,68.41888889]
a = [-11.72909091,-12.14,-12.90666667]
b = [11.07727273,14.85,16.49]

def gencolors(inlist):
    """
    returns list of colors equal to length of inlist
    """
    cm = get_cmap('Set1')
    cgen = (cm(1.*i/len(inlist)) for i in range(len(inlist)))

    colors = []
    for i in cgen:
        colors.append(i)

    return colors

y = [1] * len(L) 'to make 1-D L plot

#fh, (ax1, ax2) = plt.subplots(1,2)

fig = plt.figure(figsize = (8,6))
ax1 = plt.subplot2grid((1, 3), (0, 0), colspan=2)
ax2 = plt.subplot2grid((1, 3), (0, 2))

ax1.scatter(a,b, c=gencolors(a), s=100)
ax1.spines['left'].set_position('center')
ax1.spines['right'].set_position('center')
ax1.spines['bottom'].set_position('center')
ax1.spines['top'].set_position('center')
#ax1.spines['left'].set_smart_bounds(True)
#ax1.spines['bottom'].set_smart_bounds(True)
ax1.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('left')
ax1.grid(True)
ax1.set_xlim(-17,-10)
ax1.set_aspect(1./ax1.get_data_ratio())

ax2.scatter(y,L, c=gencolors(L), s=100)
ax2.set_xlim(.5, 1.5)
ax2.axes.get_xaxis().set_visible(False)  # remove the x-axis and its ticks
ax2.set_aspect(10, adjustable='box')  # adjustable='box' is important here
plt.tight_layout()

mng = plt.get_current_fig_manager()
### works on Ubuntu??? >> did NOT working on windows
# mng.resize(*mng.window.maxsize())
mng.window.state('zoomed') #works fine on Windows!
plt.show() 
KelSolaar commented 8 years ago

That's would be useful indeed! I missed it a bit when implementing CQS, I'm not sure when I'll be able to put my hands into that.

We were discussing with @MichaelMauderer about giving another go at generating diagrams filling colour dynamically which is slightly related as it would be useful to get some optional background as per #240.

KelSolaar commented 8 years ago

@Ron024: I was looking into that a bit, and had a few questions for you, I found this image:

image

And there is some good stuff with it, I actually quite like the fact that you have the L*a*\ or L*b* axes. I would like to make that optional in our implementation: The single L*\ would be replaced by those two axes by an argument.

I would also like to be able to optionally have the axes centered to origin and optionally use actual L_a_b* colours.

What do you think?

KelSolaar commented 8 years ago

Played a bit with your code and wrapped the stuff in a proper function:

%matplotlib inline

import numpy as np
import matplotlib.gridspec
import matplotlib.pyplot

from colour import tsplit, tstack, Lab_to_XYZ, XYZ_to_sRGB
from colour.plotting import *

def Lab_colours_plot(Lab,
                     illuminant_Lab=DEFAULT_PLOTTING_ILLUMINANT,
                     points_size=100,
                     lightness_subplot=False,
                     center_spines=True,
                     bounding_boxes=None,
                     **kwargs):
    settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)}
    settings.update(kwargs)

    figure = canvas(**settings)

    L, a, b = tsplit(Lab)

    if 'colour_cycle_map' in settings:
        cycle = colour_cycle(**settings)
        RGB = [next(cycle) for c in range(L.size)]
    else:
        RGB = XYZ_to_sRGB(Lab_to_XYZ(Lab, illuminant_Lab), illuminant_Lab)

    if lightness_subplot:
        gridspec = matplotlib.gridspec.GridSpec(
            1, 2, height_ratios=(1, 1), width_ratios=(1, 0.05))
    else:
        gridspec = matplotlib.gridspec.GridSpec(
            1, 3, height_ratios=(1, 1, 1), width_ratios=(1, 1, 1))

    def Lab_colours_subplot(parent, x, y, x_label, y_label, bounding_box=None):
        axes = matplotlib.pyplot.subplot(parent)

        axes.scatter(x, y, c=RGB, s=points_size)
        matplotlib.pyplot.xlabel(x_label)
        matplotlib.pyplot.ylabel(y_label, rotation=0)

        if center_spines:
            for location in ('left', 'right', 'bottom', 'top'):
                axes.spines[location].set_position('center')
            axes.xaxis.set_ticks_position('bottom')
            axes.xaxis.set_label_coords(-0.075, 0.5)
            axes.yaxis.set_ticks_position('left')
            axes.yaxis.set_label_coords(0.5, -0.075)

        settings_sp = {'grid': True, 'bounding_box': bounding_box}
        decorate(**settings_sp)
        boundaries(**settings_sp)

        x_min, x_max = axes.get_xlim()
        y_min, y_max = axes.get_ylim()
        axes.set_aspect((x_max - x_min) / (y_max - y_min))

        return axes

    axes = []

    axes.append(Lab_colours_subplot(
        gridspec[0], a, b, '$a^{\star}$', '$b^{\star}$',
        bounding_boxes[0] if bounding_boxes is not None else None))

    if lightness_subplot:
        axes_L = matplotlib.pyplot.subplot(gridspec[1])
        axes.append(axes_L)
        axes_L.scatter(np.zeros(L.size), L, c=RGB, s=points_size)
        axes_L.axes.get_xaxis().set_visible(False)
        matplotlib.pyplot.ylabel('$L^{\star}$', rotation=0)
        axes_L.yaxis.set_label_coords(-1.0, 0.5)

        settings_ls = {
            'grid': True,
            'bounding_box': (
                bounding_boxes[1] if bounding_boxes is not None else None)}
        decorate(**settings_ls)
        boundaries(**settings_ls)
    else:
        axes.append(Lab_colours_subplot(
            gridspec[1], a, L, '$a^{\star}$', '$L^{\star}$',
            bounding_boxes[1] if bounding_boxes is not None else None))
        axes.append(Lab_colours_subplot(
            gridspec[2], b, L, '$b^{\star}$', '$L^{\star}$',
            bounding_boxes[2] if bounding_boxes is not None else None))

    title = (settings['title'] if 'title' in settings else
             'CIE $L^{\star}a^{\star}b^{\star}$ Colours Plot')
    matplotlib.pyplot.suptitle(title, fontsize=DEFAULT_FONT_SIZE * 1.25)

    gridspec.tight_layout(figure, rect=[0, 0, 1, 0.975])

    display(**settings)

    return axes

L = [67.39181818, 68.07888889, 68.41888889]
a = [-11.72909091, -12.14, -12.90666667]
b = [11.07727273, 14.85, 16.49]

Lab_colours_plot(tstack((L, a, b)),
                 title='Custom Title')
Lab_colours_plot(tstack((L, a, b)),
                 colour_cycle_map='viridis',
                 colour_cycle_count=len(L))
Lab_colours_plot(tstack((L, a, b)))
Lab_colours_plot(tstack((L, a, b)),
                 center_spines=False)
Lab_colours_plot(tstack((L, a, b)),
                 center_spines=False,
                 bounding_boxes=((-100, 100, -100, 100),
                                 (-100, 100, 0, 100),
                                 (-100, 100, 0, 100)))
Lab_colours_plot(tstack((L, a, b)),
                 lightness_subplot=True,
                 colour_cycle_map='viridis',
                 colour_cycle_count=len(L))
Lab_colours_plot(tstack((L, a, b)),
                 lightness_subplot=True,
                 bounding_boxes=((-100, 100, -100, 100),
                                 (-1, 1, 0, 100)))
Lab_colours_plot(tstack((L, a, b)),
                 center_spines=False,
                 lightness_subplot=True,
                 bounding_boxes=((-100, 100, -100, 100),
                                 (-1, 1, 0, 100)))

image image image image image image image image

KelSolaar commented 8 years ago

I was thinking about this one this morning and it begs for being more generic and allow drawing figures for Hunter Lab, CIE Luv, etc... I would also like to investigate further #240 and find a way to optionally and responsively generate coloured background for those figures.

Ron024 commented 8 years ago

@KelSolaar The more advanced software that is available does a full 3D plot that can be orbited. It would be awesome to have different projections. Again, beyond the scope of what I am capable.

KelSolaar commented 8 years ago

@Ron024: We have some code for 3D plots in colour, the big issue is that Matplotlib has a really hard time doing that properly, that's one of the reason behind https://github.com/colour-science/colour-analysis

LaughingMaann commented 4 months ago

@KelSolaar i'm trying to run your code example, but getting an error:

from colour import tsplit, tstack, Lab_to_XYZ, XYZ_to_sRGB 8 from colour.plotting import *

ImportError: cannot import name 'tsplit' from 'colour' (unknown location)

my pip package version is colour-science 0.4.3

is tsplit and tstack, already in the latest ver 0.4.3?

KelSolaar commented 4 months ago

Hey @LaughingMaann,

tsplit and tstack are not in the base namespace anymore, try this: from colour.utilities import tsplit, tstack

Cheers,

Thomas

trainee202 commented 1 month ago

Hi,

Will we be having a formal support in upstream to plot LAB (or LAB family like OKLAB) colours in the upstream

KelSolaar commented 1 month ago

It is somewhere on the todo list but not really a priority thing as you can unfortunately see. The fact you asked for it puts it higher on the stack though!

vibhoothi commented 1 month ago

This will be useful in my workflows too, there has been uptick in interest of OKLab and if we can visualise them, it will be useful.

Also that makes me wonder whether we can also put the XYZ intermediates along with OKlab when implementing:)