Suggested enhancement to imshow shading (with possible code)

I’ve found matplotlib to be a very nice tool. In particular, I’ve been using the LightSource class in colors.py.

However, LightSource.shade always uses the array min and max to set the minimum and maximum colormap values. I’d like to be able to manually set minimum and maximum values used in the colormap, and I’d also be like to limit the elevation for the shading algorithm. I have three use cases:

(1) comparing related images, which is much easier to do when the colormaps are constant

(2) cases where I have very high or very low values

(3) cases where I’ve used the set_over or set_under methods of the colormap because low and/or high values are significant.

Since it was fairly easy to modify this code myself, I did so. This modification might be useful to other people, too, so I was wondering if it would be possible to modify LightSource.shade similar to the code below.

def shade(self,data,cmap,vmin=None,vmax=None,limit_elevation=False):

    """

    Take the input data array, convert to HSV values in the

    given colormap, then adjust those color values

    to given the impression of a shaded relief map with a

    specified light source.

    RGBA values are returned, which can then be used to

    plot the shaded image with imshow.

    Parameters

colors.py (44.4 KB)

shading2.py (1.38 KB)

shading3.py (1.5 KB)

shading4.py (1.66 KB)

···
    ----------

    data            Input data array

    vmin            Minimum data value for colormap.  Default min(data).

    vmax            Maximum data value for colormap.  Default max(data).

    limit_elevation Limit the elevation in the shading routine? Default False.

                    If true, the elevation will be limited by vmin and vmax.

    Returns

    -------

    rgb             Shaded RGBA values, suitable for use with imshow.

    """

    if (vmin is not None) or (vmax is not None):

        limitschanged = True

    else:

        limitschanged = False

    if vmin is None:

        vmin = np.min(data)

    if vmax is None:

        vmax = np.max(data)

    rgb0 = cmap((data-vmin)/(np.float(vmax-vmin)))

    #avoid using extra memory if copy of array not needed

    if limitschanged and limit_elevation:

        d = data.copy()

        d[d<vmin] = vmin

        d[d>vmax] = vmax

        rgb1 = self.shade_rgb(rgb0, elevation=d)

    else:

        rgb1 = self.shade_rgb(rgb0, elevation=data)

    rgb0[:,:,0:3] = rgb1

    return rgb0

I’ve attached a colors.py with a modified LightSource.shade and some examples (shading3.py and shading4.py) that use the modifications to shade.

This is the first time I’ve suggested a change to matplotlib, so please let me know if there was a better way to make this suggestion.

Regards,

Kathy

Kathy,

It is great that you have found mpl to be useful, and better still that you found a way to improve it.

The recommended way to submit changes is through github. The developers’ guide at matplotlib.sourceforge.net will describe how to do that.

At the very least, diff patches are needed so that we can be sure we make exactly the right changes.

Welcome!

Ben Root

···

On Thursday, February 16, 2012, Kathleen M Tacina wrote:

I’ve found matplotlib to be a very nice tool. In particular, I’ve been using the LightSource class in colors.py.

However, LightSource.shade always uses the array min and max to set the minimum and maximum colormap values. I’d like to be able to manually set minimum and maximum values used in the colormap, and I’d also be like to limit the elevation for the shading algorithm. I have three use cases:

(1) comparing related images, which is much easier to do when the colormaps are constant

(2) cases where I have very high or very low values

(3) cases where I’ve used the set_over or set_under methods of the colormap because low and/or high values are significant.

Since it was fairly easy to modify this code myself, I did so. This modification might be useful to other people, too, so I was wondering if it would be possible to modify LightSource.shade similar to the code below.

def shade(self,data,cmap,vmin=None,vmax=None,limit_elevation=False):

    """

    Take the input data array, convert to HSV values in the

    given colormap, then adjust those color values

    to given the impression of a shaded relief map with a

    specified light source.

    RGBA values are returned, which can then be used to

    plot the shaded image with imshow.



    Parameters

    ----------

    data            Input data array

    vmin            Minimum data value for colormap.  Default min(data).

    vmax            Maximum data value for colormap.  Default max(data).

    limit_elevation Limit the elevation in the shading routine? Default False.

                    If true, the elevation will be limited by vmin and vmax.



    Returns

    -------

    rgb             Shaded RGBA values, suitable for use with imshow.



    """



    if (vmin is not None) or (vmax is not None):

        limitschanged = True

    else:

        limitschanged = False

    if vmin is None:

        vmin = np.min(data)

    if vmax is None:

        vmax = np.max(data)

    rgb0 = cmap((data-vmin)/(np.float(vmax-vmin)))

    #avoid using extra memory if copy of array not needed

    if limitschanged and limit_elevation:

        d = data.copy()

        d[d<vmin] = vmin

        d[d>vmax] = vmax

        rgb1 = self.shade_rgb(rgb0, elevation=d)

    else:

        rgb1 = self.shade_rgb(rgb0, elevation=data)

    rgb0[:,:,0:3] = rgb1

    return rgb0

I’ve attached a colors.py with a modified LightSource.shade and some examples (shading3.py and shading4.py) that use the modifications to shade.

This is the first time I’ve suggested a change to matplotlib, so please let me know if there was a better way to make this suggestion.

Regards,

Kathy