J. Mendoza
J. Mendoza

Reputation: 91

How to combine 3 TIFF images into 1 PNG image with python?

I have 1 tif image for each RGB colour channel, and I would like to combine the 3 images to make a single RGB image with all 3 channels in png format using python. I have tried several experiments using the PIL library but I can't get it.

I uploaded 3 sample images to Google Drive here. Does anyone know how to do this?

Upvotes: 1

Views: 1703

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207668

The answer depends on what you are really trying to achieve...

If you want an accurate merge of the 3 channels, you should probably use the tifffile module to understand the floating point values in your input files and accurately represent them in your output files. In fact, gdal would probably be even better as it understands the GeoTIFF tags in your file. PIL is unable to handle RGB float32 images.

If you want something that vaguely allows some sort of approximate visualisation as a PNG, you will need to do some work to scale your values to something sensible (but not accurate) because PNG cannot represent float data like your images contain.


Here is a more accurate merge of your channels with tifffile:

from tifffile import imread, imwrite
import numpy as np

r = imread('r.tif')
g = imread('g.tif')
b = imread('b.tif')
RGB = np.dstack((r,g,b))
imwrite('result.tif', RGB)

With PIL you would use Image.merge() but your data is float, so you will need to convert it to uint8/uint16 first to get something you can store in a PNG:

from PIL import Image
import numpy as np

# Open images
red   = Image.open('red_channel.tif')
green = Image.open('green_channel.tif')
blue  = Image.open('blue_channel.tif')

# Convert PIL Images to Numpy arrays
npRed   = np.array(red)
npGreen = np.array(green)
npBlue  = np.array(blue)

# Get rid of the pesky -3.4e+38 marker for out-of-bounds pixels
npRed[npRed < 0]     = 0
npBlue[npBlue < 0]   = 0
npGreen[npGreen < 0] = 0

# Find maximum across all channels for scaling
max = np.max([npRed,npGreen,npBlue])

# Scale all channels equally to range 0..255 to fit in a PNG (could use 65,535 and np.uint16 instead)
R = (npRed * 255/max).astype(np.uint8)
G = (npGreen * 255/max).astype(np.uint8)
B = (npBlue * 255/max).astype(np.uint8)

# Build a PNG
RGB = np.dstack((R,G,B))
Image.fromarray(RGB).save('result.png')

enter image description here

Upvotes: 1

Related Questions