最近在编写一个简单的遥感影像处理程序,技术路线是使用Matlab实现一个较为耗时和复杂的数据处理后端,使用VS C#(.NET Framework)实现一个简单的前端GUI界面。

由于这方面网上的资料比较多,本来没打算写(水)一篇博客的,但是实现过程中遇到了不少坑和疑问,所以还是统一记录一下。

Matlab后端运算打包dll

由于遥感影像处理多涉及矩阵运算,因此使用Matlab进行编程。将所有代码写成函数后,在“附加资源管理器”中下载并安装MATLAB Compiler和MATLAB Compiler SDK两个附加包。

在App选项卡打开Library Compiler功能,在TYPE栏选择.NET Asswmbly,在EXPORTED FUNCTIONS栏添加自己想要编译的函数。如果想要打包结果不包含Matlab运行时(MCR)以节省空间,则在PACKAGING OPTIONS中选择第一个选项,在未安装Matlab本体的计算机上运行时,选择该选项需要用户手动下载并安装免费的MCR或购买Matlab本体;否则,选择第二个选项,打包包含MCR的完整包,会占据较大空间。另外,如果被打包的函数使用了其他函数,该工具会自动一并打包,不需要额外添加。

以本程序changeDetection为例:打包完成后,打开包体位置,找到“for_redistribution_files_only”文件夹,复制changeDetectionNative.dll到VS的项目目录中。

(至于changeDetection.dll文件,官方给出的解释为)

-changeDetection.dll
-contains the generated component using MWArray API.
-changeDetectionNative.dll
-contains the generated component using native API.

然后,前往 .\matlab\toolbox\dotnetbuilder\bin\win64\v4.0路径,复制MWArray.dll到VS项目目录。至此,在Matlab中的工作已经结束。

Visual Studio中创建GUI

在建立好的Windows窗体应用程序中绘制UI界面并添加上文中两个dll文件的引用。使用时,需在对应代码文件前添加using MathWorks.MATLAB.NET.Arrays;using changeDetectionNative;以实现调用。

调用的Matlab函数返回值为一个object类,如果有多个返回值,将在该类中以数组形式储存。函数的传入参数前需添加一个整数,用以代表返回值的个数。具体示例如下:

ChangeDetection CD = new ChangeDetection();
object resultObj = CD.changeDetection(1, data.beforeImage, data.afterImage, lines, samples);//1代表函数有一个返回值
object[] resultObjs = (object[])resultObj;//将object中存储的数组拆分出来
double[,] bw = (double[,])resultObjs[0];//取出所需的返回值

如果时计算耗时较短的Matlab程序,至此即完成了前后端的集成。

耗时程序的处理

对于耗时程序,在后端运算时,前端的GUI窗体会发生假死的状态,表现为UI无法更新,无法拖动,无法操作。显然,这样不符合一般用户的直觉。而遥感影像处理的Matlab后端程序基本上都需要消耗较长的时间,因此了解C#中耗时程序的处理是有必要的。

出现该状况的原因在于,后端的计算过程占用了UI更新的主线程,导致程序无法处理相关操作。解决方法也很简单,使用异步操作使程序以多线程的形式运行即可。

在C#中,使用await运算符(异步等待任务完成)创建多线程很容易,首先在执行按钮的函数中添加修饰词async,即private async void button1_Click(object sender, EventArgs e),然后将前文的代码改为:

ChangeDetection CD = new ChangeDetection();
object resultObj = await Task.Run(() =>
{
    return CD.changeDetection(1, data.beforeImage, data.afterImage, lines, samples);
});
object[] resultObjs = (object[])resultObj;//将object中存储的数组拆分出来
double[,] bw = (double[,])resultObjs[0];//取出所需的返回值

这样,在程序运行时,UI界面就可以正常更新和操作了。

至此,Matlab集成C#的遥感影像处理程序的技术框架大致构建完成,只需要继续添加需要的功能即可。

参考资料

  1. C#调用Matlab函数 - 知乎 (zhihu.com)
  2. 最简单解决c#在UI线程中执行耗时方法导致界面假死的方法_c# task.run 假死-CSDN博客
  3. await 运算符 - 异步等待任务完成 - C# | Microsoft Learn

“好久不见,Handler One”