Exemplo n.º 1
0
def render_cyl_space(space, resolution, data, c):
    """
    Render the space with the given resolution and factor.

    The sRGB gamut is used for the plot. The HSV space is used as it maps
    far better to cylinder spaces. Very close to black, on some spaces the
    models don't cover very well, so we make an additional pass using
    Cartesian coordinates extremely close to black.

    It can be noted, we don't bother plotting anything from the bottom
    disc of the HSV cylinder as they all resolve to pure black. It generates
    a lot of redundant points.
    """

    x, y, z = data

    # Resolution increase in non-hue channels helps smooth out some spaces a bit more.
    res2 = int(resolution * 1.5)

    color = Color('srgb', [])
    is_labish = issubclass(Color.CS_MAP[space], Labish)
    add = add_color if is_labish else add_cyl_color

    # We are rendering the spaces using sRGB, so just do a shell by picking
    # all the colors on the outside of the sRGB space. Render will be hollow.
    for c1, c2 in itertools.product(
        ((x / resolution) * 360 for x in range(0, resolution + 1)),
        ((x / res2) for x in range(0, res2 + 1))):

        # Only the top disc provides useful points, everything in the bottom just yields black.
        add(space, color.update('hsv', [c1, c2, 1]), x, y, z, c)
        add(space, color.update('hsv', [c1, 1, c2]), x, y, z, c)
        add(space, color.update('hsv', [c1, 1, c2 * 0.005]), x, y, z, c)
Exemplo n.º 2
0
def run(target, rgb, res):
    """Run."""

    max_x = float('-inf')
    max_y = float('-inf')
    max_z = float('-inf')
    min_x = float('inf')
    min_y = float('inf')
    min_z = float('inf')

    print('-> Current:', end="")
    x = y = z = 0
    color = Color(rgb, [0, 0, 0])
    while True:
        color.update(rgb, [x / res, y / res, z / res])
        print('\rCurrent: {}'.format(color.to_string(color=True)), end="")
        cx, cy, cz = color.convert(target).coords()
        if cx < min_x:
            min_x = cx
        if cy < min_y:
            min_y = cy
        if cz < min_z:
            min_z = cz
        if cx > max_x:
            max_x = cx
        if cy > max_y:
            max_y = cy
        if cz > max_z:
            max_z = cz

        if x == y == z == res:
            break
        elif y == z == res:
            x += 1
            y = z = 0
        elif z == res:
            y += 1
            z = 0
        else:
            z += 1

    print('')
    chan_x, chan_y, chan_z = Color('white').convert(
        target)._space.CHANNEL_NAMES
    print('---- {} range in {} ----'.format(target, rgb))
    print('{}: [{}, {}]'.format(chan_x, util.round_half_up(min_x, 3),
                                util.round_half_up(max_x, 3)))
    print('{}: [{}, {}]'.format(chan_y, util.round_half_up(min_y, 3),
                                util.round_half_up(max_y, 3)))
    print('{}: [{}, {}]'.format(chan_z, util.round_half_up(min_z, 3),
                                util.round_half_up(max_z, 3)))

    return 0
Exemplo n.º 3
0
def render_cart_space(space, resolution, data, c):
    """Render the space with the given resolution and factor."""

    x, y, z = data

    # We are rendering the spaces using sRGB, so just do a shell by picking
    # all the colors on the outside of the sRGB space. Render will be hollow.
    color = Color('srgb', [])
    for c1, c2 in itertools.product(
        (x / resolution for x in range(0, resolution + 1)),
        (x / resolution for x in range(0, resolution + 1))):

        add_color(space, color.update('srgb', [0, c1, c2]), x, y, z, c)
        add_color(space, color.update('srgb', [1, c1, c2]), x, y, z, c)

        add_color(space, color.update('srgb', [c1, 0, c2]), x, y, z, c)
        add_color(space, color.update('srgb', [c1, 1, c2]), x, y, z, c)

        add_color(space, color.update('srgb', [c1, c2, 0]), x, y, z, c)
        add_color(space, color.update('srgb', [c1, c2, 1]), x, y, z, c)
Exemplo n.º 4
0
def render_srgb_cyl_space(space, resolution, factor, data, c):
    """
    Render the sRGB cylindrical space: HSL, HSV, HWB, etc.

    Will render cylinder with the caps on the top and bottom.
    """

    x, y, z = data
    factor = 100 if space in ('hsluv', ) else 1

    # Render the cylinder by iterating through the hues and mapping them at the farthest
    # point from the center creating a hollow cylinder. Also, render the top and bottom disc caps.
    color = Color("srgb", [])
    for c1, t in itertools.product(
        ((x / resolution) * 360 for x in range(0, resolution + 1)),
        (((x / resolution) * factor, i)
         for i, x in enumerate(range(0, resolution + 1), 0))):

        # Offset the plot on every other iteration blend the rows into a mesh
        # Better looking when low resolution zoomed into higher resolution
        c2, count = t
        if count % 2 and c1 < 360:
            c1 += (360 / resolution) * 0.5

        # Top disc
        x.append(c2 * math.sin(math.radians(c1)))
        y.append(c2 * math.cos(math.radians(c1)))
        z.append(factor)
        c.append(color.update(space, [c1, c2, factor]).to_string(hex=True))

        # Bottom disc
        x.append(c2 * math.sin(math.radians(c1)))
        y.append(c2 * math.cos(math.radians(c1)))
        z.append(0)
        c.append(color.update(space, [c1, c2, 0]).to_string(hex=True))

        # Cylinder portion
        x.append(factor * math.sin(math.radians(c1)))
        y.append(factor * math.cos(math.radians(c1)))
        z.append(c2)
        c.append(color.update(space, [c1, factor, c2]).to_string(hex=True))
Exemplo n.º 5
0
def plot_slice(space,
               channel0,
               channel1,
               channel2,
               gamut='srgb',
               resolution=500,
               dark=False,
               title=""):
    """Plot a slice."""

    res = resolution
    if not dark:
        plt.style.use('seaborn-darkgrid')
        default_color = 'black'
    else:
        plt.style.use('dark_background')
        default_color = 'white'

    figure = plt.figure()

    # Create a color object based on the specified space.
    c = Color(space, [])

    # Parse the channel strings into actual values
    name0, value = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel0.split(":"), 0)
    ]
    name1, factor1, offset1 = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel1.split(':'), 0)
    ]
    name2, factor2, offset2 = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel2.split(':'), 0)
    ]

    # Get the actual indexes of the specified channels
    name0 = c._space.CHANNEL_ALIASES.get(name0, name0)
    index0 = c._space.CHANNEL_NAMES.index(name0)
    name1 = c._space.CHANNEL_ALIASES.get(name1, name1)
    index1 = c._space.CHANNEL_NAMES.index(name1)
    name2 = c._space.CHANNEL_ALIASES.get(name2, name2)
    index2 = c._space.CHANNEL_NAMES.index(name2)

    # Arrays for data points to plot
    c_map = []
    xaxis = []
    yaxis = []

    # Track minimum and maximum values
    c1_mn = float('inf')
    c1_mx = float('-inf')
    c2_mn = float('inf')
    c2_mx = float('-inf')

    # Track the edge of the graphed shape.
    edge_map = {}

    # Iterate through the two specified channels
    for c1, c2 in itertools.product(
        ((x / res * factor1) + offset1 for x in range(0, res + 1)),
        ((x / res * factor2) + offset2 for x in range(0, res + 1))):
        # Set the appropriate channels and update the color object
        coords = [NaN] * 3
        coords[index0] = value
        coords[index1] = c1
        coords[index2] = c2
        c.update(space, coords)

        # Only process colors within the gamut of sRGB.
        if c.in_gamut(gamut, tolerance=0) and not needs_lchuv_workaround(c):
            # Get the absolute min and max value plotted
            if c1 < c1_mn:
                c1_mn = c1
            if c1 > c1_mx:
                c1_mx = c1

            if c2 < c2_mn:
                c2_mn = c2
            if c2 > c2_mx:
                c2_mx = c2

            # Create an edge map so we can draw an outline
            if c1 not in edge_map:
                mn = mx = c2
            else:
                mn, mx = edge_map[c1]
                if c2 < mn:
                    mn = c2
                elif c2 > mx:
                    mx = c2
            edge_map[c1] = [mn, mx]

            # Save the points
            xaxis.append(c1)
            yaxis.append(c2)
            c_map.append(c.convert('srgb').to_string(hex=True))

    # Create a border around the data
    xe = []
    ye = []
    xtemp = []
    ytemp = []
    for p1, edges in edge_map.items():
        xe.append(p1)
        ye.append(edges[0])
        xtemp.append(p1)
        ytemp.append(edges[1])
    xe.extend(reversed(xtemp))
    ye.extend(reversed(ytemp))
    xe.append(xe[0])
    ye.append(ye[0])

    ax = plt.axes(xlabel='{}: {} - {}'.format(name1, c1_mn, c1_mx),
                  ylabel='{}: {} - {}'.format(name2, c2_mn, c2_mx))
    ax.set_aspect('auto')
    figure.add_axes(ax)

    if not title:
        title = "A Slice of '{}' and '{}' in the {} Color Space".format(
            name1, name2, space)
    title += '\n{}: {}'.format(name0, value)
    plt.title(title)

    plt.plot(xe,
             ye,
             color=default_color,
             marker="",
             linewidth=2,
             markersize=0,
             antialiased=True)

    plt.scatter(xaxis, yaxis, marker="o", color=c_map, s=2)
Exemplo n.º 6
0
def cie_diagram(mode="1931",
                observer="2deg",
                colorize=True,
                opacity=1,
                rgb_spaces=None,
                white_points=None,
                theme='light',
                title='',
                show_labels=True,
                axis=True,
                show_legend=True,
                black_body=False):
    """CIE diagram."""

    opt = DiagramOptions(theme=theme,
                         mode=mode,
                         observer=observer,
                         title=title)
    figure = plt.figure()
    ax = plt.axes(xlabel=opt.axis_labels[0], ylabel=opt.axis_labels[1])
    ax.set_aspect('equal')
    if axis is False:
        plt.axis('off')
    figure.add_axes(ax)
    plt.title(opt.title)
    if show_labels:
        plt.margins(0.15)

    xs = []
    ys = []
    annotations = []

    # Get points for the spectral locus
    for k, v in opt.observer.items():
        # Get the XYZ values in the correct format
        if opt.mode == "1931":
            x, y = util.xyz_to_xyY(v, (0.31270, 0.32900))[:2]
            xs.append(x)
            ys.append(y)
        elif opt.mode == "1976":
            x, y = util.xyz_to_uv(v)
            xs.append(x)
            ys.append(y)
        else:
            x, y = util.xy_to_uv_1960(
                util.xyz_to_xyY(v, (0.31270, 0.32900))[:2])
            xs.append(x)
            ys.append(y)

        # Prepare annotation labels for all points on the spectral locus.
        if k in labels:
            annotations.append((k, (x, y)))

    xs, ys = get_spline(xs, ys, len(xs) * 3)

    # Draw the bottom purple line
    xs.append(xs[0])
    ys.append(ys[0])

    # Generate fill colors for inside the spectral locus
    if colorize:
        px = []
        py = []
        c = []
        path = mpltpath.Path(list(zip(xs, ys)))
        for r in itertools.product(
            (x / RESOLUTION for x in range(0, RESOLUTION + 1)),
            (x / RESOLUTION for x in range(0, RESOLUTION + 1))):
            srgb = Color('srgb', [])
            if path.contains_point(r):
                if opt.mode == "1931":
                    xyz = util.xy_to_xyz(r)
                elif opt.mode == "1976":
                    xyz = util.xy_to_xyz(util.uv_to_xy(r))
                else:
                    xyz = util.xy_to_xyz(util.uv_1960_to_xy(r))
                px.append(r[0])
                py.append(r[1])
                srgb.update('xyz-d65', xyz, opacity)
                m = max(srgb.coords())
                srgb.update('srgb', [(i / m if m != 0 else 0)
                                     for i in srgb.coords()], srgb.alpha)
                c.append(srgb.to_string(hex=True, fit="clip"))
        plt.scatter(px, py, edgecolors=None, c=c, s=1)

    # Plot spectral locus and label it
    plt.plot(xs,
             ys,
             color=opt.locus_line_color if colorize else opt.default_color,
             marker="",
             linewidth=1.5,
             markersize=2,
             antialiased=True)

    if show_labels:
        # Label points
        ax = []
        ay = []
        for annotate in annotations:
            offset = opt.locus_labels(annotate[0])
            ax.append(annotate[1][0])
            ay.append(annotate[1][1])
            plt.annotate('{:d}'.format(annotate[0]),
                         annotate[1],
                         size=8,
                         color=opt.locus_label_color,
                         textcoords="offset points",
                         xytext=offset,
                         ha='center')
        plt.scatter(
            ax,
            ay,
            marker=".",
            color=opt.locus_point_color if not colorize else opt.default_color,
            zorder=100)

    # Add any specified white points.
    if white_points:
        wx = []
        wy = []
        annot = []
        for wp in white_points:
            w = WHITES[wp]
            annot.append(wp)
            if opt.mode == '1931':
                wx.append(w[0])
                wy.append(w[1])
            elif opt.mode == '1976':
                uv = util.xyz_to_uv(util.xy_to_xyz(w))
                wx.append(uv[0])
                wy.append(uv[1])
            else:
                uv = util.xy_to_uv_1960(w)
                wx.append(uv[0])
                wy.append(uv[1])
        plt.scatter(wx,
                    wy,
                    marker=".",
                    color=opt.default_colorized_color
                    if colorize else opt.default_color)
        for pt, a in zip(zip(wx, wy), annot):
            plt.annotate(a,
                         pt,
                         size=8,
                         color=opt.default_colorized_color
                         if colorize else opt.default_color,
                         textcoords="offset points",
                         xytext=(15, -3),
                         ha='center')

    # Show the black body (Planckian locus) curve
    # Work in progress.
    if black_body and opt.mode != '1976':
        uaxis = []
        vaxis = []
        bres = 20
        boffset = 1000
        brange = 24000
        for cct in range(0, bres + 1):
            t = cct / bres * brange + boffset
            bu, bv = black_body_curve(t, opt.mode)
            uaxis.append(bu)
            vaxis.append(bv)
        uaxis, vaxis = get_spline(uaxis, vaxis, len(uaxis) * 4)
        plt.plot(uaxis,
                 vaxis,
                 color=opt.default_colorized_color
                 if colorize else opt.default_color,
                 marker="",
                 linewidth=1,
                 markersize=0,
                 antialiased=True)
        # `plt.scatter(uaxis, vaxis, c=opt.default_color)`

    # Draw RGB triangles if one is specified
    if rgb_spaces:
        temp = Color('srgb', [])
        for space, color in rgb_spaces:
            if opt.mode == '1931':
                red = temp.mutate(space, [1, 0, 0]).xy()
                green = temp.mutate(space, [0, 1, 0]).xy()
                blue = temp.mutate(space, [0, 0, 1]).xy()
            elif opt.mode == '1976':
                red = temp.mutate(space, [1, 0, 0]).uv()
                green = temp.mutate(space, [0, 1, 0]).uv()
                blue = temp.mutate(space, [0, 0, 1]).uv()
            else:
                red = temp.mutate(space, [1, 0, 0]).uv('1960')
                green = temp.mutate(space, [0, 1, 0]).uv('1960')
                blue = temp.mutate(space, [0, 0, 1]).uv('1960')
            plt.plot([red[0], green[0], blue[0], red[0]],
                     [red[1], green[1], blue[1], red[1]],
                     marker='o',
                     color=color,
                     label=space,
                     linewidth=2,
                     markersize=0,
                     path_effects=[
                         path_effects.SimpleLineShadow(alpha=0.2,
                                                       offset=(1, -1)),
                         path_effects.Normal()
                     ])

    # We current only add labels when drawing RGB triangles
    if rgb_spaces and show_legend:
        ax.legend()