ENVI 5.4 新功能:ENVITask 返回虚拟栅格
17122 为本文评分:
暂无评分
ENVI 5.4 新功能:ENVITask 返回虚拟栅格
匿名作者 2016年11月17日,星期四
自从 ENVI 5.2 SP1 引入自定义 ENVITask 功能以来,一直存在一个要求:您的程序必须将所有输出对象提交到磁盘。当时的规定是,任务所包装的程序必须有一个输入关键字,用于告诉它写入对象到哪个文件名。在任务模板中,您会有一个映射到该关键字的输出参数,然后在任务加载期间,框架会自动为您创建一个输入参数,该参数映射到相同的关键字,且类型设置为“ENVIURI”。
这种输入参数的自动创建以及两个参数的内部关联,其初衷是好的,旨在简化自定义任务的创建过程。然而,用户对此功能的反馈并不总是像我们希望的那么积极。
因此,在 ENVI 5.4 中,我们对此进行了改进,并赋予任务开发者更多的控制权。如果您在任务模板中仍使用“版本”属性,并将其设置为“5.2.1”、“5.3”或“5.3.1”,那么您将获得旧的行为。但如果您切换到使用“模式”属性并将其值设置为“envitask_3.0”,那么将有一组新的规则适用于该程序以及您在程序内部可以执行的操作。在这种新模式下,您的程序将拥有独立的输入文件名关键字和输出对象引用关键字。如果您愿意,甚至可以完全跳过文件名关键字,直接返回一个完全不绑定到磁盘的对象。这使得处理像 ENVIGCPSet 和 ENVITiePointSet 这样的类型变得更加容易,同时也允许程序根据其他输入参数构建复杂的虚拟栅格链。
您可能会问,为什么希望任务返回一个虚拟栅格呢?对于作为 ENVI 5.4 一部分的新分类框架,您需要确保以与准备训练数据相同的方式准备要运行经过训练的分类器的数据。实现这一点的一种方法是创建一个以一致方式返回预处理数据的任务。如果能够避免将经过预处理的属性栅格保存到磁盘,为什么不利用虚拟栅格带来的时间和空间优势呢?
以下示例是 ENVI 5.4 版本中新的自定义任务教程的修改版本。该任务包装了一个程序,该程序在内部使用了多个其他任务来执行所有预处理。在这里,我对其进行了修改,使用虚拟栅格和其他 ENVI API 函数调用,以避免向磁盘写入任何文件。
代码包含许多步骤,我将在展示代码后对其进行描述:
pro SentinelVegIndices_blog, INPUT_RASTER_10M=raster10m, $
INPUT_RASTER_20M=raster20m, $
OUTPUT_RASTER=outputRaster
compile_opt idl2, hidden
; Get the spatial reference of the 10-meter raster
spatialRef = raster10m.SPATIALREF
coordSys = ENVICoordSys(COORD_SYS_CODE=spatialRef.COORD_SYS_CODE)
; Create a spatial grid definition
grid = ENVIGridDefinition(coordSys, $
PIXEL_SIZE=spatialRef.PIXEL_SIZE, $
NCOLUMNS = raster10m.NCOLUMNS, $
NROWS = raster10m.NROWS, $
TIE_POINT_MAP=spatialRef.TIE_POINT_MAP, $
TIE_POINT_PIXEL = spatialRef.TIE_POINT_PIXEL)
; Regrid the 20-meter bands to 10 meters
regriddedRaster = ENVISpatialGridRaster(raster20m, GRID_DEFINITION=grid)
; Create a layer stack
layerStack = ENVIMetaspectralRaster([raster10m, regriddedRaster], $
SPATIALREF=spatialRef)
; Compute image statistics
stats = ENVIRasterStatistics(layerStack)
; Perform dark subtraction as an alternative to atmospheric correction
bandRasters = ObjArr(layerStack.nBands)
for i = 1, layerStack.nBands do begin
expression = 'b' + i.ToString() + ' - ' + stats.Min[i-1].ToString()
bandRasters[i-1] = ENVIPixelwiseBandMathRaster(layerStack, expression)
endfor
bandStackRaster = ENVIMetaspectralRaster(bandRasters, $
SPATIALREF=spatialRef)
; we need to put the wavelengths back into the band stack,
; they were removed by the band math
metadata = ENVIRasterMetadata()
metadata['wavelength'] = layerStack.Metadata['wavelength']
metadata['wavelength units'] = layerStack.Metadata['wavelength units']
correctedRaster = ENVIMetadataOverrideRaster(bandStackRaster, $
METADATA_OVERRIDE=metadata)
; Scale pixel values from 0 to 1
gains = MAKE_ARRAY(layerStack.NBANDS, /FLOAT, VALUE=0.0001)
offsets = FLTARR(layerStack.NBANDS)
scaledRaster = ENVIGainOffsetRaster(correctedRaster, gains, offsets)
; Create vegetation indices
indices = [ 'Enhanced Vegetation Index', $
'Global Environmental Monitoring Index', $
'Leaf Area Index', $
'Plant Senescence Reflectance Index', $
'Red Edge Normalized Difference Vegetation Index' ]
outputRaster = ENVISpectralIndexRaster(scaledRaster, indices)
end
第一步是将 20 米栅格上采样到 10 米像素,这在教程中是使用 ENVIRegridRasterTask 完成的。一旦我们使用 10 米和 20 米栅格的属性适当混合构造了一个 ENVIGridDefinition,就可以使用 ENVISpatialGridRaster 虚拟栅格来完成此操作。
接下来,教程使用 ENVIBuildBandStackTask 来构建所有 10 米波段的元光谱堆栈。这里我们使用 ENVIMetaspectralRaster 虚拟栅格,不过我们必须传入原始 10 米栅格的空间参考,以保持此栅格在地图上的配准。
接下来是暗目标减法。教程使用了 ENVIRasterStatisticsTask,但这里我们只使用 API 函数 ENVIRasterStatistics() 来完成同样的事情。
波段最小值用于执行暗目标减法。教程使用了 ENVIDarkSubtractionCorrectionTask,它将此作为单个栅格处理。在这里,我必须为每个波段构建单独的波段数学公式,因为 ENVIPixelwiseBandMathRaster 虚拟栅格总是返回单波段输出,所以我必须构建一个波段数学表达式数组,然后对结果进行元光谱堆栈,再次传入空间参考。
波段数学的一个潜在问题是它会移除大部分光谱元数据,因此我必须将波长元数据放回栅格中,以便光谱指数计算选择正确的波段。我使用 ENVIMetadataOverrideRaster() 来实现这一点,并使用了原始元光谱栅格的元数据值副本。
然后使用 ENVIGainOffsetRaster 将栅格按 10000.0 的比例缩小,以更好地模拟大气校正并产生更准确的光谱指数值。最后,缩放后的栅格被传入 ENVISpectralIndexRaster 以计算 5 个不同的光谱指数。
编写完成后,我们可以使用一个几乎相同的 .task 文件来包装该程序。主要区别在于我们不再需要指定 OUTPUT_URI 参数,并且我使用了一个稍有不同的程序名。此任务的输出栅格永远不会提交到磁盘,但可以通过对其调用 ENVIRaster::Dehydrate() 来将其用作另一个任务的输入,这将生成一个 25KB 的 JSON 表示。
以下是更新后的 .task 文件:
{
"name": "SentinelVegIndices_blog",
"base_class": "ENVITaskFromProcedure",
"routine": "SentinelVegIndices_blog",
"display_name": "Compute Sentinel-2A Vegetation Indices",
"description": "This task regrids the visible and near-infrared bands of a Sentinel-2A Level-1C 20-meter image to 10 meters. It creates a layer stack of all bands. It applies dark-object subtraction as a simple alternative to atmospheric correction. It scales the reflectance pixel values from 0 to 1, then computes a select group of vegetation indices.",
"schema": "envitask_3.0",
"parameters": [
{
"name": "INPUT_RASTER_10M",
"display_name": "Select a 10-meter image",
"type": "ENVIRASTER",
"direction": "input",
"required": true,
"description": "Select a Sentinel Level-1C 10-meter image."
},
{
"name": "INPUT_RASTER_20M",
"display_name": "Select a 20-meter image",
"type": "ENVIRASTER",
"direction": "input",
"required": true,
"description": "Select a Sentinel Level-1C 20-meter image."
},
{
"name": "OUTPUT_RASTER",
"display_name": "Output image",
"type": "ENVIRASTER",
"direction": "output",
"required": true,
"description": "This is a reference to the output raster."
}
]
}