I have an array with 600×600×40 dimension that each band(from 40 band) represent a 600×600 image I want to save it to a multiple band .tif image. I have tried this functions
Mark's clever answer is making a multi-page TIFF. Unfortunately, imagemagick and PIL are really MONO / RGB / RGBA / CMYK libraries and they don't have direct support for multiband images.
pyvips has true multiband support. For example:
import sys
import pyvips
import numpy as np
# make a (100, 100, 40) numpy image
array = np.zeros((100, 100, 40), dtype=sys.argv[2])
# convert to vips and save
image = numpy2vips(array)
image.write_to_file(sys.argv[1])
# read it back, convert to numpy, and show info
image2 = pyvips.Image.new_from_file(sys.argv[1])
array = vips2numpy(image2)
print("shape =", array.shape)
print("format =", array.dtype)
I can run it like this:
$ ./try284.py x.tif uint8
shape = (100, 100, 40)
format = uint8
$ vipsheader x.tif
x.tif: 100x100 uchar, 40 bands, srgb, tiffload
$ identify x.tif
x.tif TIFF 100x100 100x100+0+0 8-bit sRGB 400KB 0.000u 0:00.000
It supports other dtypes as well:
$ ./try284.py x.tif uint32
shape = (100, 100, 40)
format = uint32
$ ./try284.py x.tif float32
shape = (100, 100, 40)
format = float32
etc. etc.
You can load these TIFFs in gdal. I guess gdal can be used to write them as well, though I've not tried. Annoyingly, it moves the 40 to the outermost dimension.
$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from osgeo import gdal
>>> x = gdal.Open("x.tif")
>>> a = x.ReadAsArray()
>>> a.shape
(40, 100, 100)
vips2numpy() and numpy2vips() are defined here:
https://github.com/libvips/pyvips/blob/master/examples/pil-numpy-pyvips.py
Copy-pasted for reference:
# map vips formats to np dtypes
format_to_dtype = {
'uchar': np.uint8,
'char': np.int8,
'ushort': np.uint16,
'short': np.int16,
'uint': np.uint32,
'int': np.int32,
'float': np.float32,
'double': np.float64,
'complex': np.complex64,
'dpcomplex': np.complex128,
}
# map np dtypes to vips
dtype_to_format = {
'uint8': 'uchar',
'int8': 'char',
'uint16': 'ushort',
'int16': 'short',
'uint32': 'uint',
'int32': 'int',
'float32': 'float',
'float64': 'double',
'complex64': 'complex',
'complex128': 'dpcomplex',
}
# numpy array to vips image
def numpy2vips(a):
height, width, bands = a.shape
linear = a.reshape(width * height * bands)
vi = pyvips.Image.new_from_memory(linear.data, width, height, bands,
dtype_to_format[str(a.dtype)])
return vi
# vips image to numpy array
def vips2numpy(vi):
return np.ndarray(buffer=vi.write_to_memory(),
dtype=format_to_dtype[vi.format],
shape=[vi.height, vi.width, vi.bands])