Opencv 如何将表示40波段图像的数组保存到.tif文件中

Opencv 如何将表示40波段图像的数组保存到.tif文件中,opencv,raster,tiff,scikit-image,Opencv,Raster,Tiff,Scikit Image,我有一个600×600×40维的数组,每个波段(40波段)代表一个600×600的图像 我想将其保存到多波段.tif图像中。 我从scikit image和openCV中尝试了此功能,但它们不能保存超过3个波段(作为RGB) 您可以将多个图像保存在一个TIFF文件中,每个图像表示一个波段(灰度),甚至多个波段(颜色),并使用PIL/Pillow,如下所示: from PIL import Image # Synthesize 8 dummy images, all greyscale, all

我有一个600×600×40维的数组,每个波段(40波段)代表一个600×600的图像 我想将其保存到多波段.tif图像中。 我从scikit image和openCV中尝试了此功能,但它们不能保存超过3个波段(作为RGB)


您可以将多个图像保存在一个TIFF文件中,每个图像表示一个波段(灰度),甚至多个波段(颜色),并使用PIL/Pillow,如下所示:

from PIL import Image
# Synthesize 8 dummy images, all greyscale, all same size but with varying brightness
size=(480,640)  
b1 = Image.new('L', size, color=10)                                                         
b2 = Image.new('L', size, color=20)                                                        
b3 = Image.new('L', size, color=30)                                                       
b4 = Image.new('L', size, color=40)                                                        
b5 = Image.new('L', size, color=50)                                                        
b6 = Image.new('L', size, color=60)                                                        
b7 = Image.new('L', size, color=70)                                                        
b8 = Image.new('L', size, color=80)                                                        

# Save all 8 to single TIFF file
b1.save('multi.tif', save_all=True, append_images=[b2,b3,b4,b5,b6,b7,b8]) 
im = Image.open('multi.tif')                                                               

im.seek(3) 
im.show()
# Synthesize dummy array of 40 images, each 600x600
nparr = np.random.randint(0,256,(600,600,40), dtype=np.uint8)

# Make PIL/Pillow image of first
a = Image.fromarray(nparr[:,:,0])

# Save whole lot in one TIF
a.save('multi.tif', save_all=True, append_images=[Image.fromarray(nparr[:,:,x]) for x in range(1,40)]) 
$ ./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
如果现在在命令行中使用ImageMagick检查该文件,则可以看到所有8个带区都存在:

magick identify multi.tif 
multi.tif[0] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[1] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[2] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[3] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[4] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[5] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[6] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000
multi.tif[7] TIFF 480x640 480x640+0+0 8-bit Grayscale Gray 2.34473MiB 0.000u 0:00.000

如果您正在使用OpenCV或Numpy数组进行处理,您可以使用以下方法将OpenCV或Numpy数组制作成PIL/枕头图像:

PILimage = Image.fromarray(numpyImage)
另一方面,从PIL/枕头图像到Numpy阵列:

NumpyImage = np.array(PILimage)

如果您想将其读回,可以执行以下操作:

# Open the multi image
im = Image.open('multi.tif')                                                               

# Iterate through frames
for frame in ImageSequence.Iterator(im):  
    frame.show() 


如果您想移动到特定的波段,可以这样搜索:

from PIL import Image
# Synthesize 8 dummy images, all greyscale, all same size but with varying brightness
size=(480,640)  
b1 = Image.new('L', size, color=10)                                                         
b2 = Image.new('L', size, color=20)                                                        
b3 = Image.new('L', size, color=30)                                                       
b4 = Image.new('L', size, color=40)                                                        
b5 = Image.new('L', size, color=50)                                                        
b6 = Image.new('L', size, color=60)                                                        
b7 = Image.new('L', size, color=70)                                                        
b8 = Image.new('L', size, color=80)                                                        

# Save all 8 to single TIFF file
b1.save('multi.tif', save_all=True, append_images=[b2,b3,b4,b5,b6,b7,b8]) 
im = Image.open('multi.tif')                                                               

im.seek(3) 
im.show()
# Synthesize dummy array of 40 images, each 600x600
nparr = np.random.randint(0,256,(600,600,40), dtype=np.uint8)

# Make PIL/Pillow image of first
a = Image.fromarray(nparr[:,:,0])

# Save whole lot in one TIF
a.save('multi.tif', save_all=True, append_images=[Image.fromarray(nparr[:,:,x]) for x in range(1,40)]) 
$ ./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

您还可以从TIF中提取band3,并在命令行中使用ImageMagick保存为PNG,方法是:

magick multi.tif[3] band3.png
或使用以下材料制作波段1、2、7 RGB复合材料:

magick multi.tif[1] multi.tif[2] multi.tif[7] -colorspace RGB -combine 127rgb.png
它看起来是深蓝色的,因为红色和绿色通道非常低,只有蓝色通道的值较大


我在Python方面不是世界上最好的,因此我不确定是否有任何影响/错误,但我认为如果您有一个600x600x40 numpy的图像数组,您可以按照我的建议这样做:

from PIL import Image
# Synthesize 8 dummy images, all greyscale, all same size but with varying brightness
size=(480,640)  
b1 = Image.new('L', size, color=10)                                                         
b2 = Image.new('L', size, color=20)                                                        
b3 = Image.new('L', size, color=30)                                                       
b4 = Image.new('L', size, color=40)                                                        
b5 = Image.new('L', size, color=50)                                                        
b6 = Image.new('L', size, color=60)                                                        
b7 = Image.new('L', size, color=70)                                                        
b8 = Image.new('L', size, color=80)                                                        

# Save all 8 to single TIFF file
b1.save('multi.tif', save_all=True, append_images=[b2,b3,b4,b5,b6,b7,b8]) 
im = Image.open('multi.tif')                                                               

im.seek(3) 
im.show()
# Synthesize dummy array of 40 images, each 600x600
nparr = np.random.randint(0,256,(600,600,40), dtype=np.uint8)

# Make PIL/Pillow image of first
a = Image.fromarray(nparr[:,:,0])

# Save whole lot in one TIF
a.save('multi.tif', save_all=True, append_images=[Image.fromarray(nparr[:,:,x]) for x in range(1,40)]) 
$ ./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


关键词:多波段、多波段、多光谱、多光谱、卫星图像、图像、图像处理、Python、Numpy、PIL、枕头、TIFF、TIF、NDVI

马克聪明的答案是制作多页TIFF。不幸的是,imagemagick和PIL实际上是MONO/RGB/RGBA/CMYK库,它们不直接支持多波段图像

具有真正的多波段支持。例如:

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)
我可以这样运行它:

from PIL import Image
# Synthesize 8 dummy images, all greyscale, all same size but with varying brightness
size=(480,640)  
b1 = Image.new('L', size, color=10)                                                         
b2 = Image.new('L', size, color=20)                                                        
b3 = Image.new('L', size, color=30)                                                       
b4 = Image.new('L', size, color=40)                                                        
b5 = Image.new('L', size, color=50)                                                        
b6 = Image.new('L', size, color=60)                                                        
b7 = Image.new('L', size, color=70)                                                        
b8 = Image.new('L', size, color=80)                                                        

# Save all 8 to single TIFF file
b1.save('multi.tif', save_all=True, append_images=[b2,b3,b4,b5,b6,b7,b8]) 
im = Image.open('multi.tif')                                                               

im.seek(3) 
im.show()
# Synthesize dummy array of 40 images, each 600x600
nparr = np.random.randint(0,256,(600,600,40), dtype=np.uint8)

# Make PIL/Pillow image of first
a = Image.fromarray(nparr[:,:,0])

# Save whole lot in one TIF
a.save('multi.tif', save_all=True, append_images=[Image.fromarray(nparr[:,:,x]) for x in range(1,40)]) 
$ ./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
它还支持其他数据类型:

$ ./try284.py x.tif uint32
shape = (100, 100, 40)
format = uint32
$ ./try284.py x.tif float32
shape = (100, 100, 40)
format = float32
等等等等

您可以在gdal中加载这些TIFF。我想gdal也可以用来写它们,尽管我还没试过。令人烦恼的是,它将40移动到最外层的维度

$ 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()
numpy2vips()
定义如下:

粘贴副本以供参考:

# 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])
tiffile
()支持多通道.tiff,并且有一个类似于
scikit image
OpenCV
的API:

In [1]: import numpy as np

In [2]: import tifffile

In [3]: # Channel dimension should come first

In [4]: x = np.random.randint(0, 255, 4*100*100).reshape((4, 100, 100))

In [5]: tifffile.imsave('test.tiff', x)

In [6]: y = tifffile.imread('test.tiff')

In [7]: np.all(np.equal(x, y))
Out[7]: True

我不认为您可以使用openCV或scikit来保存超过4个通道的图像。更好的选择是使用pickle.dump或numpy.save,或者你可以保存2或3个通道的所有组合。你是对的,只要我尝试过,我同意你的观点,请帮助我并用示例代码指导我,因为我有numpy.ndarray 600×600×40,我应该这样做40次:b1=Image.fromarray(array[:,:,0]),等等,直到b40,然后为最后的b1做好准备。保存(…附加_图像=[b2,b3,…b40],不存在简单的方法吗?这会奏效,但我认为你可以在那里做一个“列表理解”,并在一行中完成。我不是Python方面的世界最佳,但它类似于
附加_图像=[Image.fromarray(yourarray[:,:,x]),用于范围内的x(40)]
看看这里……试试看,因为你有你的Numpy数组,我没有,如果你能解决它或者需要一段时间的帮助,请告诉我。我非常感谢你的帮助,但真正的问题是多。如果是sigle band,我以前试过你最后的推荐,也许你不是#1 Python开发人员,但你是最好的之一,我已经添加了一些在我回答的最后使用我的虚拟图像数组。这很好,John。你知道除了
vips
,还有什么软件可以读取40波段TIFF吗?谢谢。很好。TIFF本身可以,当然,你可以在这些文件上运行
tiffinfo
tiffcp
等。libvips最初是为Hyperpe编写的ctral成像,所以它是一个必要的功能。你是对的,我不知道还有任何其他软件包可以读取这些怪物。好的,谢谢。你熟悉GDAL吗?也许可以?哦,你是对的,GDAL可以做到。我会更新我的答案。谢谢!太好了!我想如果你不使用
x=np.random,你会无意中保存一个64位TIFF。randint(0,255,4*100*100,dtype=np.uint8)
但是很好!你可以得到这样的多带(而不是多页)TIFF:
x=np.random.randint(0,255,100*100*40,dtype='uint8')。重塑((100,100,40));tiffile.imsave(“y.tif”,x,planarconfig='contig')
ie。带尺寸最后一个用于contig。有没有命名带的方法?