def colormap(*, stretch_range: Tuple[Number, Number], colormap: str = None, num_values: int = 255) -> List[Dict[str, Any]]: """Returns a list [{value=pixel value, rgba=rgba tuple}] for given stretch parameters""" from terracotta import image target_coords = np.linspace(stretch_range[0], stretch_range[1], num_values) if colormap is not None: from terracotta.cmaps import get_cmap cmap = get_cmap(colormap) else: # assemble greyscale cmap of shape (255, 4) cmap = np.ones(shape=(255, 4), dtype='uint8') * 255 cmap[:, :-1] = np.tile( np.arange(1, 256, dtype='uint8')[:, np.newaxis], (1, 3)) cmap_coords = image.to_uint8(target_coords, *stretch_range) - 1 colors = cmap[cmap_coords] return [ dict(value=p, rgba=c) for p, c in zip(target_coords.tolist(), colors.tolist()) ]
def test_colormap_range(stretch_range): """Ensure that all colors from colormap file are present in returned data""" from terracotta.cmaps import get_cmap from terracotta.handlers import colormap cmap_name = 'jet' cmap_in = get_cmap(cmap_name) num_values = cmap_in.shape[0] cmap_out = colormap.colormap(colormap=cmap_name, stretch_range=stretch_range, num_values=num_values) cmap_out_arr = np.array([val['rgba'] for val in cmap_out], dtype=cmap_in.dtype) np.testing.assert_array_equal(cmap_in, cmap_out_arr)
def test_get_cmap(): from terracotta.cmaps import get_cmap cmap = get_cmap('jet') assert cmap.shape == (255, 3) assert cmap.dtype == np.uint8
def array_to_png(img_data: Array, colormap: Union[str, Palette, None] = None) -> BinaryIO: """Encode an 8bit array as PNG""" from terracotta.cmaps import get_cmap transparency: Union[Tuple[int, int, int], int, bytes] settings = get_settings() compress_level = settings.PNG_COMPRESS_LEVEL if img_data.ndim == 3: # encode RGB image if img_data.shape[-1] != 3: raise ValueError('3D input arrays must have three bands') if colormap is not None: raise ValueError( 'Colormap argument cannot be given for multi-band data') mode = 'RGB' transparency = (0, 0, 0) palette = None elif img_data.ndim == 2: # encode paletted image mode = 'L' if colormap is None: palette = None transparency = 0 else: if isinstance(colormap, str): # get and apply colormap by name try: cmap_vals = get_cmap(colormap) except ValueError as exc: raise exceptions.InvalidArgumentsError( f'Encountered invalid color map {colormap}') from exc palette = np.concatenate( (np.zeros(3, dtype='uint8'), cmap_vals[:, :-1].flatten())) transparency_arr = np.concatenate( (np.zeros(1, dtype='uint8'), cmap_vals[:, -1])) else: # explicit mapping if len(colormap) > 255: raise exceptions.InvalidArgumentsError( 'Explicit color map must contain less than 256 values') colormap_array = np.asarray(colormap, dtype='uint8') if colormap_array.ndim != 2 or colormap_array.shape[1] != 4: raise ValueError( 'Explicit color mapping must have shape (n, 4)') rgb, alpha = colormap_array[:, :-1], colormap_array[:, -1] palette = np.concatenate( (np.zeros(3, dtype='uint8'), rgb.flatten(), np.zeros(3 * (256 - len(colormap) - 1), dtype='uint8'))) # PIL expects paletted transparency as raw bytes transparency_arr = np.concatenate( (np.zeros(1, dtype='uint8'), alpha, np.zeros(256 - len(colormap) - 1, dtype='uint8'))) assert transparency_arr.shape == (256, ) assert transparency_arr.dtype == 'uint8' transparency = transparency_arr.tobytes() assert palette.shape == (3 * 256, ), palette.shape else: raise ValueError('Input array must have 2 or 3 dimensions') if isinstance(img_data, np.ma.MaskedArray): img_data = img_data.filled(0) img = Image.fromarray(img_data, mode=mode) if palette is not None: img.putpalette(palette) sio = BytesIO() img.save(sio, 'png', compress_level=compress_level, transparency=transparency) sio.seek(0) return sio
def test_get_cmap(): from terracotta.cmaps import get_cmap, AVAILABLE_CMAPS for name in AVAILABLE_CMAPS: cmap = get_cmap(name) assert cmap.shape == (255, 3) assert cmap.dtype == np.uint8