欢迎关注博主的微信公众号:“智能遥感”。
该公众号将为您奉上Python地学分析、爬虫、数据分析、Web开发、机器学习、深度学习等热门源代码。
本人的GitHub代码资料主页(持续更新中,多给Star,多Fork):
https://github.com/xbr2017
CSDN也在同步更新:
https://blog.csdn.net/XBR_2014
“ 很多时候,我们需要研究较大空间范围的研究区域时,单幅遥感图无法覆盖整个研究区域,这时就需要对遥感图像进行镶嵌(或拼接)。”
今天的遥感之美封面图—密西西比三角洲,风吹池塘西南走。密西西比河位于北美地区,是世界四大河之一,占美国国土面积的40%,海明威、马克吐温等大作家都曾热情赞颂这条伟大的母亲河。
上面的假彩色图像显示了密西西比三角洲的研究区域。它于2016年12月1日由Landsat 8上的陆地成像仪(OLI)拍摄。其中颜色强调陆地与水体间的差异。
风是一股不可忽视的力量。它可轻易地撩起季风的涟漪,也可携走沙尘于千里之外,还能将岩石雕刻成蜿蜒的拱门。但有时,它的影响常年被视而不见,譬如它在池塘边所留下的痕迹。
一项新的研究表明,风是造成密西西比河沿岸三个流域池塘大范围增长的原因。该论文于2017年4月发表在《地球物理研究快报》上,该论文表明,风力波可以侵蚀池塘,导致它们沿着风向移动。实际上,研究人员已经证明,风能侵蚀甚至蚕食海岸线和较大水体的陆地边界,也可以在小范围内陆发生。
研究人员分析了1982年至2016年间约10,000张卫星图像,检查了陆地和水像元,以寻找整个密西西比河三角洲的内陆变化。布鲁明顿印第安纳大学的海洋地质学家亚历杭德拉奥尔蒂斯说:“到目前为止,很多人一直关注沿海‘撤退’。”相反,奥尔蒂斯及其同事专注于内部分裂;也就是说,当土地被内陆侵蚀过程分裂时会发生什么。“我们的想法是,你能大规模地看到这个现象吗?”
奥尔蒂斯和她的合作者发现,密西西比河三角洲的池塘逐渐向西南方向扩展,这与盛行风(从东北方向吹出)的方向相同。在Terrebonne和Barataria盆地尤其如此,其中80%的池塘正在扩张。另一个研究盆地Atchafalaya-Vermillion被认为相对稳定,几乎同样多的池塘也在扩张—约30%(文字描述译自NASA官网)。
图像拼接
本节重点内容分享遥感图像的拼接。相邻两个遥感图像之间有部分区域重叠,为了研究更大空间范围,通常需要将同一段时间内多幅影像进行拼接。图像拼接需要考虑不同图像之间的偏移,可以使用Transformer类或计算两个图像之间的偏移。你可能想要执行此操作的一个示例是:因为每个输入图像都位于镶嵌的不同部分,你想将图像拼接在一起。为了说明这一点,图1示意几个数字正射影像组合成一个图像。
图1 虚线表示将六个图像拼凑在一起的痕迹。实线是输出图像的覆盖区。
要组合图像,就需要知道输出图像的范围。找到这个的唯一方法是获取每个输入图像的范围并计算总体最小和最大角点坐标(图1)。为了使这更容易,你将创建一个获取图像范围的函数。它使用地理转换来获取左上角坐标,然后使用像元大小和图像尺寸计算右下角坐标:
# _*_ coding: utf-8 _*_
__author__ = 'xbr'
__date__ = '2018/12/10 11:41'
import os, sys, gdal
from gdalconst import *
import glob
def get_extent(fn):
ds = gdal.Open(fn)
rows = ds.RasterYSize
cols = ds.RasterXSize
# 获取图像角点坐标
gt = ds.GetGeoTransform()
minx = gt[0]
maxy = gt[3]
maxx = gt[0] + gt[1] * rows
miny = gt[3] + gt[5] * cols
return (minx, maxy, maxx, miny)
你可以在下面的程序中看到上述函数如何用于查找输出范围。知道空间范围后,可以计算输出尺寸并创建图像。
os.chdir('D:\MODIS-data')
in_files = glob.glob('*.tif')
# 通过两两比较大小,将最终符合条件的四个角点坐标保存,
# 即为拼接图像的四个角点坐标
minX, maxY, maxX, minY = get_extent(in_files[0])
for fn in in_files[1:]:
minx, maxy, maxx, miny = get_extent(fn)
minX = min(minX, minx)
maxY = max(maxY, maxy)
maxX = max(maxX, maxx)
minY = min(minY, miny)
# 获取输出图像的行列数
in_ds = gdal.Open(in_files[0])
gt = in_ds.GetGeoTransform()
rows = int(maxX - minX) / abs(gt[5])
cols = int(maxY - maxy) / gt[1]
# 创建输出图像
driver = gdal.GetDriverByName('gtiff')
out_ds = driver.Create('mosaic.tif', cols, rows)
out_ds.SetProjection(in_ds.GetProjection())
out_band = out_ds.GetRasterBand(1)
gt = list(in_ds.GetGeoTransform())
gt[0], gt[3] = minX, maxY
out_ds.SetGeoTransform(gt)
for fn in in_files:
in_ds = gdal.Open(fn)
trans = gdal.Transformer(in_ds, out_ds, [])
success, xyz = trans.TransformPoint(False, 0, 0)
x, y, z = map(int, xyz)
data = in_ds.GetRasterBand(1).ReadAsArray()
out_band.WriteArray(data, x, y)
del in_ds, out_band, out_ds
在程序中首先是循环遍历所有输入文件,并使用它们的范围来计算最终镶嵌的范围,然后计算输出的行数和列数。你可以通过获取每个方向上的最小值和最大值之间的距离并除以像元大小来实现。通过使用取整函数确保不会切掉边缘。然后使用这些尺寸创建新数据集,仍需要创建一个合适的地理转换,但这可以通过从输入文件中获取一个并将左上角坐标。
此时,有一个适当的空数组,所以是时候开始复制数据了,这是变换器的用武之地。对于每个输入数据集,你在该数据集和输出镶嵌之间创建一个变换器。拥有变换器后,你可以使用TransformPoint计算与输入图像左上角对应的像元偏移。最终我们以MODIS地表反射率数据作为示例,来看一下拼接前后的结果图。
拼接前的MOD09A
拼接后的MOD09A