Skip to main content

使用栅格像元迭代器的快速浏览

您可以用 Raster Cell Iterator (RCI) 来执行自定义栅格分析。 它允许您访问单个栅格像元位置,并提供迭代控制以在 Python 环境中查询和修改栅格像元值。

使用栅格像元迭代器的基本操作

在本节中,您将从概念层面理解使用 RCI 可执行的基本操作。 您将学会如何:创建隐式 RCI,查询并分配栅格像元值,创建带有元数据的空栅格,索引栅格对象,以及处理 NoData。 每个部分都包含代码示例,演示如何从 Python 窗口中使用 RCI。 建议您遵循本文档的先后顺序进行阅读,以理解这些代码示例。

隐式栅格像元迭代器

可以对栅格对象隐式调用 RCI,以迭代其行和列索引。 下文所示的代码示例是从现有的栅格数据集 "myras" 中创建了一个名为 myRas 的栅格对象。 隐式栅格迭代器定义于栅格对象上,用于在循环中枚举栅格的行和列索引对。 在迭代器内部,会打印每个像元位置的行索引 i、列索引 j 和像元值 myRas[i,j]。 这是使用索引记法查询给定像元位置像元值的示例。

from arcpy.sa import *
myRas = Raster("myras")
for i,j in myRas:
 print(i, j, myRas[i,j])

为像元分配值

利用 RCI,您可以为栅格像元分配值,从而修改栅格数据集。 默认情况下,从现有栅格数据集创建的栅格对象有一个 readOnly 属性,设置为 True。 要修改栅格数据集,首先应将 readOnly 属性设置为 False。 一旦修改栅格像元,应在栅格对象上调用 save() 方法以持久保存这些更改。 有关 readOnly 属性和保存栅格的详细信息,请参阅 Raster 对象帮助主题。

myRas.readOnly = False
for i,j in myRas:
 if myRas[i,j] == 0:
     myRas[i,j] = 128
myRas.save()

创建空栅格

您可以利用 getRasterInfo() 方法轻松创建一个空的栅格对象,并从现有栅格对象复制元数据。 对现有栅格对象调用 getRasterInfo() 方法后,会返回该栅格的 rasterInfo() 对象。 然后将 rasterInfo() 对象作为输入参数传递给 Raster() 类,以实例化一个空的栅格。 该空栅格对象与其创建的栅格对象拥有相同的元数据,但所有栅格像元均为 NoData。 栅格元数据包括 bandCount、像元大小、extentspatialReferencepixelTypenoDataValues 等其他属性。 当使用 getRasterInfo() 方法创建栅格对象时,readOnly 默认属性设置为 False,以便向此空栅格对象分配值。

outRas = Raster(myRas.getRasterInfo())

您也可以创建一个空的 rasterInfo 类对象,并使用 rasterInfo() 对象上的 FromJSONString() 方法填充栅格元数据。 随后,该对象可用于创建空栅格,如下所示。

ri = arcpy.RasterInfo()
ri_as_jsonstring = '{"bandCount":1,' \
                '"extent":{"xmin":0,"ymin":0,"xmax":10,"ymax":10,"spatialReference":{"wkid":26912}},' \
                '"pixelSizeX":1,' \
                '"pixelSizeY":1,' \
                '"pixelType":"F32"}'
ri.fromJSONString(ri_as_jsonstring)
outRas = Raster(ri)

索引栅格对象

索引栅格对象允许对栅格数据集中的特定像元进行读写访问。 索引可以在栅格像元迭代器内部或外部使用。

对于单波段栅格,栅格对象有两个索引 [i, j],分别是行索引和列索引。 查询和分配值时至少需要两个索引。 所有索引均从零开始。 行索引的值范围由 0 到 n(行)- 1 定义,其中 n(行)是该栅格中的行数。 同样,列索引的值范围由 0 到 n(列)- 1 定义,其中 n(列)是该栅格中的列数。

索引从栅格数据集的左上角开始,其中最左上角的像元索引为 [0, 0],最右下角的像元索引为 [n(行)- 1,n(列)- 1]。

处理 NoData 像元

当使用索引记法在 RCI 上下文内外查询带有 NoData 的像元时,Python 返回的值为 NaN。 例如,如果已知 myRas 在行索引和列索引为 [2, 2] 的位置处存在 NoData 值,查询该像元值将返回 NaN。 这可以通过 math.isnan() 方法进行验证,该方法将为此像元返回布尔值 True

import math
math.isnan(myRas[2,2])
out: True

如果在 Python 中为某个像元分配 NaN,则该栅格像元将变为 NoData,无论该栅格数据集的格式或 NoDataValues 为何。 这是将像元转换为 NoData 的正确方式。 例如,在行和列索引处为 [3, 3] 的位置将 NaN 分配给 myRas,即可将该像元修改为 NoData。

myRas[3,3] = math.nan

不建议通过直接将栅格数据集的 noDataValues 分配给像元来将其设为 NoData,而应按照上述方法进行操作。

使用栅格像元迭代器的高级操作

在本节中,您将从概念层面理解使用 RCI 可执行的高级操作。 您将学习如何创建显式栅格像元迭代器来迭代多个栅格,以及如何使用 paddingskipNoData 选项。 您将学习多波段栅格的索引方法,并了解如何在 RCI 中使用。 您将学习如何处理不同的栅格像元类型,以及如何处理超出边界的值。

显式栅格像元迭代器

调用 RCI 的另一种方式是使用显式 RasterCellIterator 类对象。 虽然隐式迭代器适合对单个栅格数据集进行迭代,但显式栅格迭代器专为迭代多个栅格而设计,并为此进行了优化。 在迭代多个栅格时,您可以用输入栅格进行分析(其像元值用于计算),并同时使用输出栅格(其像元值在计算过程中写入)。

以下示例演示了如何使用显式 RCI 迭代多个输入和输出栅格。 输入栅格 myRas1myRas2 具有相同的行数和列数,以及像元大小、范围和空间参考等其他属性。 输出栅格 outRas1outRas2 是使用第一个输入栅格的 rasterInfo 类对象创建的。 在每个栅格像元处,会进行焦点运算以计算 3 像元 x 3 像元邻域的平均值(针对 myRas1 和 myRas2)。 在该像元位置,会分配 outRas1 一个像元值,作为各平均值中的最小值;而分配 outRas2 一个像元值,作为各平均值中的最大值。

myRas1 = Raster("myras1")
myRas2 = Raster("myras2")
outRas1 = Raster(myRas1.getRasterInfo())
outRas2 = Raster(myRas1.getRasterInfo())
with RasterCellIterator({'rasters':[myRas1, myRas2, outRas1, outRas2]}) as rci:
 for i,j in rci:
     meanMyRas1 = (myRas1[i-1,j-1] + myRas1[i-1, j] + myRas1[i-1, j+1] + \
                   myRas1[i,j-1] + myRas1[i, j] + myRas1[i, j+1] + \
                   myRas1[i+1,j-1] + myRas1[i+1, j] + myRas1[i+1, j+1]) / 9
     meanMyRas2 = (myRas2[i-1,j-1] + myRas2[i-1, j] + myRas2[i-1, j+1] + \
                   myRas2[i,j-1] + myRas2[i, j] + myRas2[i, j+1] + \
                   myRas2[i+1,j-1] + myRas2[i+1, j] + myRas2[i+1, j+1]) / 9
     outRas1[i,j] = min(meanMyRas1, meanMyRas2)
     outRas2[i,j] = max(meanMyRas1, meanMyRas2)
outRas1.save()
outRas2.save()

当迭代器中使用多个栅格时,列表中的第一个栅格定义了迭代器的栅格分析环境。 列表中的所有其他栅格在空间参考、像元大小和范围上都应与第一个栅格匹配。 如果列表中第二个及后续栅格的这些属性与第一个栅格不匹配,则应用第一个栅格的分析环境。 建议使用第一个栅格的 rasterInfo() 对象创建所有输出栅格,以便继承其空间参考、像元大小和范围。

此外,如需支持环境设置,应先将 ApplyEnvironment 函数应用到列表中的第一个栅格,然后再在迭代器中使用。 以下示例对此进行了演示。

myRas1 = Raster("myras1")
myRas2 = Raster("myras2")
#Apply environment settings to myRas1
myRas1_env = arcpy.sa.ApplyEnvironment(myRas1)
outRas1_env = Raster(myRas1_env.getRasterInfo())
with RasterCellIterator({'rasters':[myRas1_env, myRas2, outRas1_env]}) as rci:
 for i,j in rci:
     meanMyRas1 = (myRas1_env[i-1,j-1] + myRas1_env[i-1, j] + myRas1_env[i-1, j+1] + \
                   myRas1_env[i,j-1] + myRas1_env[i, j] + myRas1_env[i, j+1] + \
                   myRas1_env[i+1,j-1] + myRas1_env[i+1, j] + myRas1_env[i+1, j+1]) / 9
     meanMyRas2 = (myRas2[i-1,j-1] + myRas2[i-1, j] + myRas2[i-1, j+1] + \
                   myRas2[i,j-1] + myRas2[i, j] + myRas2[i, j+1] + \
                   myRas2[i+1,j-1] + myRas2[i+1, j] + myRas2[i+1, j+1]) / 9
     outRas1_env[i,j] = min(meanMyRas1, meanMyRas2)
outRas1_env.save()

填充选项

显式迭代器提供填充选项。 填充不会以任何方式更改输出,但是如果在迭代像元位置时访问邻域像元值,则填充可提高性能。 例如,如果邻域操作的核在迭代过程中会访问距离当前像元位置最远两行或两列的像元值,则填充为 2。

with RasterCellIterator({'rasters':[myRas1, outRas1], 'padding': 2}) as rci_padded:
 for i,j in rci_padded:
     outRas1[i,j] = (myRas1[i-2,j-2] + myRas1[i-2, j] + myRas1[i-2, j+2] + \
                   myRas1[i,j-2] + myRas1[i, j] + myRas1[i, j+2] + \
                   myRas1[i+2,j-2] + myRas1[i+2, j] + myRas1[i+2, j+2]) / 9
outRas1.save()

跳过 NoData 选项

显式迭代器能够完全跳过 NoData 像元,而无需逐个像元进行处理。 使用 skipNoData 键提供一个栅格列表,以确定迭代器跳过的栅格像元。 对于任何给定的像元,如果该列表中的任意栅格存在 NoData 值,则跳过该像元。 由于这些栅格中 NoData 像元的状态在迭代过程中可能会发生变化,因此是否在访问每个像元时应用跳过操作,取决于迭代器访问该像元时列表中各栅格的状态。 该选项的主要好处是在对稀疏栅格数据集进行迭代时性能提升,因为许多 NoData 像元不再需要访问。

在以下示例中,稀疏栅格 streamRas 仅在表示河流水系的像元处有值,其余位置均为 NoData。 流向栅格 flowDirRas 在栅格范围内所有位置均有值,没有任何 NoData 像元。 当使用 RCI 沿水系像元提取流向栅格值时,可使用 skipNoData 键,使迭代器仅访问带有值的水系像元,从而最小化总像元访问数。

streamRas = Raster("streams")
flowDirRas = Raster("flowdir")
outRas = Raster(flowDirRas.getRasterInfo())
with RasterCellIterator({'rasters':[streamRas, flowDirRas, outRas],'skipNoData':[flowDirRas, streamRas]}) as rci_skip:
 for i,j in rci_skip:
     outRas[i,j] = flowDirRas[i,j]
outRas.save()

多波段栅格索引

对于多波段栅格,您可以提供两到三个索引。 它们之间行为的差异将在后续内容中进行详细讨论。

如果提供了两个索引,则将其解释为行和列索引,并针对指定的 [row, column] 索引从每个波段返回一个值的元组。 例如,一个包含三个波段的多波段栅格“mbras”,可以使用两个索引进行查询,假设为 [2,2]。 这些索引可解释为行和列索引,并且三个波段的值将作为一个元组返回。

multibandRas = Raster("mbras")
multibandRas[2,2]
out: (4,5,6)

如果指定了三个索引,则将其解释为 [band, row, column] 索引,并返回该波段、行和列像元位置的像元值。 波段索引从零开始,多波段栅格的值范围由 0 到 n(波段)- 1 决定,其中 n(波段)是波段的总数。

multibandRas[0,2,2]
out: 4

处理超出边界的值

所有栅格都有一个像素类型及其对应的位深度。 有关每个像元可包含的位深和值范围的完整列表,请参阅栅格数据集像素的位深度容量。 当给一个栅格像元分配的值超过其位深度的可接受范围时,就会出现超出边界的情况。 在这种情况下,像元会分配为 NoData。 例如,栅格对象 myRas1 的像素类型是 'U8',即一个无符号 8 位整数。 该栅格可以包含 0 到 255 的整数值。 如果 myRas1 中的像元分配了值 260,则该像元会分配为 NoData。 分配完后,查询该像元的值会返回 NaN

myRas1 = Raster("myras1")
myRas1Info = myRas1.getRasterInfo()
pixelType_MyRas1 = myRas1Info.getPixelType()
print(pixelType_MyRas1)
out: 'U8'
myRas1[3,3] = 260
math.isnan(myRas1[3,3])
out: True