Skip to content

Instantly share code, notes, and snippets.

@jimfleming
Last active November 25, 2022 19:36
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jimfleming/c1adfdb0f526465c99409cc143dea97b to your computer and use it in GitHub Desktop.
Save jimfleming/c1adfdb0f526465c99409cc143dea97b to your computer and use it in GitHub Desktop.
A utility function for TensorFlow that maps a grayscale image to a matplotlib colormap for use with TensorBoard image summaries.
import matplotlib
import matplotlib.cm
import tensorflow as tf
def colorize(value, vmin=None, vmax=None, cmap=None):
"""
A utility function for TensorFlow that maps a grayscale image to a matplotlib
colormap for use with TensorBoard image summaries.
By default it will normalize the input value to the range 0..1 before mapping
to a grayscale colormap.
Arguments:
- value: 2D Tensor of shape [height, width] or 3D Tensor of shape
[height, width, 1].
- vmin: the minimum value of the range used for normalization.
(Default: value minimum)
- vmax: the maximum value of the range used for normalization.
(Default: value maximum)
- cmap: a valid cmap named for use with matplotlib's `get_cmap`.
(Default: 'gray')
Example usage:
```
output = tf.random_uniform(shape=[256, 256, 1])
output_color = colorize(output, vmin=0.0, vmax=1.0, cmap='viridis')
tf.summary.image('output', output_color)
```
Returns a 3D tensor of shape [height, width, 3].
"""
# normalize
vmin = tf.reduce_min(value) if vmin is None else vmin
vmax = tf.reduce_max(value) if vmax is None else vmax
value = (value - vmin) / (vmax - vmin) # vmin..vmax
# squeeze last dim if it exists
value = tf.squeeze(value)
# quantize
indices = tf.to_int32(tf.round(value * 255))
# gather
cm = matplotlib.cm.get_cmap(cmap if cmap is not None else 'gray')
colors = tf.constant(cm.colors, dtype=tf.float32)
value = tf.gather(colors, indices)
return value
@DEKHTIARJonathan
Copy link

Hello @jimflemming,

Thanks a lot for this gist, I was trying to use it for the colormap conversion.

However, I get this error with TF 1.4

File "##################################", line 375, in __create_summaries
    g_image_colorized =  colorize(self.G_GAN_network_train.outputs, vmin=0.0, vmax=1.0, cmap='gray')
  File "##################################", line 155, in colorize
    colors = tf.constant(cm.colors, dtype=tf.float32)
AttributeError: 'LinearSegmentedColormap' object has no attribute 'colors'

Do you have any idea where this could come from ?

Thanks for the help

@mickolka
Copy link

mickolka commented Jan 19, 2018

@DEKHTIARJonathan, the object returned by matplotlib.cm.get_cmap indeed has no attribute 'colors'; however, it may be called as a function accepting numpy arrays of grayscale values in [0, 255] (and returning pixel intensities in [0., 1.] for 4 channels (RGBA), not 3). So substituting line 48 of the gist with the following may work for you:

colors = cm(np.arange(256))[:, :3]
colors = tf.constant(colors, dtype=tf.float32)

Hope that helps.

@buckleytoby
Copy link

buckleytoby commented Mar 3, 2018

This is very cool, thanks!
Something that would help out is if you make it compatible with the normal (batch_size, width, height, channels=1) input for keras / tensorflow. The output then would be (batch_size, width, height, 3), creating one image per batch input.
Does this require much change?

Edit: I added this line which effectively did what I was describing:
out = tensorflow.map_fn(lambda img: colorize(img, cmap='jet'), out)

@ragulpr
Copy link

ragulpr commented Apr 2, 2018

Very helpful, thanks alot. Came here from Pytorch. This works with numpy/torch arrays and exports a (h,w,4)- int8 instead.

import matplotlib
import matplotlib.cm

def colorize(value, vmin=None, vmax=None, cmap=None):
    """
    A utility function for Torch/Numpy that maps a grayscale image to a matplotlib
    colormap for use with TensorBoard image summaries.
    By default it will normalize the input value to the range 0..1 before mapping
    to a grayscale colormap.
    Arguments:
      - value: 2D Tensor of shape [height, width] or 3D Tensor of shape
        [height, width, 1].
      - vmin: the minimum value of the range used for normalization.
        (Default: value minimum)
      - vmax: the maximum value of the range used for normalization.
        (Default: value maximum)
      - cmap: a valid cmap named for use with matplotlib's `get_cmap`.
        (Default: Matplotlib default colormap)
    
    Returns a 4D uint8 tensor of shape [height, width, 4].
    """

    # normalize
    vmin = value.min() if vmin is None else vmin
    vmax = value.max() if vmax is None else vmax
    if vmin!=vmax:
        value = (value - vmin) / (vmax - vmin) # vmin..vmax
    else:
        # Avoid 0-division
        value = value*0.
    # squeeze last dim if it exists
    value = value.squeeze()

    cmapper = matplotlib.cm.get_cmap(cmap)
    value = cmapper(value,bytes=True) # (nxmx4)
    return value

from tensorboardX import SummaryWriter
import torch
import numpy as np
# x = np.ones([100,100])
x = torch.ones([100,100])
colorized = colorize(x)

writer = SummaryWriter('runs/imagetest/')
writer.add_image('TEST', colorized, 1)

@theRealSuperMario
Copy link

when you remove line 41 value = tf.squeeze(value) it works on arbitrary input shapes and returns [original_shape, ] + [3]. Could be useful to write whole batches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment