SarthakJariwala/seaborn-image

Add support for robust plotting of RGB images

robintw opened this issue · 1 comments

Firstly, thanks for this wonderful library. If it had been around during my PhD it would have saved me a lot of time!

My PhD was in satellite imaging, and I still work in that area today. When visualising satellite images, we often want to apply a robust image stretch (eg. a 2%-98% percentile stretch) to an RGB image. The way this works is by doing a stretch on each band separately, and then combining them together.

This doesn't seem to be possible with seaborn_image at the moment: I notice that in

robust = False # set robust to False if RGB image
, robust is set to False if it's a RGB image.

I've had a bit of a poke around in the code, and it seems like the robust image display is done using vmax and vmin parameters to matplotlib. It looks like those probably don't support doing a different stretch on each band - so it would probably have to be done manually, making it a bit more difficult.

Anyway, I might have time to play around with this sometime (depending on work constraints) - would this be something you'd be interested in? It might require a change to how the library deals with robust plotting.

Thank you!

Your interpretation of the robust parameter is correct. It is not transforming the data but merely altering the colormap vmin and vmax display. The primary intention behind robust is to ignore any outliers.

I think what you propose would be of interest, perhaps as a separate stretch or scale_int parameter which performs the intensity rescale based on the percentile specified to the perc parameter, instead of changing robust plotting.

For the time being, there is an existing workaround. You can map a function to your input image using the map_func parameter in imgplot(). For instance, for the specific case you mentioned (as I understand it), you could try the following -

from skimage.exposure import rescale_intensity
import numpy as np
import seaborn_image as isns

def stretch(rgb_data, p=(2,98)):
    rescaled_data = np.zeros_like(rgb_data)
    for i in range(rgb_data.shape[-1]):
        _lower, _upper = np.nanpercentile(rgb_data[:, :, i], p)
        rescaled_data[:, :, i] = exposure.rescale_intensity(rgb_data[:, :, i], in_range=(_lower, _upper))
    return rescaled_data

ax = isns.imgplot(rgb_data, map_func=stretch, p=(2,98))

Additionally, if you are interested in viewing the individual bands/channels, you can try ImageGrid() :

g = isns.ImageGrid(rgb_data, robust=True, perc=(2,98))

Here, the robust parameter with be applied to all the individual bands and displayed separately. You can also use the map_func parameter to map your function of interest :

g = isns.ImageGrid(rgb_data, map_func=stretch, p=(2,98))

Overall, I agree that having an easier way to re-scale the intensity using a stretch or scale_int parameter would be helpful.