Source code for wbia.plottool.color_funcs

# -*- coding: utf-8 -*-
from wbia.plottool import custom_constants  # NOQA
from matplotlib import colors as mcolors
import colorsys
import numpy as np  # NOQA
import utool as ut

# from wbia.plottool import colormaps as cmaps2
# (print, print_, printDBG, rrr, profile) = utool.inject(__name__, '[colorfuncs]', DEBUG=False)
ut.noinject(__name__)
# '[colorfuncs]')


def _test_base01(channels):
    tests01 = {
        'is_float': all([ut.is_float(c) for c in channels]),
        'is_01': all([c >= 0.0 and c <= 1.0 for c in channels]),
    }
    return tests01


def _test_base255(channels):
    tests255 = {
        # 'is_int': all([ut.is_int(c) for c in channels]),
        'is_255': all([c >= 0.0 and c <= 255.0 for c in channels]),
    }
    return tests255


[docs]def is_base01(channels): """check if a color is in base 01""" if isinstance(channels, str): return False return all(_test_base01(channels).values())
[docs]def is_base255(channels): """check if a color is in base 01""" if isinstance(channels, str): return False return all(_test_base255(channels).values())
[docs]def assert_base01(channels): try: tests01 = _test_base01(channels) assert tests01['is_float'], 'channels must be floats' assert tests01['is_01'], 'channels must be in 0-1' except AssertionError as ex: ut.printex(ex, key_list=['channels', 'tests01']) raise
[docs]def assert_base255(channels): try: tests255 = _test_base255(channels) assert tests255['is_255'], 'channels must be in 0-255' except AssertionError as ex: ut.printex(ex, key_list=['channels', 'tests255']) raise
[docs]def to_base01(color255): """converts base 255 color to base 01 color""" color01 = [channel / 255.0 for channel in color255] return color01
[docs]def to_base255(color01, assume01=False): """converts base 01 color to base 255 color""" if not assume01: assert_base01(color01) color255 = list(map(int, [round(channel * 255.0) for channel in color01])) return color255
[docs]def ensure_base01(color): """always returns a base 01 color Note, some colors cannot be determined to be either 255 or 01 if they are in float format. Args: color (?): Returns: ?: color01 CommandLine: python -m wbia.plottool.color_funcs ensure_base01 Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> ensure_base01('g') >>> ensure_base01('orangered') >>> ensure_base01('#AAAAAA') >>> ensure_base01([0, 0, 0]) >>> ensure_base01([1, 1, 0, 0]) >>> ensure_base01([1., 1., 0., 0.]) >>> ensure_base01([.7, .2, 0., 0.]) """ if is_base01(color): color01 = color else: if isinstance(color, str) and color in mcolors.BASE_COLORS: # base colors are 01 based color01 = mcolors.BASE_COLORS[color] color01 = [float(c) for c in color01] else: color255 = ensure_base255(color) color01 = to_base01(color255) return color01
[docs]def convert_255_to_hex(color255): """ >>> color255 = [255, 51, 0] target_rgb01 = pt.FALSE_RED[0:3] target_rgb = np.array([[target_rgb01]]).astype(np.float32) / 25 target_lab = vt.convert_colorspace(target_rgb, 'lab', 'rgb') # Find closest CSS color in LAB space dist_lab = {} dist_rgb = {} css_colors = ub.map_vals(convert_hex_to_255, mcolors.CSS4_COLORS) for k, c in css_colors.items(): rgb = np.array([[c]]).astype(np.float32) / 255 lab = vt.convert_colorspace(rgb, 'lab', 'rgb') dist_lab[k] = np.sqrt(((target_lab - lab) ** 2).sum()) dist_rgb[k] = np.sqrt(((target_rgb - rgb) ** 2).sum()) best_keys = ub.argsort(dist_lab) ub.odict(zip(best_keys, ub.take(dist_lab, best_keys))) """ colorhex = '0x' + ''.join(['%02x' % c for c in color255]) return colorhex
[docs]def convert_hex_to_255(hex_color): """ hex_color = '#6A5AFFAF' """ assert hex_color.startswith('#'), 'not a hex string %r' % (hex_color,) parts = hex_color[1:].strip() color255 = tuple(int(parts[i : i + 2], 16) for i in range(0, len(parts), 2)) assert len(color255) in [3, 4], 'must be length 3 or 4' # # color = mcolors.hex2color(hex_color[0:7]) # if len(hex_color) > 8: # alpha_hex = hex_color[7:9] # alpha_float = int(alpha_hex, 16) / 255.0 # color = color + (alpha_float,) return color255
[docs]def ensure_base255(color): """ always returns a base 255 color Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> ensure_base255('g') >>> ensure_base255('orangered') >>> ensure_base255('#AAAAAA') >>> ensure_base255([0, 0, 0]) >>> ensure_base255([1, 1, 0, 0]) >>> ensure_base255([.9, 1., 0., 0.]) >>> ensure_base255([1., 1., 0., 0.]) # FIXME >>> ensure_base255([.7, .2, 0., 0.]) """ if isinstance(color, str): if color in mcolors.BASE_COLORS: # base colors are 01 based color01 = mcolors.BASE_COLORS[color] color255 = to_base255(color01, assume01=True) elif color in mcolors.CSS4_COLORS: # cs4 are hex based color_hex = mcolors.CSS4_COLORS[color] color255 = convert_hex_to_255(color_hex) elif color.startswith('#'): color255 = convert_hex_to_255(color) else: raise ValueError('unknown color=%r' % (color,)) elif is_base01(color): color255 = to_base255(color) else: color255 = color assert_base255(color255) return color255
[docs]def brighten_rgb(rgb, amount): hue_adjust = 0.0 sat_adjust = amount val_adjust = amount return adjust_hsv_of_rgb(rgb, hue_adjust, sat_adjust, val_adjust)
[docs]def testshow_colors(rgb_list, gray=ut.get_argflag('--gray')): """ colors = ['r', 'b', 'purple', 'orange', 'deeppink', 'g'] colors = list(mcolors.CSS4_COLORS.keys()) CommandLine: python -m wbia.plottool.color_funcs testshow_colors --show Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> colors = ut.get_argval('--colors', type_=list, default=['k', 'r']) >>> ut.quit_if_noshow() >>> rgb_list = ut.emap(ensure_base01, colors) >>> testshow_colors(rgb_list) >>> import wbia.plottool as pt >>> pt.show_if_requested() """ import wbia.plottool as pt import vtool as vt block = np.zeros((5, 5, 3)) block_list = [block + color[0:3] for color in rgb_list] # print(ut.repr2(block_list)) # print(ut.repr2(rgb_list)) chunks = ut.ichunks(block_list, 10) stacked_chunk = [] for chunk in chunks: stacked_chunk.append(vt.stack_image_list(chunk, vert=False)) stacked_block = vt.stack_image_list(stacked_chunk, vert=True) # convert to bgr stacked_block = stacked_block[:, :, ::-1] uint8_img = (255 * stacked_block).astype(np.uint8) if gray: import cv2 uint8_img = cv2.cvtColor(uint8_img, cv2.COLOR_RGB2GRAY) pt.imshow(uint8_img)
# pt.show_if_requested()
[docs]def desaturate_rgb(rgb, amount): r""" CommandLine: python -m wbia.plottool.color_funcs --test-desaturate_rgb --show Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> rgb = (255.0 / 255.0, 100 / 255.0, 0 / 255.0) >>> amount = .5 >>> new_rgb = desaturate_rgb(rgb, amount) >>> # xdoctest: +REQUIRES(--show) >>> color_list = [rgb, new_rgb, desaturate_rgb(rgb, .7)] >>> testshow_colors(color_list) >>> # verify results >>> result = ut.repr2(new_rgb) >>> print(result) (1.0, 0.696078431372549, 0.5) (1.0, 0.41599384851980004, 0.039215686274509776) """ hue_adjust = 0.0 sat_adjust = -amount val_adjust = 0.0 new_rgb = adjust_hsv_of_rgb(rgb, hue_adjust, sat_adjust, val_adjust) return new_rgb
[docs]def darken_rgb(rgb, amount): hue_adjust = 0.0 sat_adjust = 0.0 val_adjust = -amount new_rgb = adjust_hsv_of_rgb(rgb, hue_adjust, sat_adjust, val_adjust) return new_rgb
[docs]def lighten_rgb(rgb, amount): r""" CommandLine: python -m wbia.plottool.color_funcs --test-lighten_rgb --show python -m wbia.plottool.color_funcs --test-lighten_rgb Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> # build test data >>> rgb = np.array((255.0 / 255.0, 100 / 255.0, 0 / 255.0)) >>> amount = .1 >>> # execute function >>> new_rgb = lighten_rgb(rgb, amount) >>> import wbia.plottool as pt >>> if pt.show_was_requested(): >>> color_list = [rgb, new_rgb, lighten_rgb(rgb, .5)] >>> testshow_colors(color_list) >>> # verify results >>> result = ut.repr2(new_rgb, with_dtype=False) >>> print(result) """ hue_adjust = 0.0 sat_adjust = -amount val_adjust = amount new_rgb = adjust_hsv_of_rgb(rgb, hue_adjust, sat_adjust, val_adjust) return new_rgb
[docs]def adjust_hsv_of_rgb255(rgb255, *args, **kwargs): """ CommandLine: python -m wbia.plottool.color_funcs --test-adjust_hsv_of_rgb255 --show Example: >>> # DISABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> import wbia.plottool as pt >>> # build test data >>> rgb = (220, 220, 255) >>> hue_adjust = 0.0 >>> sat_adjust = -0.05 >>> val_adjust = 0.0 >>> # execute function >>> new_rgb = adjust_hsv_of_rgb255(rgb, hue_adjust, sat_adjust, val_adjust) >>> # verify results >>> result = str(new_rgb) >>> print(result) >>> import wbia.plottool as pt >>> if pt.show_was_requested(): >>> color_list = [to_base01(rgb), to_base01(new_rgb)] >>> testshow_colors(color_list) """ rgb = to_base01(rgb255) new_rgb = adjust_hsv_of_rgb(rgb, *args, **kwargs) new_rgb255 = to_base255(new_rgb) return new_rgb255
[docs]def adjust_hsv_of_rgb(rgb, hue_adjust=0.0, sat_adjust=0.0, val_adjust=0.0): """works on a single rgb tuple Args: rgb (tuple): hue_adjust (float): sat_adjust (float): val_adjust (float): Returns: ?: new_rgb CommandLine: python -m wbia.plottool.color_funcs --test-adjust_hsv_of_rgb --show Example: >>> # DISABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> import wbia.plottool as pt >>> # build test data >>> rgb_list = [pt.DEEP_PINK[0:3], pt.DARK_YELLOW[0:3], pt.DARK_GREEN[0:3]] >>> hue_adjust = -0.1 >>> sat_adjust = +0.5 >>> val_adjust = -0.1 >>> # execute function >>> new_rgb_list = [adjust_hsv_of_rgb(rgb, hue_adjust, sat_adjust, val_adjust) for rgb in rgb_list] >>> import wbia.plottool as pt >>> if pt.show_was_requested(): >>> color_list = rgb_list + new_rgb_list >>> testshow_colors(color_list) >>> # verify results >>> result = str(new_rgb) >>> print(result) Ignore: print(np.array([-.1, 0.0, .1, .5, .9, 1.0, 1.1])) print(np.array([-.1, 0.0, .1, .5, .9, 1.0, 1.1]) % 1.0) print(divmod(np.array([-.1, 0.0, .1, .5, .9, 1.0, 1.1]), 1.0)) print(1 + np.array([-.1, 0.0, .1, .5, .9, 1.0, 1.1]) % 1.0) """ assert_base01(rgb) # assert_base01([sat_adjust, val_adjust]) numpy_input = isinstance(rgb, np.ndarray) # For some reason numpy input does not work well if numpy_input: dtype = rgb.dtype rgb = rgb.tolist() # print('rgb=%r' % (rgb,)) alpha = None if len(rgb) == 4: (R, G, B, alpha) = rgb else: (R, G, B) = rgb hsv = colorsys.rgb_to_hsv(R, G, B) (H, S, V) = hsv H_new = H + hue_adjust if H_new > 0 or H_new < 1: # is there a way to more ellegantly get this? H_new %= 1.0 S_new = max(min(S + sat_adjust, 1.0), 0.0) V_new = max(min(V + val_adjust, 1.0), 0.0) # print('hsv=%r' % (hsv,)) hsv_new = (H_new, S_new, V_new) # print('hsv_new=%r' % (hsv_new,)) new_rgb = colorsys.hsv_to_rgb(*hsv_new) if alpha is not None: new_rgb = list(new_rgb) + [alpha] # print('new_rgb=%r' % (new_rgb,)) assert_base01(new_rgb) # Return numpy if given as numpy if numpy_input: new_rgb = np.array(new_rgb, dtype=dtype) return new_rgb
[docs]def brighten(*args, **kwargs): return brighten_rgb(*args, **kwargs)
[docs]def distinct_colors( N, brightness=0.878, randomize=True, hue_range=(0.0, 1.0), cmap_seed=None ): r""" Args: N (int): brightness (float): Returns: list: RGB_tuples CommandLine: python -m wbia.plottool.color_funcs --test-distinct_colors --N 2 --show --hue-range=0.05,.95 python -m wbia.plottool.color_funcs --test-distinct_colors --N 3 --show --hue-range=0.05,.95 python -m wbia.plottool.color_funcs --test-distinct_colors --N 4 --show --hue-range=0.05,.95 python -m wbia.plottool.color_funcs --test-distinct_colors --N 3 --show --no-randomize python -m wbia.plottool.color_funcs --test-distinct_colors --N 4 --show --no-randomize python -m wbia.plottool.color_funcs --test-distinct_colors --N 6 --show --no-randomize python -m wbia.plottool.color_funcs --test-distinct_colors --N 20 --show References: http://blog.jianhuashao.com/2011/09/generate-n-distinct-colors.html CommandLine: python -m wbia.plottool.color_funcs --exec-distinct_colors --show python -m wbia.plottool.color_funcs --exec-distinct_colors --show --no-randomize --N 50 python -m wbia.plottool.color_funcs --exec-distinct_colors --show --cmap_seed=foobar Example: >>> # ENABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> # build test data >>> N = ut.get_argval('--N', int, 2) >>> randomize = not ut.get_argflag('--no-randomize') >>> brightness = 0.878 >>> # execute function >>> cmap_seed = ut.get_argval('--cmap_seed', str, default=None) >>> hue_range = ut.get_argval('--hue-range', list, default=(0.00, 1.0)) >>> RGB_tuples = distinct_colors(N, brightness, randomize, hue_range, cmap_seed=cmap_seed) >>> # verify results >>> assert len(RGB_tuples) == N >>> result = str(RGB_tuples) >>> print(result) >>> ut.quit_if_noshow() >>> color_list = RGB_tuples >>> testshow_colors(color_list) >>> import wbia.plottool as pt >>> pt.show_if_requested() """ # TODO: Add sin wave modulation to the sat and value # import wbia.plottool as pt if True: import wbia.plottool as pt # HACK for white figures remove_yellow = not pt.is_default_dark_bg() # if not pt.is_default_dark_bg(): # brightness = .8 use_jet = False if use_jet: import wbia.plottool as pt cmap = pt.plt.cm.jet RGB_tuples = list(map(tuple, cmap(np.linspace(0, 1, N)))) elif cmap_seed is not None: # Randomized map based on a seed # cmap_ = 'Set1' # cmap_ = 'Dark2' choices = [ # 'Set1', 'Dark2', 'jet', # 'gist_rainbow', # 'rainbow', # 'gnuplot', # 'Accent' ] cmap_hack = ut.get_argval('--cmap-hack', type_=str, default=None) ncolor_hack = ut.get_argval('--ncolor-hack', type_=int, default=None) if cmap_hack is not None: choices = [cmap_hack] if ncolor_hack is not None: N = ncolor_hack N_ = N seed = sum(list(map(ord, ut.hashstr27(cmap_seed)))) rng = np.random.RandomState(seed + 48930) cmap_str = rng.choice(choices, 1)[0] # print('cmap_str = %r' % (cmap_str,)) cmap = pt.plt.cm.get_cmap(cmap_str) # ut.hashstr27(cmap_seed) # cmap_seed = 0 # pass jitter = (rng.randn(N) / (rng.randn(100).max() / 2)).clip(-1, 1) * ( (1 / (N ** 2)) ) range_ = np.linspace(0, 1, N, endpoint=False) # print('range_ = %r' % (range_,)) range_ = range_ + jitter # print('range_ = %r' % (range_,)) while not (np.all(range_ >= 0) and np.all(range_ <= 1)): range_[range_ < 0] = np.abs(range_[range_ < 0]) range_[range_ > 1] = 2 - range_[range_ > 1] # print('range_ = %r' % (range_,)) shift = rng.rand() range_ = (range_ + shift) % 1 # print('jitter = %r' % (jitter,)) # print('shift = %r' % (shift,)) # print('range_ = %r' % (range_,)) if ncolor_hack is not None: range_ = range_[0:N_] RGB_tuples = list(map(tuple, cmap(range_))) else: sat = brightness val = brightness hmin, hmax = hue_range if remove_yellow: hue_skips = [(0.13, 0.24)] else: hue_skips = [] hue_skip_ranges = [_[1] - _[0] for _ in hue_skips] total_skip = sum(hue_skip_ranges) hmax_ = hmax - total_skip hue_list = np.linspace(hmin, hmax_, N, endpoint=False, dtype=np.float) # Remove colors (like hard to see yellows) in specified ranges for skip, range_ in zip(hue_skips, hue_skip_ranges): hue_list = [hue if hue <= skip[0] else hue + range_ for hue in hue_list] HSV_tuples = [(hue, sat, val) for hue in hue_list] RGB_tuples = [colorsys.hsv_to_rgb(*x) for x in HSV_tuples] if randomize: ut.deterministic_shuffle(RGB_tuples) return RGB_tuples
[docs]def add_alpha(colors): return [list(color) + [1] for color in colors]
CMAP_DICT = dict( [ ('Perceptually Uniform Sequential', ['viridis', 'inferno', 'plasma', 'magma']), ( 'Sequential', [ 'Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', ], ), ( 'Sequential (2)', [ 'afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter', ], ), ( 'Diverging', [ 'BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic', ], ), ( 'Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3'], ), ( 'Miscellaneous', [ 'gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism', ], ), # ('New', ['magma', 'inferno', 'plasma', 'viridis']), ] )
[docs]def show_all_colormaps(): """ Displays at a 90 degree angle. Weird FIXME: Remove call to pylab References: http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps http://matplotlib.org/examples/color/colormaps_reference.html Notes: cmaps = [('Perceptually Uniform Sequential', ['viridis', 'inferno', 'plasma', 'magma']), ('Sequential', ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']), ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter']), ('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic']), ('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3']), ('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism']) ] CommandLine: python -m wbia.plottool.color_funcs --test-show_all_colormaps --show python -m wbia.plottool.color_funcs --test-show_all_colormaps --show --type=Miscellaneous python -m wbia.plottool.color_funcs --test-show_all_colormaps --show --cmap=RdYlBu Example: >>> # DISABLE_DOCTEST >>> from wbia.plottool.color_funcs import * # NOQA >>> import wbia.plottool as pt >>> show_all_colormaps() >>> pt.show_if_requested() """ from matplotlib import pyplot as plt import pylab import numpy as np pylab.rc('text', usetex=False) TRANSPOSE = True a = np.outer(np.arange(0, 1, 0.01), np.ones(10)) if TRANSPOSE: a = a.T pylab.figure(figsize=(10, 5)) if TRANSPOSE: pylab.subplots_adjust(right=0.8, left=0.05, bottom=0.01, top=0.99) else: pylab.subplots_adjust(top=0.8, bottom=0.05, left=0.01, right=0.99) type_ = ut.get_argval('--type', str, default=None) if type_ is None: maps = [m for m in pylab.cm.datad if not m.endswith('_r')] # maps += cmaps2.__all__ maps.sort() else: maps = CMAP_DICT[type_] print('CMAP_DICT = %s' % (ut.repr3(CMAP_DICT),)) cmap_ = ut.get_argval('--cmap', default=None) if cmap_ is not None: maps = [getattr(plt.cm, cmap_)] length = len(maps) + 1 for i, m in enumerate(maps): if TRANSPOSE: pylab.subplot(length, 1, i + 1) else: pylab.subplot(1, length, i + 1) # pylab.axis("off") ax = plt.gca() ax.set_xticks([]) ax.set_yticks([]) # try: cmap = pylab.get_cmap(m) # except Exception: # cmap = getattr(cmaps2, m) pylab.imshow(a, aspect='auto', cmap=cmap) # , origin="lower") if TRANSPOSE: ax.set_ylabel( m, rotation=0, fontsize=10, horizontalalignment='right', verticalalignment='center', ) else: pylab.title(m, rotation=90, fontsize=10)
# pylab.savefig("colormaps.png", dpi=100, facecolor='gray')