23.2 示例

23.2.1 创建 HDevEngine/.NET 应用程序

本节介绍如何使用 .NET Core 创建一个简单的 HDevEngine/.NET 应用程序。如需更全面的说明,请阅读 "使用 HDevEngine/.NET" 一节

  1. 为系统安装 .NET Core SDK。
  2. 在命令行中运行以下命令:

    
    dotnet new console -n hdevengine-example
    cd hdevengine-example
    dotnet add package MVTec.HalconDotNet -v 23050
    

  3. Program.cs 的内容更改为

    using System;
    using System.Diagnostics;
    using System.IO;
    
    using HalconDotNet;
    
    namespace hdevengine_example
    {
      class Program
      {
        static void Main(string[] args)
        {
          string ExampleDir = HSystem.GetSystem("example_dir");
          string ProcedurePath = "/hdevengine/procedures";
          
          HDevEngine Engine = new HDevEngine();
          Engine.SetProcedurePath(Path.GetFullPath(ExampleDir + ProcedurePath));
    
          HImage Image = new HImage("fin2");
    
          HDevProcedure Procedure = new HDevProcedure("detect_fin");
          HDevProcedureCall ProcCall = new HDevProcedureCall(Procedure);
    
          ProcCall.SetInputIconicParamObject("Image", Image);
          ProcCall.Execute();
    
          HTuple FinArea = ProcCall.GetOutputCtrlParamTuple("FinArea");
          Console.WriteLine(String.Format("Fin Area: {0}", FinArea.I));
        }
      }
    }
    

  4. 要运行应用程序,请在命令行中键入以下命令:

    dotnet run
    

结果,您将看到以下输出 "Fin Area: 1634'。

23.2.2 使用 HDevEngine/.NET

本节借助 C# 编写的示例应用程序说明如何使用 HDevEngine/.NET,这些示例应用程序位于 %HALCONEXAMPLES%\hdevengine\c# 子目录下。大多数示例使用 Windows Forms,它仅适用于 Windows 上的 .NET Core。HDevEngine/.NET 的特定信息也适用于一般的 .NET Core。

对于某些示例,还提供 Visual Basic .NET 版本,它们位于 %HALCONEXAMPLES%\hdevengine\vb.net 子目录下。除了两种语言之间的标准差异外,它们完全相同。

示例应用程序展示了如何

23.2.3 执行 HDevelop 程序

本节将介绍如何使用 HDevEngine 加载和执行 HDevelop 程序。代码片段来自示例程序 ExecProgram,该程序用于检查塑料零件的边界是否有鳍片。图 23.1 显示了应用程序的截图;其中包含两个按钮,分别用于加载和执行 HDevelop 程序。

图 23.1: 执行 HDevelop 程序,检测边界上的鳍片。

23.2.3.1 步骤 1:初始化

首先,我们创建主 HDevEngine 类 HDevEngine 的全局实例。

private HDevEngine MyEngine = new HDevEngine();

加载表单时,我们会存储 HDevelop 程序的路径,并使用 SetProcedurePath 方法设置外部函数路径:

String ProgramPathString;

private void ExecProgramForm_Load(object sender, System.EventArgs e)
{
  string ExampleDir = HSystem.GetSystem("example_dir");
  string ProcedurePath = "/hdevengine/procedures";
  MyEngine.SetProcedurePath(Path.GetFullPath(ExampleDir + ProcedurePath));

  ProgramPathString = Path.GetFullPath(
    ExampleDir + "/hdevengine/hdevelop/fin_detection.hdev"
  );
}

请注意,后者只有在 HDevelop 程序调用外部函数时才有必要。

23.2.3.2 步骤 2:加载程序

单击按钮加载 HDevelop 程序时,将创建一个 HDevProgram 类实例,并将程序路径作为参数。此外,还会创建一个 HDevProgramCall 实例供以后使用。请注意,如果加载了程序,工作目录将被更改。

在构造函数中出现的异常,如文件名未正确指定等,将使用标准的 C# 错误处理机制进行处理:

private void LoadBtn_Click(object sender, System.EventArgs e)
{
  try
  {
    HDevProgram Program = new HDevProgram(ProgramPathString);
    ProgramCall = new HDevProgramCall(Program);
  }
  catch (HDevEngineException Ex)
  {
    MessageBox.Show(Ex.Message, "HDevEngine Exception");
    return;
  }

  ...
}

有关错误处理的更多信息,请参阅 "错误处理 " 一节

23.2.3.3 步骤 3:执行程序

单击按钮执行程序时,会调用方法 mHDEExecuteName

private void RunProgram()
{
  try
  {
    try
    {
      ProgramCall.Execute();
    }
  ...
  }
}

23.2.3.4 步骤 4:获取结果

这就是执行 HDevelop 程序所需要做的全部工作。您还可以使用 GetCtrlVarTuple 方法访问程序的 "结果",即变量。在示例程序中,我们将查询并显示提取鳍片的面积:

double FinArea;
FinArea = ProgramCall.GetCtrlVarTuple("FinArea");
Window.SetTposition(150, 20);
Window.WriteString("Fin Area: ");

请注意,只有在程序终止后才能访问程序变量。

23.2.3.5 常规: 显示结果

如何在程序运行时显示结果,请参阅 "显示" 一节

23.2.4 执行 HDevelop 函数

本节介绍执行 HDevelop 函数的示例应用程序:

23.2.4.1 执行外部 HDevelop 函数

本节将介绍如何使用 HDevEngine 加载和执行外部 HDevelop 程序。下面的代码片段来自示例程序 ExecExtProc,它与上一节描述的示例一样,检查塑料零件的边界是否有鳍片。图 23.2 显示了应用程序的截图;其中包含两个按钮,分别用于加载和执行 HDevelop 函数。

与前一个示例不同的是,结果显示是明确编程的,而不是依靠内部显示算子。

图 23.2: 执行外部 HDevelop 函数,检测边界上的鳍片。

23.2.4.2 步骤 1:初始化

在执行 HDevelop 程序时,我们会创建主 HDevEngine 类 HDevEngine 的全局实例,并在加载窗体时使用 SetProcedurePath 方法设置外部函数路径(省略构建路径的代码)。如果外部函数来自函数库,外部函数路径可能包括库文件名。

private HDevEngine MyEngine = new HDevEngine();

private void ExecExtProcForm_Load(object sender, System.EventArgs e)
{
  string ProcedurePath = "/hdevengine/procedures";
  ...
  MyEngine.SetProcedurePath(Path.GetFullPath(ExampleDir + ProcedurePath));
}

与本示例应用程序的 C++ 版本不同,我们不希望在自由浮动的图形窗口中显示结果,而是希望在窗体中,即在 HSmartWindowControl 的实例中显示结果(另见 "为可视化添加和自定义 HSmartWindowControl" 一节"可视化" 一节)。为了调用 HALCON 算子,我们为底层 HALCON 窗口声明了一个 HWindow 类全局变量;加载窗体时,我们将该变量设置为 HSmartWindowControl 中的 HALCON 窗口,并初始化该窗口:

private HWindow Window;

private void WindowControl_Load(object sender, EventArgs e)
{
  Window = WindowControl.HalconWindow;

  Window.SetDraw("margin");
  Window.SetLineWidth(4);
}

23.2.4.3 步骤 2:加载函数

单击 加载 按钮后,HDevelop 函数将通过 HDevProcedure 类的构造函数加载,同时指定函数名称,并以 HDevProcedureCall 类实例的形式创建相应的函数调用。在构造函数中出现的异常,如文件名或函数路径指定不正确,将通过标准的 C# 错误处理机制进行处理。有关错误处理的更多信息,请参阅 "错误处理" 一节

private void LoadBtn_Click(object sender, System.EventArgs e)
{
  try
  {
    HDevProcedure Procedure = new HDevProcedure("detect_fin");
    ProcCall = new HDevProcedureCall(Procedure);
  }
  catch (HDevEngineException Ex)
  {
    MessageBox.Show(Ex.Message, "HDevEngine Exception");
    return;
  }
}

执行函数由多个步骤组成。首先,我们加载一个示例图像序列:

private void ExecuteBtn_Click(object sender, System.EventArgs e)
{
  HFramegrabber Framegrabber = new HFramegrabber();
  Framegrabber.OpenFramegrabber("File", 1, 1, 0, 0, 0, 0, "default",
     -1, "default", -1, "default", "fin.seq", "default", -1, -1);

23.2.4.4 步骤 3:设置函数的输入参数

现在,每个图像都应由函数处理,该函数具有以下特征,即它将图像作为(图标)输入参数,并将检测到的鳍片区域及其面积分别作为图标和控制输出参数返回:

procedure detect_fin (Image: FinRegion: : FinArea)

我们通过 SetInputIconicParamObject 方法将图像存储在 HDevProcedureCall 实例中,从而将图像作为输入对象传递。要设置的参数可通过其名称指定(也可通过索引指定):

HImage Image = new HImage();
HRegion FinRegion;
HTuple FinArea;

for (int i = 0; i <= 2; i++)
{
  Image.GrabImage(Framegrabber);
  Image.DispObj(Window);

  ProcCall.SetInputIconicParamObject("Image", Image);

除了传递参数,您还可以在 HDevEngine 中使用全局变量(请参阅 HDevelop 用户向导 )。您可以使用 SetGlobalIconicVarObjectSetGlobalCtrlVarTuple 方法设置全局变量的值,也可以使用 GetGlobalIconicVarObjectGetGlobalCtrlVarTuple 方法查询全局变量。
不过,请注意不要用一个程序的变量值覆盖另一个程序的变量值: 在所有运行的 HDevEngine 实例中,每个全局变量一次只能有一个值。

23.2.4.5 步骤 4:执行函数

现在,我们使用 mHDEExecuteName 方法执行函数。

  ProcCall.Execute();

23.2.4.6 步骤 5:获取函数的输出参数

如果函数执行成功,我们可以使用类 HDevProcedureCallnmHDEGetOutputIconicParamRegionGetOutputCtrlParamTuple 方法访问其结果,即鳍片区域及其面积;同样,您可以通过参数名称或索引指定参数。请注意,您可以使用 GetOutputIconicParamObject 将图标输出对象作为相应类(此处为 HRegion)的实例或作为 HObject 的实例获取。

  FinRegion = ProcCall.GetOutputIconicParamRegion("FinRegion");
  FinArea = ProcCall.GetOutputCtrlParamTuple("FinArea");

23.2.4.7 步骤 6:显示函数结果

最后,我们在图形窗口中显示结果:

  this.Invoke((MethodInvoker)delegate
  {
    Image.DispObj(Window);
    Window.SetColor("red");
    Window.DispObj(FinRegion);
    Window.SetColor("white");
    Window.SetTposition(150, 20);
    Window.WriteString("FinArea: " + FinArea.D);
  });

23.2.4.8 执行本地和外部 HDevelop 函数

示例应用程序 ExecProcedures 使用 HDevEngine 执行本地和外部 HDevelop 函数。它模仿了 "执行 HDevelop 程序" 一节 中描述的 HDevelop 程序的行为。结果显示部分由显式编程完成,部分委托给 HDevelop 函数,使用 "显示" 一节 中描述的内部显示算子实现。图 23.3 显示了应用程序的截图。

图 23.3: 应用程序截图

下面,我们将简要介绍部分代码。

本地函数和外部函数的创建和执行方式完全相同。唯一不同的是,要使用本地函数,必须加载其所包含的程序,而要加载外部函数,则必须设置函数路径。在本例中,图像处理函数是本地函数,而另一个是外部函数。请注意,此处省略了构建程序和函数路径的代码。

private HDevProcedureCall InitAcqProcCall;
private HDevProcedureCall ProcessImageProcCall;
private HDevProcedureCall VisualizeDetailsProcCall;

private void ExecProceduresForm_Load(object sender, System.EventArgs e)
{
  string ProcedurePath = "/hdevengine/procedures";
  ...
  MyEngine.SetProcedurePath(Path.GetFullPath(ExampleDir + ProcedurePath));
}

private void LoadBtn_Click(object sender, System.EventArgs e)
{
  try
  {
    HDevProgram Program = new HDevProgram(ProgramPathString);

    HDevProcedure InitAcqProc = new HDevProcedure(Program, "init_acquisition");
    HDevProcedure ProcessImageProc = new HDevProcedure(Program, "detect_fin");
    HDevProcedure VisualizeDetailsProc =
          new HDevProcedure(Program, "display_zoomed_region");

    InitAcqProcCall = new HDevProcedureCall(InitAcqProc);
    ProcessImageProcCall = new HDevProcedureCall(ProcessImageProc);
    VisualizeDetailsProcCall = new HDevProcedureCall(VisualizeDetailsProc);
  ...
}

其中一个函数会打开图像采集设备。它会返回相应的句柄,我们将其存储在 HFramegrabber 类的一个实例中。

private HFramegrabber Framegrabber;

private void InitAcqBtn_Click(object sender, System.EventArgs e)
{
  InitAcqProcCall.Execute();
  Framegrabber =
      new HFramegrabber(InitAcqProcCall.GetOutputCtrlParamTuple("AcqHandle").H);
  ...
}

在示例应用程序中,当应用程序终止并调用类 HFramegrabber 的终结器时,设备将被关闭,而终结器又会调用算子 CloseFramegrabber。如果使用 HDevelop 函数关闭与设备的连接,就会使句柄失效,从而导致终结器引发异常。

与前面的示例一样,图像处理的结果(按钮 Process Image)是通过调用 HALCON/.NET 算子 "手动" 显示的。相反,当您单击 "Visualize Details" 按钮时,将执行一个 HDevelop 函数,放大提取的鳍片。为此,我们将传递 HDevelop 内部显示算子的实现(有关实现类的更多信息,请参阅 "显示" 一节 ),并在执行函数后将其移除。

private void VisualizeDetailsBtn_Click(object sender, System.EventArgs e)
{
  MyEngine.SetHDevOperators(MyHDevOperatorImpl);

  VisualizeDetailsProcCall.SetInputIconicParamObject("Image", Image);
  VisualizeDetailsProcCall.SetInputIconicParamObject("Region", FinRegion);
  VisualizeDetailsProcCall.SetInputCtrlParamTuple("ZoomScale", 2);
  VisualizeDetailsProcCall.SetInputCtrlParamTuple("Margin", 5);
  VisualizeDetailsProcCall.Execute();

  MyEngine.SetHDevOperators(null);
}

执行类的实例通过表单的 HALCON 窗口初始化。

private HDevOpMultiWindowImpl MyHDevOperatorImpl;

private void WindowControl_Load(object sender, EventArgs e)
{
  Window = WindowControl.HalconWindow;
  ...
  MyHDevOperatorImpl = new HDevOpMultiWindowImpl(Window);
}

如果类 HDevOpMultiWindowImpl 在初始化时没有指定窗口,则会自动打开一个新的 HALCON 窗口,以模拟 HDevelop 的行为。因此,在 HDevelop 程序或函数中使用算子 dev_open_window 将打开另一个窗口。新打开的窗口会自动设置为活动状态。

23.2.5 显示

与 C++ 版本的 HDevEngine 不同,HDevEngine/.NET 已经以两个类的形式提供了 HDevelop 内部显示算子的便捷实现:

在示例代码中,部分程序和函数的实际执行被委托给了后台线程。这是一种很好的做法,因为长时间执行会导致图形用户界面反应迟钝。此外,如果图形用户界面线程被阻塞,使用交互式绘图对象的 HDevelop 代码将无法使用 HSmartWindowControl

虽然 HALCON 显示算子是线程安全的,但对 Windows 窗体元素的访问却不是(例如,启用按钮或设置标签文本)。因此,在这些示例中,使用 Invoke() 调用将结果可视化委托回 GUI 线程。

最后,通过 HDevOpFixedWindowImplHDevOpMultiWindowImpl 使用 dev_* 片子进行可视化对于多线程应用程序(并行执行多个程序)是有限制的。这是因为在任何时候都只有一个全局 "活动" 窗口(由 dev_set_window 控制),因此线程无法独立控制其输出窗口。这种行为与 HDevelop(也只有一个活动窗口)中通过 par_start 使用多线程时的行为一致。

果需要并行可视化,我们建议使用直接输出到所需窗口的 HALCON 算子编写显式可视化代码。更多信息,参见 "创建多线程应用程序" 一节

示例程序 ExecProgram 使用了 HDevOpMultiWindowImpl。要使用该类(或 HDevOpFixedWindowImpl),可通过 nmHDESetHDevOperatorsName 方法将其实例传递给 HDevEngine

private void WindowControl_Load(object sender, EventArgs e)
{
  Window = WindowControl.HalconWindow;

MyEngine.SetHDevOperators(new HDevOpMultiWindowImpl(Window));
}

如果您的应用程序有这两个类无法满足的特殊显示要求,您可以创建一个实现接口 IHDevOperators 的类,并重载其 DevOpenWindowDevDisplay 等方法,从而提供自己的显示算子实现,类似于 C++ 版本的 HDevelop(参见 "显示" 一节 )。

23.2.6 错误处理

在本节中,我们将仔细研究 HDevEngine 中的异常。下面的代码片段来自示例应用程序 ErrorHandling,该程序会在按下按钮时引发并捕获不同类型的异常。图 23.4 显示了应用程序的截图。

图 23.4: 在 HDevEngine 中引发异常。

HDevEngine 将异常作为 HDevEngineException 类的实例抛出,该类包含异常的类型(类别)、描述异常的消息,以及根据异常类型提供的信息,如执行函数的名称或 HALCON 错误代码(另见 "HDevEngineException" 一节 )。

In the example application, the following procedure displays all the information contained in HDevEngineException in a message box: 在示例应用程序中,以下函数将在消息框中显示 HDevEngineException 中包含的所有信息:

private void DisplayException(HDevEngineException Ex)
{
  string FullMessage = "Message: <" + Ex.Message + ">" +
    ",  Error in program / procedure: <" + Ex.ProcedureName + ">" +
    ",  program line: <" + Ex.LineText + ">" +
    ",  line number: <" + Ex.LineNumber + ">" +
    ",  HALCON Error Number: <" + Ex.HalconError + ">";

  string Title = "HDevEngine Exception (Category: " +
    Ex.Category.ToString() + ")";

  MessageBox.Show(FullMessage, Title);
}

当出现异常时,将调用此函数;请注意,前几节中描述的示例应用程序仅显示异常消息。

try
{
  HDevProgram Program = new HDevProgram(ProgramPathString);
  new HDevProgramCall(Program);
}

catch (HDevEngineException Ex)
{
  DisplayException(Ex);
  return;
}

图 23.5 显示了由于应用程序试图加载一个不存在的 HDevelop 程序(类别 ExceptionFile)而发生的异常。正如您所看到的,在这种情况下,只有消息包含有用的信息。

图 23.5:无法加载 HDevelop 程序时的异常内容。

下一个异常发生在执行一个输入参数未初始化的函数时(类别 ExceptionInpNotInit):

procedure detect_fin_with_error_inpnotinit (Image: FinRegion: : FinArea)
  bin_threshold (NotExistingImage, Dark)
  ...

图 23.6 显示了异常的内容,其中包含关于错误发生位置和原因的详细信息。

图 23.6: 输入参数未初始化时的异常内容

最后一个异常是在执行函数时产生的,在该程序中,由于第三个参数无效,对算子 closing_circle 的调用失败(类别 ExceptionCall)。

procedure detect_fin_with_error_call (Image: FinRegion: : FinArea)
  bin_threshold (Image, Dark)
  difference (Image, Dark, Background)
  dev_set_color ('blue')
  dev_display (Background)
  closing_circle (Background, ClosedBackground, -1)
  ...

图 23.7 显示了异常情况的内容。

图 23.7:HALCON 算子调用发生错误时的异常内容。

通过UserData方法(参见 "HDevEngineException" 一节),你还可以访问用户异常数据,这些数据是在HDevelop程序或函数中通过算子 throw 抛出的,类似于算子 dev_get_exception_data

如果出现异常(未在函数中捕获),函数调用将被清理。这意味着所有子线程都会被销毁,所有输入和输出参数的值都会被清除。因此,我们建议您在执行调用前设置所有输入参数,即使其中一些参数没有发生变化。

请注意,在加载包含无效行或未解决的过程调用的程序时,可以通过 SetEngineAttribute 方法配置 HDevEngine 的行为(参见 "HDevEngine" 一节 )。

23.2.7 创建多线程应用程序

HALCON 提供了两个使用 HDevEngine/.NET 多线程的 C# 示例应用程序:

在下文中,我们将简要列出创建多线程 HDevEngine 应用程序时需要遵守的最重要规则。此外,请参阅 "使用 HALCON 进行并行编程" 一节 中有关使用 HALCON 进行并行编程的一般信息,尤其是 "并行化的程序设计问题" 一节 中的样式指南。

23.2.7.1 多个线程并行执行函数

本节介绍的示例应用程序 MultiThreading 利用多核或多处理器系统,通过两个线程并行执行同一个 HDevelop 函数(任务)。该函数使用基于形状的匹配来查找瓶盖。

图 23.8 显示了应用程序的结构概览。它由四个线程组成: 主线程(即窗体)负责图形用户界面(GUI),如图 23.9 所示。它由一个用于显示结果的 HALCON 窗口和用于初始化、启动和停止应用程序的按钮组成。

图 23.8: 线程的任务

图 23.9:应用程序截图。

主线程还通过 HDevelop 函数训练形状模型,并通过创建和初始化其他三个线程来初始化应用程序:两个处理线程和所谓的控制线程,控制线程控制这两个处理线程。

控制线程获取图像并将其传递给处理线程,然后处理线程处理图像并将结果传回。控制线程收集结果,但本身并不显示结果,因为 HALCON 窗口中的所有活动都必须由创建该窗口的线程(即主线程)执行。

现在,我们来仔细看看相应的代码。请注意,我们没有显示所有细节,特别是错误处理和终止包括内存管理。

初始化

应用程序在 Init 按钮(文件:MultiThreadingForm.cs)的事件处理程序中初始化。

private void InitButton_Click(object sender, System.EventArgs e)

图像处理

单击 运行 按钮后,应用程序开始循环处理图像。

结果显示

23.2.7.2 多个线程并行执行多个函数

与上一节不同的是,这里介绍的示例应用程序 MultiThreadingTwoWindows 通过两个线程并行执行不同的 HDevelop 函数(任务)。一个任务是使用形状匹配查找瓶盖,另一个任务是读取 ECC 200 数据码。

图 23.10 显示了应用程序的结构概览。与上一节描述的应用程序一样,它由四个线程组成: 主线程(即窗体)负责图形用户界面(GUI),如 图 23.9 所示。它由一个用于显示结果的 HALCON 窗口和用于初始化、启动和停止应用程序的按钮组成。

主线程还通过创建和初始化其他三个线程(两个处理线程和控制两个处理线程的所谓控制线程)来初始化应用程序。与之前的应用程序不同,这里的处理线程通过 HDevelop 函数分别训练形状模型和数据码模型,从而初始化图像处理任务。

控制线程获取图像并将其传递给处理线程,然后处理线程处理图像并将结果传回。控制线程收集结果,但本身并不显示结果,因为 HALCON 窗口中的所有活动都必须由创建该窗口的线程(即主线程)执行。与前一个应用程序不同的是,两个任务的结果分别显示在两个窗口中。

下面,我们将仔细研究相应的代码,但仅限于与前一个应用程序不同的部分。

图 23.10:线程的任务。

图 23.11:应用程序截图。

初始化

与上一个示例一样,应用程序在 Init 按钮(文件:MultiThreadingTwoWindowsForm.cs)的事件处理程序中初始化。

图像处理

结果显示

与上一个示例一样,结果显示由主线程在方法 ResultDisplay(文件:MultiThreadingTwoWindowsForm.cs)中执行。主要区别在于,现在是根据结果容器中的变量在两个 HALCON 窗口之间切换显示。

public void DisplayResults()
{
  HWindow Window;

  if (Result.WindowIndex == 1)
  {
    Window = Window1;
  }
  else
  {
    Window = Window2;
  }

此外,现在的显示方法会检查图像处理是否成功,以避免访问不存在的结果元素。对于这两项任务,都会显示结果轮廓,即分别找到的形状或数据码区域。对于数据码任务,还会显示读取的码。

Window.ClearWindow();
Window.DispImage(Result.InputImage);
if (Result.DetectionSuccessful)
{
  Window.DispObj(Result.FoundContours);
  // Additional display for data code result: code.
  if (Result.WindowIndex == 2)
  {
    Row = (int)Result.ResultData[0].D;
    Col = (int)Result.ResultData[1].D;
    Window.SetTposition(Row, Col);
    Window.WriteString((string)Result.ResultData[2].S);
  }
}
else
{
  Window.SetColor("red");
  Window.SetTposition(20, 20);
  Window.WriteString("Detection failed!");
  Window.SetColor("green");
}

23.2.8 使用向量变量执行 HDevelop 程序

示例应用程序 UseVectorVariables 展示了如何在 HDevengine/C# 中加载和执行包含向量变量的 HDevelop 示例。示例中使用了两个向量进行处理:一个包含输入图像,另一个包含缩放因子。执行程序时,输入图像的灰度值将根据缩放因子进行缩放。请查看源文件 UseVectorVariablesForm.cs,了解如何在 HDevengine/.NET 中使用向量变量的详细信息。