From 15ef0295276e9884cfa6159c81dc834e6d50a803 Mon Sep 17 00:00:00 2001 From: David Reed Date: Sun, 19 Jan 2014 12:22:42 -0500 Subject: [PATCH] ENH: color brightening utility --- .../2017-12_color_brightening.rst | 9 ++++ examples/color/color_brighten_demo.py | 31 ++++++++++++ lib/matplotlib/colors.py | 48 +++++++++++++++++++ lib/matplotlib/tests/test_colors.py | 34 ++++++++++++- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 doc/users/next_whats_new/2017-12_color_brightening.rst create mode 100644 examples/color/color_brighten_demo.py diff --git a/doc/users/next_whats_new/2017-12_color_brightening.rst b/doc/users/next_whats_new/2017-12_color_brightening.rst new file mode 100644 index 000000000000..4d269ab23380 --- /dev/null +++ b/doc/users/next_whats_new/2017-12_color_brightening.rst @@ -0,0 +1,9 @@ +Color brightening +---------------------- + +Often we would like the ability add subtle color variation to our plots, +especially when similar element are plotted in close proximity to one another. +`matplotlib.colors.brighten_color` can be used to take an existing color and +brighten or darken it, by giving a positive or negative fraction. + +.. plot:: mpl_examples/color/color_brighten_demo.py diff --git a/examples/color/color_brighten_demo.py b/examples/color/color_brighten_demo.py new file mode 100644 index 000000000000..74edf1f4a694 --- /dev/null +++ b/examples/color/color_brighten_demo.py @@ -0,0 +1,31 @@ +""" +=================== +Brighten color demo +=================== + +Demo of `~.brighten_color` utility. + +""" + +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +import numpy as np + +fig, ax = plt.subplots() + +lightfacs = [-1., -0.5, 0., 0.5, 0.75, 1.0] +N = len(lightfacs) +y = np.linspace(0., 1., N+1) * N - 0.5 +for n, lightfac in enumerate(lightfacs): + brightened_color = mcolors.brighten_color('blue', lightfac) + ax.fill_between([0, 1], [y[n], y[n]], [y[n+1], y[n+1]], + facecolor=brightened_color, edgecolor='k') + +ax.set_yticklabels([''] + lightfacs) + +ax.set_xlim([0, 1]) +ax.set_ylim(np.min(y), np.max(y)) +ax.set_ylabel('Brightening Fraction') +ax.set_xticks([]) +ax.set_title('Brightening of Color Blue') +plt.show() diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 6da028e81b4f..14a69155837c 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -51,12 +51,14 @@ from six.moves import zip from collections import Sized +import colorsys import itertools import re import warnings import numpy as np import matplotlib.cbook as cbook + from ._color_data import BASE_COLORS, TABLEAU_COLORS, CSS4_COLORS, XKCD_COLORS @@ -1992,3 +1994,49 @@ def from_levels_and_colors(levels, colors, extend='neither'): norm = BoundaryNorm(levels, ncolors=n_data_colors) return cmap, norm + + +def brighten_color(color, frac): + """A color helper utility to either darken or brighten the given color. + + This function first converts the given color to RGB using + `~.colorConverter` and then to + HSL using `colorsys.rgb_to_hsl`. The lightness is modified according + to the given fraction, clipped to be between 0 and 1, and then converted + back to RGB using `colorsys.hsl_to_rgb`: + L = np.clip(L0 * (1.0 + frac), 0., 1.), where L0 is the original + lighness of the color in HSL space. + + Parameters + ---------- + color : string, list, hexvalue + Any acceptable Matplotlib color value, such as 'red', + 'slategrey', '#FFEE11', (1,0,0) + + frac : float + The amount by which to darken or brighten the color. Negative + darkens, positive brightens. Lowest valid negative value is -1. + Highest positive value is 1/L of the original color, where L is + the lightness in HSL space. Entries outside this range are allowed, + but the resulting lightness is clipped between 0. and 1. + + Returns + ------- + color : tuple of floats + tuple representing converted rgb values + + """ + + frac = float(frac) + if not np.isfinite(frac): + raise ValueError('argument frac must be a finite float') + + rgb = colorConverter.to_rgb(color) + + h, l, s = colorsys.rgb_to_hls(*rgb) + + l *= 1. + frac + + l = np.clip(l, 0., 1.) + + return colorsys.hls_to_rgb(h, l, s) diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 264d75ebe57a..f25c7edbe046 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -10,7 +10,8 @@ import numpy as np import pytest -from numpy.testing.utils import assert_array_equal, assert_array_almost_equal +from numpy.testing.utils import (assert_array_equal, + assert_array_almost_equal, assert_equal) from matplotlib import cycler import matplotlib @@ -715,3 +716,34 @@ def __add__(self, other): else: assert len(recwarn) == 0 recwarn.clear() + + +def _brighten_test_helper(color, shade, expected): + sc = mcolors.brighten_color(color, shade) + assert_equal(sc, expected) + + +def test_color_brightening(): + test_colors = ( + 'red', + 'red', + 'red', + 'red', + 'red', + [0, .5, .9], + 'slategrey', + ) + test_brighten = (0, 0.50, 1.00, -0.50, -1.00, 0.20, -0.20) + known_brighten_result = ( + (1.0, 0.0, 0.0), + (1.0, 0.5, 0.5), + (1.0, 1.0, 1.0), + (0.5, 0.0, 0.0), + (0.0, 0.0, 0.0), + (0.080000000000000071, 0.59111111111111092, 1.0), + (0.35097730430754981, 0.40156862745098038, 0.45215995059441105) + ) + for color, brighten, expected in zip(test_colors, + test_brighten, + known_brighten_result): + _brighten_test_helper(color, brighten, expected)