Nour
Nour

Reputation: 307

Extracting RGB from a 4-band image (rasterio)

I am a beginner at python and I am still getting familiar with its libraries, apologies if this is a novice question. I simply want to display an image of 4 bands (R,G,B,NIR). I want to remove NIR and keep the other 3. How can I do this? Since rasterio returns an object and not an image, I am unable to use the split function.

from rasterio.plot import show
from matplotlib import pyplot
dataset = rasterio.open('sample.tif')
show(dataset)

This of course shows an incorrect result (white and yellow colored image).

Edit, I tried the following:

img_combined = cv2.merge((dataset.read(1),dataset.read(2),dataset.read(3)))
%matplotlib qt
plt.imshow(img_combined)
plt.xticks([]), plt.yticks([])
plt.show()

but it still shows the image in yellow and white.

Upvotes: 2

Views: 8105

Answers (3)

ryanjdillon
ryanjdillon

Reputation: 18968

Band order

The order of the bands matters, and they can be ordered differently depending on the TIFF. The data's documentation should outline what the band order is, and hopefully it can be read from the data itself (my experience is that is often not the case).

You could first try checking the band labels (though, you still want to refer to the docs).

>>>src.descriptions
(None, None, None, None)

If nothing is labeled there, you should refer to the data documentation.

Let's say there are 4 bands ordered as BGRN (as in NIR--near infrared); you would then want the first three bands but in reverse order.

data = src.read([3,2,1])

Normalizing and data types

If your bands are not in RGB order, it is likely that it is of 16bit integer datatype, and you would want 8bit for drawing via imshow (or writing to another image encoding, e.g. PNG and JPEG). First normalize the data to 0-255, then cast from uint16 to uint8. Note that by doing this you lose precision, so you want to do your analysis on the original data.

import numpy

def normalize(x, lower, upper):
    """Normalize an array to a given bound interval"""

    x_max = numpy.max(x)
    x_min = numpy.min(x)

    m = (upper - lower) / (x_max - x_min)
    x_norm = (m * (x - x_min)) + lower

    return x_norm

# Normalize each band separately
data_norm = numpy.array([normalize(data[i,:,:], 0, 255) for i in range(data.shape[0])])
data_rgb = data_norm.astype("uint8")

Image libraries and data dimensions

If you use a library other than rasterio, e.g. cv2 or PIL, the order that the bands are read in from RGB(A) images can differ, as well as the order of the dimensions. If you need to change the order of the dimensions for your plotting method, you can do the following

# Make the first (band) dimension the last
plt.imshow(numpy.moveaxis(data, 0, -1))

Upvotes: 2

joswhite
joswhite

Reputation: 35

In order to display only the RGB bands (not alpha) or any particular band you need to use the read method along with the show method.

import rasterio
from rasterio.plot import show

#to display RGB
dataset = rasterio.open('sample.tif')
show(dataset.read([1,2,3]))

#to display just the red band:
show(dataset.read(1))

Upvotes: 3

AlanWik
AlanWik

Reputation: 322

Normally the dataset is an array, in which each element is the band you are looking for. Given your code, dataset[0] is the R band, dataset[1] the G band, and so on.

So,

show(dataset[0])

will show the R band.

Upvotes: 0

Related Questions