Repository: rasterio/rasterio

Issue:
Rasterio.plot.show() cannot deal properly with RGB and uint16 data. 
## Expected behavior and actual behavior.

Basically to `rasterio.plot.show()` to print any (masked) \*.tif file. 

#### What I did:
I downloaded an RGB raster file (\*.tif-format) from the Sentinel-hub. The data had four layers in `uint16` format - which rasterio didn't like. After trying a simple `np.uint8(src.read())`\*\* and plotting the output with plt.imshow(), the output figure contained a lot of noise. This was because the data also needed some normalization. I did this - as quick fix - through [OpenCV](https://github.com/skvark/opencv-python). 

Next _"error"_ was,  that I couldn't use cmap on an RGB *.tif, so I converted the RGB to grayscale with [scikit-image's rgb2gray function](https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_rgb_to_gray.html).

Then the masking did not really work well. I guess, here the misconception by rasterio is, that it reads the array as (3, X, Y) with 3 being the RGB dimension. On the otherhand, `plt.imshow()` rather wants the array to be (X, Y, 3) for RGB and (X, Y, 4) for RGB+Mask (or RGBA with A for alpha or transparency). Here, a simple `np.transpose()` helped out pretty well. 

I haven't pushed anything on GitHub, because

1. I never did that before and I think I rather destroy things than do any good.  

2. I am not a software engineer, using Python, but I am not a good coder. I guess there is still some improvements to be made, which I rather leave to the experts here. 

\*\* I'm  trying to follow the [rasterio.readthedocs.io Advanced Topics](https://rasterio.readthedocs.io/en/latest/topics/index.html) writing style

## Steps to reproduce the problem.

Read above.

## First bug-fix iteration.
`orthoname` is the input file
```
def printRaster(orthoname,**kwargs):
    if 'ax' in kwargs.keys():
        ax1=kwargs['ax']
        kwargs.pop('ax')
    else:
        ax1=plt.gca()        
    with rasterio.open(orthoname) as src:

        ortho = src.read()
        # if input is not 'uint8', then convert it to 'uint8'
        if src.meta['dtype'] != 'uint8':
            ## requires 3rd party package.
            import cv2
            ortho = cv2.normalize(src=ortho, dst=None, alpha=0, beta=255,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
        
        # if input is RGB but user wants cmap, then convert src image to grayscale - and then mask .tif
        if ortho.ndim > 2 and 'cmap' in kwargs.keys():
            ## requires 3rd party package. Easier to implement?
            from skimage.color import rgb2gray
            ortho = rgb2gray(np.transpose(ortho, (1, 2, 0)))
            image = np.ma.masked_where(src.read_masks(1) == 0, ortho)
        
        #otherwise, just mask
        else:
            image = np.ma.masked_where(src.read_masks(1) == 0, ortho)
            
        # plot in geo-coordinates
        extent = np.array( src.meta['transform'] * (0, 0) + src.meta['transform'] * (src.width,src.height) )[[0, 2, 3, 1]]

        
    ax1.imshow(image,extent=extent,**kwargs)
    return image
```

## Operating system

Ubuntu Linux 20.04 

## Rasterio version and provenance

Version 1.1.7


Discussion:
Yes, let's make 16 bit data plottable. https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html does the right thing for scalar arrays, we're just interfering with it.

I think that automatic conversion of 3 or 4 band 16-bit imagery to RGB or RGBA will remain out of scope for rasterio.

Resolving patch:
diff --git a/rasterio/plot.py b/rasterio/plot.py
--- a/rasterio/plot.py
+++ b/rasterio/plot.py
@@ -101,8 +101,10 @@ def show(source, with_bounds=True, contour=False, contour_label_kws=None,
                 colorinterp = rasterio.enums.ColorInterp
 
                 # Gather the indexes of the RGB channels in that order
-                rgb_indexes = [source_colorinterp[ci] for ci in
-                               (colorinterp.red, colorinterp.green, colorinterp.blue)]
+                rgb_indexes = [
+                    source_colorinterp[ci]
+                    for ci in (colorinterp.red, colorinterp.green, colorinterp.blue)
+                ]
 
                 # Read the image in the proper order so the numpy array
                 # will have the colors in the order expected by
@@ -126,10 +128,11 @@ def show(source, with_bounds=True, contour=False, contour_label_kws=None,
 
     if adjust and arr.ndim >= 3:
         # Adjust each band by the min/max so it will plot as RGB.
-        arr = reshape_as_raster(arr)
+        arr = reshape_as_raster(arr).astype("float64")
 
         for ii, band in enumerate(arr):
-            arr[ii] = adjust_band(band, kind='linear')
+            arr[ii] = adjust_band(band)
+
         arr = reshape_as_image(arr)
 
     show = False
@@ -149,8 +152,8 @@ def show(source, with_bounds=True, contour=False, contour_label_kws=None,
 
         if contour_label_kws is None:
             # no explicit label kws passed use defaults
-            contour_label_kws = dict(fontsize=8,
-                                     inline=True)
+            contour_label_kws = dict(fontsize=8, inline=True)
+
         if contour_label_kws:
             ax.clabel(C, **contour_label_kws)
     else:
@@ -321,26 +324,22 @@ def show_hist(source, bins=10, masked=True, title='Histogram', ax=None, label=No
         plt.show()
 
 
-def adjust_band(band, kind='linear'):
+def adjust_band(band, kind=None):
     """Adjust a band to be between 0 and 1.
 
     Parameters
     ----------
     band : array, shape (height, width)
         A band of a raster object.
-    kind : 'linear'
-        The kind of normalization to apply. For now, there
-        is only one option ('linear').
+    kind : str
+        An unused option. For now, there is only one option ('linear').
 
     Returns
     -------
     band_normed : array, shape (height, width)
         An adjusted version of the input band.
+
     """
-    band_normed = np.zeros_like(band)
-    if kind == 'linear':
-        # Including this `if` statement in case future norms are added.
-        imin = np.nanmin(band)
-        imax = np.nanmax(band)
-        band_normed = (band - imin) / (imax - imin)
-    return band_normed
+    imin = np.float64(np.nanmin(band))
+    imax = np.float64(np.nanmax(band))
+    return (band - imin) / (imax - imin)


Test patch: