Python地学分析 — GDAL将多个遥感图像叠加保存为tif文件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XBR_2014/article/details/84780689

欢迎关注博主的微信公众号:“智能遥感”。

该公众号将为您奉上Python地学分析、爬虫、数据分析、Web开发、机器学习、深度学习等热门源代码。

本人的GitHub代码资料主页(持续更新中,多给Star,多Fork):

https://github.com/xbr2017

CSDN也在同步更新:

https://blog.csdn.net/XBR_2014


“ 遥感影像的特点之一就是同一个图像文件可以存储若干个波段的图像,本节内容主要介绍GDAL将多个尺寸相同的图像叠加到一起,以GeoTiff格式输出,这有利于不同波段之间进行数值运算。”

本节以LandSat影像作为案列,来实现多波段叠加功能。美国NASA的陆地卫星(Landsat)计划从1972年7月23日以来,已发射8颗。目前Landsat1—4均相继失效,Landsat 5于2013年6月退役。Landsat 7于1999年4月15日发射升空。Landsat8于2013年2月11日发射升空,经过100天测试运行后开始获取影像。

GDAL实现多波段叠加

现在理论已经不在了,让我们学习如何使用GDAL处理这些数据集。栅格数据存在许多不同的文件格式,GDAL是一个非常流行且强大的库,用于读写许多文件格式。GDAL库是开源的,但具有许可,因此即使许多商业软件包也使用它。

GDAL库以其读写较多不同格式的数据而闻名,但它也包含一些数据处理功能,如最邻近法。在许多情况下,你仍然需要编写自己的处理代码,但这是对于许多类型的分析来说相对容易。有一个名为NumPy的Python模块,用于处理大型数据数组,你可以使用GDAL直接将数据读入NumPy数组。根据需要操作数据后,使用NumPy或其他适用于这些数组的模块,你可以将数组作为栅格数据集写入磁盘,这是一个非常简单的过程。

为了说明如何使用GDAL读取和写入栅格数据,让我们从一个将三个Landsat波段组合成一个叠加图像的示例开始,如图1所示。

图1  红色(A),绿色(B)和蓝色(C)Landsat带以黑色和白色显示,你可以看到它们看起来有点不同。D图显示这三个波段叠加成RGB图像。

Landsat计划是美国地质调查局(USGS)与美国国家航空航天局(NASA)之间的联合倡议,自1972年以来一直在全球范围内采集中等分辨率的卫星图像。Landsat图像由USGS作为集合分发GeoTIFFs,每个数据集均有波段。除了波段6(热)和8(全色)之外,每个波段都有30米的分辨率,因为它们来自同一个Landsat场景,尺寸相同。这使得事情变得简单,因为波段直接相互叠加而不需要重采样之类的处理。下面的程序实现了如何创建具有相同尺寸的三波段数据集,然后将波段3,2和1复制到其中。这三个波段分别对应于可见光的红色,绿色和蓝色波长,因此将它们按此顺序排列将产生RGB(红色,绿色,蓝色)图像,其外观与你自己的眼睛非常相似。图1显示了黑色和白色的单独红色,蓝色和绿色波段,以及生成的三波段自然彩色图像。如果你在ArcGIS中预览图像,它们可能看起来与此类似,因为ArcGIS很可能会拉伸它们。下面来看看Python代码的实现:

# _*_ coding: utf-8 _*_
__author__ = 'xbr'
__date__ = '2018/12/3 11:57'

import os
from osgeo import gdal

# 当前所在路径
os.chdir(r'D:\osgeopy-data\Landsat\Washington')
band1_fn = 'p047r027_7t20000730_z10_nn10.tif'
band2_fn = 'p047r027_7t20000730_z10_nn20.tif'
band3_fn = 'p047r027_7t20000730_z10_nn30.tif'

in_ds = gdal.Open(band1_fn)
in_band = in_ds.GetRasterBand(1)

gtiff_driver = gdal.GetDriverByName('GTiff')
out_ds = gtiff_driver.Create('nat_color.tif',
         in_band.XSize, in_band.YSize, 3, in_band.DataType)
out_ds.SetProjection(in_ds.GetProjection())
out_ds.SetGeoTransform(in_ds.GetGeoTransform())

# 读取第1波段数据
in_data = in_band.ReadAsArray()
out_band = out_ds.GetRasterBand(3)
out_band.WriteArray(in_data)

# 读取第2波段数据
in_ds = gdal.Open(band2_fn)
out_band = out_ds.GetRasterBand(2)
out_band.WriteArray(in_ds.ReadAsArray())

# 读取第3波段数据
out_ds.GetRasterBand(1).WriteArray(
    gdal.Open(band3_fn).ReadAsArray())

out_ds.FlushCache()
for i in range(1, 4):
    out_ds.GetRasterBand(i).ComputeStatistics(False)

out_ds.BuildOverviews('average', [2, 4, 8, 16, 32])
del out_ds

代码中首先需要导入gdal模块,然后设置当前目录并指定哪个文件对应哪个Landsat波段。然后通过将文件名传递给gdal.Open打开包含第一个波段的GeoTIFF。你还可以获取数据集中第一个也是唯一一个波段的句柄,尽管你尚未读取任何数据。请注意,使用索引1而不是0来获取第一个波段。当你使用GetRasterBand时,带编号始终以1开头,很多人会忘记这个知识点,容易出错。无论如何,在创建输出图像之前,你需要此波段对象,因为它具有输出文件所需要的基本信息(尺寸大小、投影、坐标等)。

友情提示  请记住,波段索引从1开始而不是0。

接下来,你将创建一个新数据集以将像元数据复制到其中。你必须使用驱动程序对象来创建新数据集,因此你可以找到GeoTIFF驱动程序,然后使用其Create函数。这是该功能的完整参数:

driver.Create(filename, xsize, ysize, [bands], [data_type], [options])
  • filename是要创建的数据集的路径。
  • xsize是新数据集中的列数。
  • ysize是新数据集中的行数。
  • bands是新数据集中的波段数。默认值为1。
  • data_type是将存储在新数据集中的数据类型。默认值为GDT_Byte。
  • options是创建选项字符串的列表。可能的值取决于正在创建的数据集的类型。

因为你使用GeoTIFF驱动程序,所以无论你提供什么文件扩展名,输出文件都将是GeoTIFF。但是,扩展名不会自动添加,因此你需要提供该扩展程序。在这种情况下,你将其命名为nat_color.tif并将其保存在指定文件夹中,因为这是使用os.chdir设置的当前文件夹。你还需要提供列数和行数创建新数据集,因此分别使用XSize和YSize属性从输入波段获取该信息。Open的下一个参数是band的数量,你希望这个新的raster有三个。下一个可选参数是数据类型,它必须是表1中的值之一。你可以从输入波段获取此信息,但在这种情况下你可能会忽略它,因为这些图像使用默认类型的DT_Byte。

            表1  GDAL数据类型常量

常量

数据类型

GDT_Unknown

未知

GDT_Byte

无符号8位整数(字节)

GDT_UInt16

无符号16位整数

GDT_Int16

有符号的16位整数

GDT_UInt32

无符号32位整数

GDT_Int32

有符号的32位整数

GDT_Float32

32位浮点

GDT_Float64

64位浮点

GDT_CInt16

16位复数整数

GDT_CInt32

32位复数整数

GDT_CFloat32

32位复杂浮点

GDT_CFloat64

64位复杂浮点

GDT_TypeCount

可用数据类型的数量

你从输入数据集中获取投影(SRS)并将其复制到新数据集,然后对地理转换执行相同操作。地理转换很重要,因为它提供原点坐标和像元大小。如上所述,在将数据集放置在正确的空间位置时,原点和像元大小非常重要。虽然你不必在添加像元值之前添加投影和地理转换信息,但在创建新数据集后立即将其解除。

设置数据集后,就可以添加像元值了。因为你已经拥有Landsat band 1的GeoTIFF中的波段对象,所以你可以将其中的像元值读入NumPy数组。如果不向ReadAsArray提供任何参数,则所有像元值都以二维数组的形式返回,其尺寸与栅格本身相同。此时,你的in_data变量包含一个二维像元值数组:

in_data = in_band.ReadAsArray()

现在,因为Landsat图像的波段1是蓝色波段,你需要将其放入输出图像的第三个波段以获得RGB顺序的波段。接下来要做的是从out_ds获取第三个波段,然后使用WriteArray将in_data数组中的值复制到新数据集的第三个波段:

out_band = out_ds.GetRasterBand(3)
out_band.WriteArray(in_data)

你仍然需要将绿色和红色Landsat波段添加到数据集中,然后打开第二个波段的GeoTIFF。请注意,你没有从数据集中获取band对象,因为这次你将直接从数据集本身读取像元数据。因为第二个Landsat波段是绿色波段,你可以获得堆叠数据集中第二个(绿色)波段的句柄,并将Landsat文件中的数据复制到堆叠数据集:

in_ds = gdal.Open(band2_fn)
out_band = out_ds.GetRasterBand(2)
out_band.WriteArray(in_ds.ReadAsArray())

当你在数据集上调用ReadAsArray时,如果你正在读取的数据集具有多个波段,则会获得三维数组。由于Landsat文件只有一个波段,因此数据集上的ReadAsArray返回与波段对象相同的二维数组。而不是将数据保存到中间变量,这次你立即将其发送到输出波段。然后,你对红色波段像元值执行相同的操作,但将其压缩为更少的代码。但效果是一样的:

out_ds.GetRasterBand(1).WriteArray(gdal.Open(band3_fn).ReadAsArray())

在下一段代码中,你可以计算数据集中每个波段的统计信息。这不是必须的,但它使一些软件更容易显示它。统计数据包括平均值,最小值,最大值和标准差。GIS可以使用此信息来拉伸屏幕上的数据并使其看起来更好。你将在后面的章节中看到如何手动拉伸数据的示例。在计算统计数据之前,你必须确保数据已写入磁盘而不是仅缓存在内存中,因此这是对FlushCache的调用。然后循环遍历波段并计算每个波段的统计数据。将False传递给此函数会告诉你需要实际统计信息而不是估计值,它可能来自概览图层(尚不存在)或从像元子集中采样。如果估计值可以接受,那么你可以传递True;这也将使计算更快,因为不是每个像元都需要检查:

out_ds.FlushCache()
for i in range(1, 4):
  out_ds.GetRasterBand(i).ComputeStatistics(False)

你要做的最后一件事是为数据集构建概览图层。由于这些像元值是连续数据,因此使用平均插值而不是默认的最近邻法。你还可以指定要构建的五个级别的概视图。碰巧有五个级别是你需要为此特定图像获取大小为256的图像块:

out_ds.BuildOverviews('average', [2, 4, 8, 16, 32])

最后,不要忘记删除输出的数据集。当变量超出范围时,这将自动发生,但如果你使用交互式Python环境,则可能不会在脚本完成运行时发生。在平时处理数据时,这经常会发生。它们不会刷新缓存或删除变量,并且当脚本完成时,它们的IDE不会释放数据集对象,因此它们最终会显示空图像并且不知道是什么原因。

最后来张LandSat高清彩色合成图:

猜你喜欢

转载自blog.csdn.net/XBR_2014/article/details/84780689