diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f715e537..48e430b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Improvements - Harden the geojson annotation parser ([#1743](../../pull/1743)) +- Add more color palettes ([#1746](../../pull/1746)) ## 1.30.5 diff --git a/large_image/tilesource/utilities.py b/large_image/tilesource/utilities.py index eceb55a2a..1e4a4c412 100644 --- a/large_image/tilesource/utilities.py +++ b/large_image/tilesource/utilities.py @@ -618,7 +618,40 @@ def _arrayToPalette(palette: List[Union[str, float, Tuple[float, ...]]]) -> np.n return np.array(arr) -def getPaletteColors(value: Union[str, List[Union[str, float, Tuple[float, ...]]]]) -> np.ndarray: +def _mpl_lsc_to_palette(cmap: Any) -> List[str]: + """ + Convert a matplotlib colormap to a palette of hexcolors. + + :param cmap: a matplotlib LinearSegmentedColormap or ListedColormap. + :return: a list with hexadecimal color numbers. + """ + import matplotlib as mpl + + try: + if isinstance(cmap, mpl.colors.LinearSegmentedColormap): + ri = cast(Any, cmap)._segmentdata['red'] + gi = cast(Any, cmap)._segmentdata['green'] + bi = cast(Any, cmap)._segmentdata['blue'] + ai = cast(Any, cmap)._segmentdata.get('alpha', None) + pal: List[str] = [] + for idx in range(len(ri)): + r = int(round(float(ri[idx][-1]) * 255)) + g = int(round(float(gi[idx][-1]) * 255)) + b = int(round(float(bi[idx][-1]) * 255)) + if ai is not None: + a = int(round(float(ai[idx][-1]) * 255)) + entry = f'#{r:02X}{g:02X}{b:02X}{a:02X}' + else: + entry = f'#{r:02X}{g:02X}{b:02X}' + if not len(pal) or pal[-1] != entry: + pal.append(entry) + return pal + except Exception: + pass + return [mpl.colors.to_hex(cmap(i)) for i in range(cmap.N)] + + +def getPaletteColors(value: Union[str, List[Union[str, float, Tuple[float, ...]]]]) -> np.ndarray: # noqa """ Given a list or a name, return a list of colors in the form of a numpy array of RGBA. If a list, each entry is a color name resolvable by either @@ -659,9 +692,21 @@ def getPaletteColors(value: Union[str, List[Union[str, float, Tuple[float, ...]] cmap = (mpl.colormaps.get_cmap(str(value)) if hasattr(getattr( mpl, 'colormaps', None), 'get_cmap') else mpl.cm.get_cmap(str(value))) - palette = [mpl.colors.to_hex(cmap(i)) for i in range(cmap.N)] + palette = _mpl_lsc_to_palette(cmap) # type: ignore except (ImportError, ValueError, AttributeError): pass + if palette is None: + if str(value).startswith('tol.'): + key = value[4:] + try: + import tol_colors + + if key in tol_colors.colorsets: + palette = list(tol_colors.colorsets[key]) + elif key in tol_colors.TOLcmaps().namelist: + palette = _mpl_lsc_to_palette(tol_colors.tol_cmap(key)) # type: ignore + except ImportError: + pass if palette is None: raise ValueError('cannot be used as a color palette.: %r.' % value) return _arrayToPalette(palette) @@ -734,6 +779,13 @@ def getAvailableNamedPalettes(includeColors: bool = True, reduced: bool = False) palettes.add(key) except ImportError: pass + try: + import tol_colors + + palettes |= {f'tol.{key}' for key in tol_colors.colorsets} + palettes |= {f'tol.{key}' for key in tol_colors.TOLcmaps().namelist} + except ImportError: + pass if reduced: palettes = { key for key in palettes diff --git a/setup.py b/setup.py index 75377e9f3..7f3021f37 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def prerelease_local_scheme(version): 'memcached': ['pylibmc>=1.5.1 ; platform_system != "Windows"'], 'redis': ['redis>=4.5.5'], 'converter': [f'large-image-converter{limit_version}'], - 'colormaps': ['matplotlib'], + 'colormaps': ['matplotlib', 'tol_colors'], 'tiledoutput': ['pyvips'], 'performance': [ 'psutil>=4.2.0',