PythonAPI

也可以通过名为的模块从Python访问这些应用程序 otbApplication 。然而,使用它是有技术要求的。如果通过独立包使用OTB,则应使用提供的环境脚本 otbenv 要正确设置变量,如 PYTHONPATHOTB_APPLICATION_PATH (在Unix系统上,不要忘记编写脚本)。在其他情况下,您应该根据您的配置设置这些变量。

在Unix系统上,它通常位于 /usr/lib/otb/python 目录。根据您安装OTB的方式,您可能需要配置环境变量 PYTHONPATH 包含此目录,以便可以从Python访问该模块。

在Windows上,您可以安装 otb-python 包,该模块将自动从OSGeo4WShell可用。

对于命令行,需要使用 OTB_APPLICATION_PATH 环境变量。Unix系统上的标准位置是 /usr/lib/otb/applications 。在Windows上,这些应用程序位于 otb-bin OSGeo4W包,环境自动配置为 OTB_APPLICATION_PATH 不需要修改 OTB_APPLICATION_PATH

设置好环境后,您就可以从Python中使用OTB应用程序了,就像这个小示例一样:

#  Example on the use of the Smoothing application

# The python module providing access to OTB applications is otbApplication
import otbApplication as otb

# Let's create the application with codename "Smoothing"
app = otb.Registry.CreateApplication("Smoothing")

# We set its parameters
app.SetParameterString("in", "my_input_image.tif")
app.SetParameterString("type", "mean")
app.SetParameterString("out", "my_output_image.tif")

# This will execute the application and save the output file
app.ExecuteAndWriteOutput()

基础知识

otbApplication 模块中,可以操作两个主要类:

  • Registry ,它提供对可用应用程序列表的访问,并可以创建应用程序。
  • Application ,所有应用程序的基类。这允许用户与由 Registry

下面是一个如何使用Python来运行 Smoothing 应用程序,在每次迭代时更改算法。

#  Example on the use of the Smoothing application
#

# We will use sys.argv to retrieve arguments from the command line.
# Here, the script will accept an image file as first argument,
# and the basename of the output files, without extension.
from sys import argv

# The python module providing access to OTB applications is otbApplication
import otbApplication

# otbApplication.Registry can tell you what application are available
print('Available applications: ')
print (str( otbApplication.Registry.GetAvailableApplications()))

# Let's create the application  "Smoothing"
app = otbApplication.Registry.CreateApplication("Smoothing")

# We print the keys of all its parameters
print (app.GetParametersKeys())

# First, we set the input image filename
app.SetParameterString("in", argv[1])

# The smoothing algorithm can be set with the "type" parameter key
# and can take 3 values: 'mean', 'gaussian', 'anidif'
for type in ['mean', 'gaussian', 'anidif']:

  print('Running with ' + type + ' smoothing type')

  # Now we configure the smoothing algorithm
  app.SetParameterString("type", type)

  # Set the output filename, using the algorithm type to differentiate the outputs
  app.SetParameterString("out", argv[2] + type + ".tif")

  # This will execute the application and save the output to argv[2]
  app.ExecuteAndWriteOutput()

如果要处理Python字典中的参数,可以使用函数 SetParameters()GetParameters()

params = {"in":"myInput.tif", "type.mean.radius":4}
app.SetParameters(params)
params2 = app.GetParameters()

块状数组处理

以NumPy数组的形式向任何OTB应用程序输入和输出图像现在都可以在OTB Python包装中实现。Python包装仅公开OTB应用程序引擎模块(称为 ApplicationEngine ),它允许用户访问现有的C++应用程序。由于ApplicationEngine加载机制的良好特性,每个应用程序不需要特定的包装。

对Python包装的NumPy扩展允许以数组而不是磁盘文件的形式将数据交换到应用程序。当然,也可以从文件加载图像,然后将其转换为NumPy数组,或者只需通过Application.Set参数字符串(...)提供文件,如上一节所述。

NumPy和OTB之间的桥梁使得通过使用GDAL、GRASS GIS、OSSIM等可以处理NumPy的地理信息系统/图像处理工具的Python代码将OTB插入任何图像处理链变得容易。

下面的代码使用Python Pillow库(PIL的分支)读取输入图像,并将其转换为NumPy数组。NumPy数组用作应用程序的输入,通过 SetImageFromNumpyArray(...) 方法。本例中使用的应用程序是ExtractROI。在提取一小块区域后,将输出图像作为NumPy数组,并使用 GetImageFromNumpyArray(...) 方法,从而避免将输出写入临时文件。

import sys
import os
import numpy as np
import otbApplication
from PIL import Image as PILImage

pilimage = PILImage.open('poupees.jpg')
npimage = np.asarray(pilimage)
inshow(pilimage)

ExtractROI = otbApplication.Registry.CreateApplication('ExtractROI')
ExtractROI.SetImageFromNumpyArray('in', npimage)
ExtractROI.SetParameterInt('startx', 140)
ExtractROI.SetParameterInt('starty', 120)
ExtractROI.SetParameterInt('sizex', 150)
ExtractROI.SetParameterInt('sizey', 150)
ExtractROI.Execute()

ExtractOutput = ExtractROI.GetImageAsNumpyArray('out')
output_pil_image = PILImage.fromarray(np.uint8(ExtractOutput))
imshow(output_pil_image)

将OTB与其他库混合使用

现在可以将Python库(rasterio用于打开图像、SCRISKIT-IMAGE、SCRIPKIT-LEARN FOR PROCESSINGS)与OTB混合使用。为了构建高效的代码,下面是一个小示例说明的一些技巧。

  • OTB expects "(height, width, channels)" shaped arrays 而rasterio和其他库通常使用“(Channel,Height,Width)”数组。油炸土豆 np.transpose(x,y,z) 帮助您转置轴心
  • 对于单波段图像,请使用 otbapp.SetImageFromNumpyArray(..) 方法和方法 otbapp.SetVectorImageFromNumpyArray(..) 否则的话。
  • *OTB returns a reference 在NumPy数组输出上:根据您的用例,您可以复制数组 (numpy.copy() )
  • OTB expects C contiguous arrays :有时并非如此,例如,如果多个进程使用Shared_Memory数组。在特定情况下,您可以使用Numpy.ascontiguousarray方法使其正常工作
ds = rio.open(image)
np_image_raw = ds.read()
print(">> shape rio.open().read() "+str(np_image_raw.shape))

nbchannels = np_image_raw.shape[0]
heigth = np_image_raw.shape[1]
width = np_image_raw.shape[2]
# use np.transpose to switch axis : OTB expects [height, width, nb channels] images
np_image=np_image_raw.transpose(1, 2, 0)
[ ... ]
app_rindices = otb.Registry.CreateApplication("RadiometricIndices")
app_rindices.SetVectorImageFromNumpyArray('in', np_image)
# OTB expects C contiguous arrays and in certain conditions (ex : shared_memory arrays),
# the ndarray should be transform as following
app_rindices.SetVectorImageFromNumpyArray('in', np.ascontiguousarray(np_image))
[ ... set parameters ..]
app_rindices.Execute() # we don't write output
# Get output result
res_indices = app_rindices.GetVectorImageAsNumpyArray("out")
# to write it with rasterio or pass it to another library, you may switch back axis
res_indices = res_indices.transpose(2,0,1)
# Be aware that as soon as OTB application is deleted from memory, the ndarray is deallocated...
# You should consider making a copy of the array
res_indices = app_rindices.GetVectorImageAsNumpyArray("out").copy()

内存连接

应用程序通常用作更大的处理工作流的一部分。链接应用程序目前需要在应用程序之间写入/读回图像,导致繁重的I/O操作和大量专门用于写入临时文件的时间。

从OTB 5.8开始,可以将来自一个应用的输出图像参数连接到下一个参数的输入图像参数。这导致将内部ITK/OTB管道连接在一起,从而允许在应用程序之间传输图像。因此,这消除了写入临时映像的需要,并提高了性能。只有处理链的最后一个应用程序负责写入最终结果图像。

应用程序之间的内存连接既可以在C++API级别上使用,也可以使用Python绑定进行。

以下是将几个应用程序连接在一起的Python代码示例:

import otbApplication as otb

app1 = otb.Registry.CreateApplication("Smoothing")
app2 = otb.Registry.CreateApplication("Smoothing")
app3 = otb.Registry.CreateApplication("Smoothing")
app4 = otb.Registry.CreateApplication("ConcatenateImages")

app1.IN = argv[1]
app1.Execute()

# Connection between app1.out and app2.in
app2.SetParameterInputImage("in",app1.GetParameterOutputImage("out"))

# Execute call is mandatory to wire the pipeline and expose the
# application output. It does not write image
app2.Execute()

app3.IN = argv[1]

# Execute call is mandatory to wire the pipeline and expose the
# application output. It does not write image
app3.Execute()

# Connection between app2.out, app3.out and app4.il using images list
app4.AddImageToParameterInputImageList("il",app2.GetParameterOutputImage("out"));
app4.AddImageToParameterInputImageList("il",app3.GetParameterOutputImage("out"));

app4.OUT = argv[2]

# Call to ExecuteAndWriteOutput() both wires the pipeline and
# actually writes the output, only necessary for last application of
# the chain.
app4.ExecuteAndWriteOutput()

Note: 只有当应用程序内部实现没有中断流时,流才能正常工作,例如,通过使用内部写入器写入中间数据。在这种情况下,执行应该仍然正确,但会读取或写入一些中间数据。

混合内存/磁盘连接

作为OTB应用程序连接的扩展(在上一节中介绍),还可以使用混合模式在以下各项之间轻松切换:

  • in-memory :如果您希望避免应用程序之间不必要的I/O
  • on-disk :如果需要磁盘上的中间输出

此混合模式基于 Application::ConnectImage() 功能。这是您使用它的方式:

# for in-memory mode
app1 = otb.Registry.CreateApplication("Smoothing")
app2 = otb.Registry.CreateApplication("Smoothing")

app1.IN = input_image

app2.ConnectImage("in", app1, "out")
app2.OUT = output_image
app2.ExecuteAndWriteOutput()

与标准的内存连接相比,您可以注意到:

  • 进行连接的语法更简单
  • 你不需要打电话 Execute() 在上游应用程序上,这是通过调用 ExecuteAndWriteOutput() 关于最新的申请

该代码的磁盘版本非常相似:

# for on-disk mode
app1 = otb.Registry.CreateApplication("Smoothing")
app2 = otb.Registry.CreateApplication("Smoothing")

app1.IN = input_image
app1.OUT = temp_image

app2.ConnectImage("in", app1, "out")
app2.OUT = output_image
app2.PropagateConnectMode(False)
app2.ExecuteAndWriteOutput()

该功能 PropagateConnectMode() 在上游应用程序中递归应用。它允许在两种模式之间切换:

  • True :表示内存模式(这是默认设置)
  • False :表示磁盘上模式

当您想要使用磁盘模式时,必须在 output 上游应用的图像参数 (app1.OUT 在上一个示例中)。如果此路径为空,则使用内存模式作为后备模式。

此功能也适用于 InputImageList 。调用该函数 ConnectImage("il", app1, "out")il 作为输入图像列表,将向列表追加新图像,并将其连接到输出参数 out

将参数加载并保存到XML

就像使用 command line interface 您可以将应用程序参数保存到XML文件:

# Save application parameters to XML
app = otb.Registry.CreateApplication('BandMath')
app.SetParameterStringList("il", ["image1.tif", "image2.tif"], True)
app.SetParameterString("out", out, True)
app.SetParameterString("exp", "cos(im1b1)+im2b1*im1b1", True)
app.SaveParametersToXML("parameters.xml")

并在以后加载它们以供执行:

# Load application parameters from XML
app = otb.Registry.CreateApplication("BandMath")
app.LoadParametersFromXML("parameters.xml")
app.ExecuteAndWriteOutput()

与OTB管道的相互作用

应用程序框架已经扩展,以便提供与每个应用程序内的管道交互的方法。它仅适用于使用输入或输出图像的应用程序。让我们来看看有哪些函数在 Application 班级。有很多getter函数:

函数名称 返回值
GetImageOrigin(...) 图像的原点(第一个像素中心的物理位置)
GetImageSpacing(...) 图像的带符号间距
GetImageSize(...) 最大可能区域的大小
GetImageNbBands(...) 每像素的组件数
GetImageProjection(...) 投影WKT字符串
GetMetadataDictionary(...) 整个元数据字典
GetImageRequestedRegion(...) 请求的区域
GetImageBasePixelType(...) 基础Image/VectorImage的像素类型。
GetImageMetadata(...) ImateMetadata对象

所有这些getters函数都使用以下参数:

  • key :包含图像参数键的字符串
  • idx :可选索引(默认为0),可用于透明访问ImageList参数

还有一个将订单发送到管道的函数:

PropagateRequestedRegion(key, region, idx=0) :在图像上设置给定的RequestedRegion并传播它,返回内存打印估计。此函数可用于测量产生完整输出摘录所需的输入图像的请求部分。

注意:请求的区域(和OTB::Image的C++API中的其他区域一样)由图像索引和大小组成,后者定义了完整图像的矩形提取。

这组函数已用于增强OTB图像和Numpy阵列之间的桥梁。现在,Python中提供了导入和导出函数,可在转换为Numpy数组期间保留图像的元数据:

  • ExportImage(self, key) :将输出图像参数导出到Python词典。
  • ImportImage(self, key, dict, index=0) :将图像从Python词典导入到图像参数中(作为单波段图像)。
  • ImportVectorImage(self, key, dict, index=0) :将图像从Python词典导入到图像参数中(作为多波段图像)。

所使用的Python词典具有以下条目:

  • 'array' :包含像素缓冲区的Numpy数组
  • 'origin' :图像的原点
  • 'spacing' :图像的有符号间距
  • 'size' :图像的完整大小
  • 'region' :缓冲区中存在的图像区域
  • 'metadata' :元数据词典(包含投影,...)

元数据词典包含各种类型的数据。以下是该词典的可用关键字,按类型排序:

  • 加倍:

    绝对校准常量、平均场景高度、蓝色显示通道、CalFactor、CalScale、居中间隔角度、DataType、绿色显示通道、线距、NoData、列数、线数、轨道数、PRF、物理Bias、物理Gain、像素间距、RSF、雷达频率、RangeTimeFirstPixel、RangeTimeLastPixel、RangeTimeLastPixel、RedDisplay Channel、RescalingFactor、SatAzimth、SatalElevation、SolarRadiance、SpectralMax、SpectralMin、SpectralStep、Sunazimth、SunelElevation、TileHintX、TileHintelPixel、RangeTimeLast Pixel、RedDisplay Channel、RescalingFactor、Satazuth、SateElevation、SolarRadiance、SpectralMax、SpectralMin、SpectralStep、Sunazimth、SuneElevation、TileHintX、TileHintelSpacing、RangeTime Last Last Pixel、RangeDisplay Channel、Rescaling Factor、Satazuth、SateElevation、SolarRadiance、SpectralMax、SpectralMin、SpectrStep、Sunazimth、SuneElevation、TileHintx、TileHintage

  • 字符串:

    AREA_OR_POINT、BANDNAME、BEAM MODE、BEAM SWATH、EnhancedBandNAME、GeometricLevel、InstrumentIndex、Layer_TYPE、METADATATYPE、MISTION、MODE、OTB_VERSION、OrbitDirection、极化、ProductType、RadiometricLevel、SensorID、SWATH

  • LUT 1D:

    SpectralSensitivity

  • 时间对象:

    获取日期、获取开始时间、获取停止时间、生产日期

本词典还包含与投影和传感器模型相关的元数据。目前无法访问相应的密钥。但这本词典还提供了一些额外的方法:

  • GetProjectedGeometry() 返回表示投影的字符串。它可以是WKN、EPSG或项目字符串。
  • GetProjectionWKT() 返回一个字符串,该字符串将投影表示为WKT。
  • GetProjectionProj() 将表示投影的字符串作为投影字符串返回。

下面是关于该界面的一些基本问答:

  • What portion of the image is exported to Numpy array?

    默认情况下,会导出整个图像。如果您有一个非空的请求区域(调用PropagateRequestedRegion()的结果),则会导出该区域。

  • What is the difference between ImportImage and ImportVectorImage?

    第一个是为需要单波段OTB::图像的应用程序准备的。在大多数情况下,您将使用第二个:ImportVectorImage。

  • What kind of objects are there in this dictionary export?

    该数组是一个数值.ndarray。其他字段是来自OTB库的包装对象,但您可以用一种Python方式与它们交互:它们支持 len()str() 运算符和方括号运算符 [] 。他们中的一些人还拥有 keys() 功能就像字典一样。

该界面允许您将OTB图像(或摘录)导出到Numpy数组,并通过其他方式对其进行处理,然后使用保留的元数据重新导入。请注意,这与内存连接不同。

下面是一个可以做到的小例子:

import otbApplication as otb
from sys import argv

# Create a smoothing application
app = otb.Registry.CreateApplication("Smoothing")
app.SetParameterString("in",argv[1])

# only call Execute() to setup the pipeline, not ExecuteAndWriteOutput() which would
# run it and write the output image
app.Execute()

# Setup a special requested region
myRegion = otb.itkRegion()
myRegion['size'][0] = 20
myRegion['size'][1] = 25
myRegion['index'][0] = 10
myRegion['index'][1] = 10
ram = app.PropagateRequestedRegion("out",myRegion)

# Check the requested region on the input image
print(app.GetImageRequestedRegion("in"))

# Create a ReadImageInfo application
app2 = otb.Registry.CreateApplication("ReadImageInfo")

# export "out" from Smoothing and import it as "in" in ReadImageInfo
ex = app.ExportImage("out")
app2.ImportVectorImage("in", ex)
app2.Execute()

# Check the result of ReadImageInfo
someKeys = ['sizex', 'sizey', 'spacingx', 'spacingy', 'sensor', 'projectionref']
for key in someKeys:
  print(key + ' : ' + str(app2.GetParameterValue(key)))

# Only a portion of "out" was exported but ReadImageInfo is still able to detect the
# correct full size of the image

边角箱

在使用Python包装器时,有几种特殊情况需要注意。它们往往是局限性,总有一天可能会在未来的版本中得到解决。如果发生这种情况,本文档将报告修复该问题的OTB版本。

调用ZonalStatistics应用程序时的多线程问题

有一个已知的多线程问题,只有在您调用时才会在Python中发生,如果您使用的是>=3.8,则可能会得到错误“free():Inside Next Size”。这是由一个python Gil问题引起的,该问题有时会在线程尝试访问内存之前使一部分内存失效。欲了解更多信息,请参阅关于锻造的第2334期

解决此问题的方法是通过命令行或图形工具在C++/中使用ZonalStatistics应用程序。

调用Update参数()

UpdateParameters() 可用于PythonAPI。但在正常使用中,它不需要手动调用。从OTB 7.0.0及更高版本开始,每次调用 SetParameter*() 方法:研究方法。对于早期版本的OTB,您可能需要在设置参数后调用它。

NumPy数组中没有元数据

使用NumPy模块,可以在OTB和NumPy数组之间转换图像。例如,从OTB数组转换为NumPy数组时:

  • 一个 Update() 对底层的 otb::VectorImage 是被请求的。请注意,已生成完整图像。
  • 像素缓冲区被复制到 numpy.array

正如您所看到的,没有导出元数据,例如原点、间距、地理投影。这意味着,如果您想要将NumPy数组重新导入OTB,该映像将不会有任何这些元数据。这可能会给与几何图形、投影和校准相关的应用程序带来问题。

未来的开发可能会提供一种更适合在OTB和Python世界之间导入和导出图像的结构。