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
seaborn-image/src/seaborn_image/_general.py
Line 302 in 4ecefc8
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.