如何编写应用程序

本章介绍编写您自己的应用程序的不同步骤,以及围绕它的框架。

应用程序设计

第一个合乎逻辑的步骤是定义应用程序的角色:

  • 您的应用程序的功能是什么?尝试绘制框图来描述应用程序的设计。请注意,您不必担心打开和保存图像(或矢量数据)文件,这是由框架处理的。
  • 哪些变量(或数据对象)必须在应用程序外部公开?试着列出你的应用程序的输入、输出和参数。

然后,您应该对您的应用程序管道有一个很好的愿景。根据使用的不同筛选器,可以对应用程序进行流式处理和线程化处理。筛选器之间的线程功能可能不同,因此没有总体线程参数(默认情况下,每个筛选器都有其自己的线程设置)。

对于流媒体来说,情况就不同了。由于镜像编写器是在框架内和开发人员无法控制的范围内处理的,因此默认行为是使用流。如果其中一个滤镜不支持流,它会将请求的输出区域放大到可能的最大区域,并立即处理整个图像。因此,开发人员不必处理流或线程。但是,有一种方法可以选择流媒体部门的数量(请参阅一节 参数选择 )。

班级的架构

每个应用程序都派生自类 otb::Wrapper::Application 。应用程序不能模板化。它必须包含标准类typedef和对 OTB_APPLICATION_EXPORT 宏命令。

您还需要定义标准宏 itk::NewMacroitk::TypeMacro

在新应用程序中还必须实现三个方法:

  • DoInit()
  • DoUpdateParameters()
  • DoExecute()

DoInit()

此方法在实例化应用程序时调用一次。它应该包含以下操作:

  • 设置应用程序的名称和描述
  • 填写文档并举例说明
  • 声明所有参数
  • 定义文档链接:用于Contrrib应用程序 SetDocLink("docLink") ,供正式申请使用 SetOfficialDocLink()

DoUpdate参数()

每次修改参数值后都会调用此方法。使用命令行启动器,每次加载参数时都会调用它。使用Qt启动器,每次修改参数字段时都会调用它。它可以用来维护参数之间的一致性和关系(例如,在ExtractROI中:当更改输入图像时,可能需要更新ROI大小)。

DoExecute()

此方法包含应用程序的实际操作。这是管道必须设置的地方。应用程序框架提供了不同的方法来获取与参数关联的值或对象:

  • GetParameterInt(key) :获取参数的整数值
  • GetParameterFloat(key) :获取参数的浮点值
  • GetParameterString(key) :获取参数的字符串值
  • GetParameterImage(key) :获取指向图像对象的指针,从输入中给出的文件名中读取

哪里 key 指参数键,使用定义 AddParameter() 中的方法 DoInit() 方法。

将数据对象绑定到输出参数也有类似的方法:

  • SetParameterOutputImage(key,data) :将图像对象链接到给定的输出参数
  • SetParameterOutputVectorData(key,data) :将矢量数据对象链接到给定的输出参数

如果可能,不应在此函数内调用任何筛选器更新。之后将自动调用更新:对于每个图像或矢量数据输出,都会创建并更新一个编写器。

参数选择

在新的应用程序框架中,每个输入、输出或参数都派生自 otb::Wrapper::Parameter 。应用程序引擎提供以下类型的参数:

  • ParameterType_Bool :存储布尔值的参数。
  • ParameterType_Int :存储整数的参数。
  • ParameterType_Radius :存储半径的参数。
  • ParameterType_Float :存储浮点数的参数。
  • ParameterType_String :参数存储字符串。
  • ParameterType_StringList :存储字符串列表的参数。
  • ParameterType_InputFilename :存储输入文件名的参数。
  • ParameterType_InputFilenameList :存储输入文件名列表的参数。
  • ParameterType_Directory :存储文件夹名称的参数。
  • ParameterType_Group :参数存储子参数。
  • ParameterType_Choice :存储选项列表的参数(不支持多选)。它还允许为每个可用选项创建特定子参数。
  • ParameterType_ListView :存储选项列表的参数(支持多选和单选)。
  • ParameterType_InputImage :存储输入图像的参数。
  • ParameterType_InputImageList :存储输入图像列表的参数。
  • ParameterType_InputVectorData :存储输入矢量数据的参数。
  • ParameterType_InputVectorDataList :存储输入矢量数据列表的参数。
  • ParameterType_OutputFilename :存储输出文件名的参数。
  • ParameterType_OutputImage :存储输出图像的参数。
  • ParameterType_OutputVectorData :存储输出矢量数据的参数。
  • ParameterType_RAM :存储要使用的最大内存量的参数。
  • ParameterType_Field :存储给定矢量数据中的字段列表的参数(支持多选和单选)。
  • ParameterType_Band :存储给定栅格数据的波段列表的参数(支持多选和单选)。

参数说明

每个创建的参数都有一个唯一的键和几个布尔标志来表示其状态。这些标志可用于将参数设置为可选或测试用户是否已修改参数值。这些参数是在 DoInit() 方法,则框架将设置它们的值(通过解析命令行或读取图形用户界面)。这个 DoExecute() 当所有必需参数都已赋值时,调用方法,该值可以通过 otb::Wrapper::Application 。使用设置强制(或非强制)参数 MandatoryOn(key) 方法 (MandatoryOff(key) )。

有些函数是特定于数字参数的,例如 SetMinimumParameterIntValue(key,value)SetMaximumParameterFloatValue(key,value) 。默认情况下,数值参数被视为输入。如果应用程序输出数字,则可以使用数值参数并通过调用 SetParameterRole(key,Role_Output)

输入类型 InputImageInputImageListInputVectorDataInputVectorDataList 存储要加载的文件的名称,但它们也封装了生成输入数据所需的读取器。

输出类型 OutputImageOutputVectorData 存储要写入的文件的名称,但它们还封装了相应的编写器。

复合应用程序

应用程序框架已扩展为允许实现复合应用程序: applications that use other applications 。概念很简单:您有两个要链接的应用程序A和B,以便构建第三个应用程序C。您希望重用应用程序A和B,而不是通过复制A和B的代码来编写C。这个简单的示例将在本节中重新使用以进行解释。

专门的班级 otb::Wrapper::CompositeApplication 存在用于创建这样的应用程序的。如果您派生此类来实现应用程序C,则将能够创建复合应用程序。

创建内部应用程序

与标准应用程序一样,您必须编写一个 DoInit() 功能。在此函数中,您应该首先使用函数清除任何内部应用程序 ClearApplications() ( DoInit() 函数在某些情况下被调用两次)。然后,您可以实例化要使用的内部应用程序(例如A和B)。该功能 AddApplication() 将根据以下条件来实现这一目标:

  • 应用程序类型(即其官方名称,如ExtractROI、BandMath、…)
  • 一个标识符:与参数键一样,您必须指定一个标识符来引用这个内部应用程序。使用与参数相同的命名约定。
  • A Description:对此内部应用程序的角色做一个简短的描述。

使用函数 GetInternalApplication() ,则可以获取指向与给定标识符相对应的内部应用程序的指针。

在引言中给出的示例中,我们假设:

  • 已使用标识符添加了类型A的内部应用程序 a
  • 已使用标识符添加了类型B的内部应用程序 b

连接参数

一旦拥有了内部应用程序,您可能需要设置它们的参数。通常有3个病例。

您可能希望将内部应用程序的参数公开为复合应用程序的参数。假设您想要公开参数 io.in 从应用程序 a 使用键插入到复合应用程序C中 input 。您可以调用该函数:

ShareParameter(input,a.io.in)

因此,这些参数 input 在应用程序C和 io.in 在应用中 a 将指向相同的对象。在两个参数键下,有一个唯一的值。这两个参数可以认为是同步的。

这导致了第二种情况:您可能希望从内部应用程序同步两个参数。假设您想要同步参数 field 从应用程序 a 带参数 fname 从应用程序 b 。您可以调用该函数:

Connect(a.field,b.fname)

请注意,这些函数 ShareParameter()Connect()

  • 使用相同的语法访问内部参数(“应用程序标识符”点“参数键”)。
  • 在添加内部应用程序后,应在DoInit()函数中使用。

在这种同步中,这两个参数应该具有相同的类型,或者具有类似的接口,例如,输入和输出文件名都可以使用 GetParameterString()SetParameterString()

这种类型的连接是对第三种情况的过渡:您可能希望将一个内部应用程序的输出连接到另一个内部应用程序的输入。这里的困难在于,要连接的两个参数可能具有不同的类型。假设您想要连接参数 a.out TO参数 b.in 。在有利的情况下,“Connect()”函数可能会起作用(参见上一段),但对于图像,您有两个选择:

  • 从输入图像参数中的输出图像参数显式复制图像指针(使用函数 SetParameterInputImage()GetParameterOutputImage() )。它将连接应用程序A和B中的管道,以形成一个“内存”连接。此操作必须在调用 DoExecute() 应用程序A和B的。
  • 使用临时文件名存储输出图像 a.out 并与其一起阅读 b.in 。在这种情况下,您必须手动调用参数的编写器 a.out

目前,不支持矢量数据参数的内存连接。

流程编排

DoUpdateParameters() 对于复合应用程序,您可以使用函数在内部应用程序上调用同一函数 UpdateInternalParameters() 。仅当您的内部应用程序在参数更新期间具有特定行为时才需要此选项。

DoExecute() 对于复合应用程序,您必须调用 ExecuteInternal() 以便启动每个内部应用程序。该顺序应与图像参数连接兼容。如果要进行“内存中”连接,可以在两次调用之间进行 ExecuteInternal() ,例如:

ExecuteInternal("a");
GetInternalApplication("b")->SetParameterInputImage("in", GetInternalApplication("a")->GetParameterOutputImage("out"));
ExecuteInternal("b");

应用程序 BundleToPerfectSensor 是复合应用程序的一个简单示例。对于更复杂的示例,您可以查看应用程序TrainImages分类器。

编译您的应用程序

为了编译您的应用程序,您必须调用宏 OTB_CREATE_APPLICATIONCMakelists.txt 文件。此宏生成库 otbapp_XXX.so ,在(OTB_BINARY_DIR/lib/OTB/Applications)中,其中 XXX 指的是类名。

执行您的应用程序

启动应用程序有不同的方式:

命令行:
命令行选项可通过以下方式调用 otbApplicationLauncherCommandLine 可执行文件,后跟类名、应用程序目录和应用程序参数。
Python :
此外,还提供了一个Python包装器。

测试您的应用程序

编写应用程序测试是可能的。它们与过滤测试非常相似。宏程序 OTB_TEST_APPLICATION 使得定义新测试变得很容易。

应用程序示例

请参见示例 ApplicationExample.cxx