在C# Winform程序中调用Matlab作多个图像

  1. 概述
    本文主要解决以下几个问题:

    1. Matlab函数生成Dll;
    2. 在C#程序中调用生成的dll;
    3. 程序中需要同时显示多个Figure时的处理。
  2. 问题背景
    工作中遇到在winform中作3维点云图的需求,网上翻阅了很多资料无果,最终无奈选择使用matlab作图,将matlab的figure嵌入到程序窗体中。
    由于存在同时显示多个图像的需求,网上有将图像窗口嵌入程序中的方法,但没有找到同时使用多个figure的情况,为此做了一些尝试,将尝试的过程做简单记录。
    测试过程中发现使用matlab的图像窗口会导致程序内存占用上升较多,特别是使用多个图像窗口时,所以以后如果能找到替代方法,预计会将这种做法废弃。
    本文中用到的方法其实不局限于winform程序。

  3. Matlab函数生成Dll

    • 环境:Matlab r2020a
    • 函数:
    % 函数作用:根据传入的一维数组,画3维点云图
    function [] = draw(xyz)
    figure()
    % 将一维数组转换成3*n并转置,因为点云是由一些(x,y,z)坐标的点构成
    a = reshape(xyz, 3, length(xyz) / 3).';
    % 转换为点云类型
    p = pointCloud(b);
    % 这是一个专门用来作点云图的方法
    % 听说如果是4维的数据的话,第4维可以用来指定颜色,如果没有指定颜色的话,默认渲染出来的彩图颜色还挺好看的
    pcshow(p);
    
    • dll生成步骤:
      命令行输入deploytool->选择Library Compiler->选择.Net Assembly->增加函数文件->修改className->选择Package->选择生成的文件夹for_redistribution_files_only中的dll文件,我使用的是没有Native后缀的文件。

  4. C#程序中调用Dll
    程序引用中添加Com组件,选择Matlab Appliaction Type Library;

    浏览,添加MWArray.dll,我的这个dll的路径在C:/Windows/Microsoft.Net/assembly/GAC_MSIL/MWArray下面,这边注意要根据是32位程序还是64位程序作出选择,我的程序是AnyCpu;
    添加上一步骤中生成的dll的引用;
    程序中增加对上面两个dll的using。
    使用时:以我上面的函数为例,

    Draw draw = new Draw();
    // int[]类型需要转换为MWNumericArray
    draw.draw((MWNumericArray)new int[] { 0, 0, 0});
    

    注意:此处的Draw是IDisposable的,用完之后一定要Dispose掉,new出来并使用的Draw对象越多,占用的内存越大,这个东西占用的内存还是很大的。

  5. 多个图同时显示并存在特定的刷新需求
    这个问题其实是我本篇中所要解决的最主要的问题,上面所说的这些内容都是可以在网上找到参考事项的,关于如何在winform程序中捕获到调用matlab函数生成的figure不做过多赘述,有需求可以参考文末的参考链接。

    • 解决这个问题有以下几个点需要注意或会用到:
    1. 每次调用figure会生成一个新的figure,如果不在figure的时候做出限制,figure的标题将按照顺序递增,依次为figure 1,figure 2,figure 3...
    2. 如果关闭figure,将会重新开始计数;
    3. 使用figure('toolbar', 'none', 'menubar', 'none', "name", name, 'numberTitle', 'off'); 对"name"属性和"numberTitle"属性做出限制,可以将标题中的figure n删除,并指定标题名称,"toolbar"和"menubar"属性用来关闭菜单;
    4. clf可以清除图上的内容;
    5. findobj('Type', 'figure')函数可以用来找到当前打开的所有figure,返回值是一个数组。
    • 具体要求:
    1. 同时打开多个figure;
    2. 随机可能需要刷新第n个figure上的图像。
    • 解决这个问题我尝试了以下思路:(本人非专业使用matlab,只是曾经使用过,可能有一些简便方法我不知道)
    1. 先说如果只有一个figure的情况,可以加一个flag,表明当前是否需要生成新的figure,只在需要生成新的figure的时候调用figure函数,否则调用clf函数清空已作图像;
    2. 在需要刷新的时候,生成一个新的figure,并调用user32中的SendMessage接口,将上一个窗口关闭,此前需要获取到上一个窗口的句柄,
      这种做法的问题在于窗口的闪烁非常严重,因为本来就涉及到捕获窗口的问题,figure窗口刚刚出来的时候其实是独立于我的程序界面的,很容易能够看到窗口一闪而过,而且经常会出现窗口嵌入我的程序中时窗口大小不一致,窗口很混乱,有时候能正常显示,有时候显示的数量不足,某些窗口没有显示出来,这个可能和找到的窗口句柄有关系。
      这种做法的主要思想就是找到窗口句柄,关闭不需要的窗口,以此为蓝本,我希望能够将当前的窗口句柄作为参数传入接口中,接口函数中直接将图作在拥有指定句柄的图形窗口中,然而,matlab中的窗口句柄与我通过user32获取到的窗口句柄有所不同,matlab中的窗口句柄更像是一个对象,而user32获取到的窗口句柄是int型的。通过不断尝试,终于找到了下面的方法:
    3. 为figure分配id,在调用函数的时候将id作为传入参数,以id作为figure的name,使用findobj函数查找所有已经打开的figure,根据figure.Name属性判断是否有和传入的name参数一致的figure,如果有则清空对应的figure,否则则创建新的figure
  6. 参考:C#调用Matlab画图,figure嵌入到Winform窗体

posted @ 2020-12-26 10:46  yutou2016  阅读(1638)  评论(0编辑  收藏  举报