5

I'm dealing with some satellite images, consisting of 16-bit .tiff images. The color is encoded as 16-bit per channel. I would like to know how I can convert these images to normal 8-bit RGB for further CNN processing.

I have tried OpenCV (cv2.read('file',-1)) and PIL (read('file')), but these two packages cannot recognize and read 16-bit tiff images.

john-hen
  • 4,410
  • 2
  • 23
  • 40
qqxlafk
  • 129
  • 1
  • 1
  • 6
  • "16bit tiff image to RGB"? tiff is a image file format, while RGB is a color space/Mode. It's quite unclear what you are trying to do with your `.tiff` file – Vasu Deo.S Jun 25 '19 at 18:44
  • @JohnHennig Yes, 16bit per channel – qqxlafk Jun 25 '19 at 19:32
  • Just to note, if you use satellite images you should work with rasterio package. Notice that in rasterio the first index is the channel, and to transform it to pillow for example you must do image = np.moveaxis(image, [0, 1, 2], [2, 1, 0]) – Learning from masters Jul 29 '22 at 10:13

3 Answers3

11

Generally, when you want to read or write images in Python — of any bit-depth and format — it is best to use ImageIO. As the name suggests, its singular goal is to input/output images. Only caveat: It may ignore the image's meta data. That is: It may not deal correctly with images defining a color space other than the standard sRGB, or it might fail to preserve the image's intended orientation.

You would read in the image, say example.tif, like so:

import imageio
image = imageio.imread('example.tif')

As for the conversion, that's just basic math. The data structure in which you'll receive the pixel data is a NumPy array. Introspect image.shape and image.dtype. You should expect your images to have a shape of (y, x, 3), where y is the number of pixels in the vertical, x in the horizontal direction, and 3 represents the three color channels: red, green, blue. Its dtype (data type) should be uint16, meaning unsigned 16-bit integers.

Side note: As there are three color channels, each sampled with a 16-bit resolution, the color depth of the image is more commonly described as "48 bits" (per pixel).

16-bit integer numbers range between 0 and 65535 (= 216−1). They need to be coerced to the 8-bit range: 0 to 255 (= 28−1). So divide by 256 (= 28):

image = image / 256

This will yield an array of floating-point pixel values. Its data type must be explicitly cast to 8-bit integer in order to drop any fractions.

image = image.astype('uint8')

Equivalently, and more efficiently, you may also bit-shift the 16-bit values 8 bits to the right:

image = (image >> 8).astype('uint8')

This makes the conversion faster (by a factor of 2 or so on modern hardware) as it skips the floating-point operations.

Then, either use the final image array for further processing, or save it to a new file:

imageio.imwrite('example.png', image)
john-hen
  • 4,410
  • 2
  • 23
  • 40
3

If all you want is to convert, your .tiff file's color space to RGB. Then Try:-

from PIL import Image

img = Image.open(r"Path_to_tiff_image")
img = img.convert("RGB")
img.save(r"path_of_destination_image")

The above code, first opens a .tiff image, then changes its color mode to RGB. And then saves it to the destination location.

Vasu Deo.S
  • 1,820
  • 1
  • 11
  • 23
  • 1
    PIL cannot recognize 16-bit tiff image...\n it gives error: cannot identify image file '/spacenet_sample/spacenet_sample/AOI_2_Vegas_Train/RGB-PanSharpen/RGB-PanSharpen_AOI_2_Vegas_img225.tif' – qqxlafk Jun 25 '19 at 19:27
  • 1
    @Qianhui Liang: Better yet, upload the image and put a link to it in your question. – martineau Jun 25 '19 at 19:48
  • @QianhuiLiang PIL, natively does support 16 bit 3 channel'd tiff images, if they are uncompressed, If an error occurs when you are trying to open your tiff files, then it may be an compressed one. [Read More About this](https://pillow.readthedocs.io/en/5.1.x/handbook/image-file-formats.html#tiff) in the official PIL `.tiff` documentation – Vasu Deo.S Jun 25 '19 at 20:02
3

Hey I used tifffile to handle the file and a calculation that I've found in a different thread here for rescaling the 16-bit image to 8-bit.

import numpy as np
import tifffile as tif
import cv2

image = tif.imread('/home/trance/test.tiff')

# Rescale 16-bit to 8-bit
img_rescaled = 255 * (image - image.min()) / (image.max() - image.min())

# Colourising image and saving it with opencv
img_col = cv2.applyColorMap(img_rescaled.astype(np.uint8), cv2.COLORMAP_INFERNO)
cv2.imwrite('/home/trance/test.png', img_col)
 
starryn1ght
  • 113
  • 4