前言:
本节我们尝试将一个有很多函数和文件的Matlab算法文件集成到C#的项目里面。
本文缩语:
MT = Matlab
问题提出:
1 我们有一个比较复杂的Matlab文件:
这个MATLAB的算法,写了很多的算法函数在其他的M文件里面,这样,前面博客的方法就不够用了。会报错:
解决办法如下:
写在前面的结论:
1 你要用一个外壳函数,封装你的最上层的M文件
2 去掉不必要的M文件间的耦合,把M文件的参数都写到 外壳函数上
步骤:
1 编译:
1.1 常规操作: 多入口报错
【报错】只能接受具有单个入口函数的 MATLAB 函数和 MEX 文件作为导出函数。未添加以下文件因为它们不是可接受的文件类型,或者包含在 MATLAB 根目录下:
MT ,显然不支持多个入口函数的Matlab的源码:
【解决办法,封装成一个大函数】用一个function包起来就好了
function ballfitting(fname)
% Your code here
end
这里笔者,
1.1.1 还没有MT的输出参数的传递。
在MT命令行窗口调用deploytool
对C#的输出,MT里面我们选取如下:
1 using the Matlab .NET Assembly
上图中,是没有MATLAB的输出参数的情况:但是,已经自动,把所有的MT相关的原文件都加了。
1.1.2 有MT的输出参数的传递的外壳函数封装。
function [centers,angle_degree] = findid_v1(filename)
... // your code
end
上面,
笔者给MT的外壳函数加了2个输出的的变量,和一个输入变量。
注意:外壳函数的现在的名称,findid_v1似乎没啥用,即使在C#调用,也没有用到。
然后,我们调用在MT中,命令行调用,deploytool,和前面一样。
上图,自动,把所有的MT相关的原文件都加了的同时,大家应该注意到,
类MTFindid,多了两个输入的参数。
1.2 注意库的版本,最好每次更新M的原文件的时候,选择更新一下版本号
2 在C#中的调用步骤和方法:
2.1 在C#的项目中,
然后,调用就可以了。 在【项目】/【添加引用】
把上一小结,编译生成的动态库加入。
2.2 在C#的项目Frame中,,引入库
using MathWorks.MATLAB.NET.Arrays;
using find_id;
using MathWorks.MATLAB.NET.Utility;
2.3 真正的调用:
注意!!!现在,我要开始讲到比较真正细节的解释接口编程了:
2.3.1 首先,我们要清楚,我们在MT做编译库的时候,做的类名和方法分别是什么:
// Pass the file name to the dll.
// lib name : find_id
// Class name : MTFindid
// Method name: find_id(filename)
这里,我在上面,再单独列举了一下:
2.3.2 接口编写:
private void button2_Click(object sender, EventArgs e)
{
// Create an instance of the open file dialog box.
OpenFileDialog openFileDialog1 = new OpenFileDialog();
// Set filter options and filter index.
openFileDialog1.Filter = "PLY Files (.ply)|*.ply|All Files (*.*)|*.*";
openFileDialog1.FilterIndex = 1;
// Call the ShowDialog method to show the dialog box.
DialogResult result = openFileDialog1.ShowDialog();
// Process input if the user clicked OK.
if (result == DialogResult.OK)
{
// Pass the file name to the dll.
// lib name : find_id
// Class name : MTFindid
// Method name: find_id(filename)
// Get the file name from the dialog box.
string filename = openFileDialog1.FileName;
// new object for class MTFindid
find_id.MTFindid mtfdid = new find_id.MTFindid();
// call the method
mtfdid.find_id(2, filename);
}
}
【Franlin案,详细解释,这段代码的最后两行,我在dll库find_id中,用库中的类结构MTFindid,申明了一个类的对象实例mtfdid,
然后,在源码的最后一行,我用对象mtfdid的方法find_id,调用MT库的外壳函数,
这个外壳函数,前面已经提到,有2个输出参数,一个文件名输入参数。
在上面的例子源码中,我已经给出了,输出参数,包括,C#中打开一个PLY图像,并把名字写入MT的dll的方法,下面,
我将详细讨论,MT dll如何在C#中,向C#传递输出参数。
】
3 拿出Matlab里面的输出数据:
【franklin案】要拿出Matlab的数据,MT 和 C# 两方面都需要修正:
3.1 matlab 中的修改
function [centers,angle_degree] = findid_v1(filename)
figure; imshow(segmented_img);
% Display centers and angles
disp('Centers:');
disp(centers);
disp('Angle Degree:');
disp(angle_degree);
return;
end
上述,MT 函数,外壳函数findid_v1写了个两个输出的参数,[centers,angle_degree]
这点,其实前面已经说了。
3.2 C# 中进行拾取:
private void button2_Click(object sender, EventArgs e)
{
// Create an instance of the open file dialog box.
OpenFileDialog openFileDialog1 = new OpenFileDialog();
// Set filter options and filter index.
openFileDialog1.Filter = " BMP Files (.bmp)|*.bmp|PLY Files (.ply)|*.ply|All Files (*.*)|*.*";
openFileDialog1.FilterIndex = 1;
// Call the ShowDialog method to show the dialog box.
DialogResult DialogResult = openFileDialog1.ShowDialog();
// Process input if the user clicked OK.
if (DialogResult == DialogResult.OK)
{
// Get the file name from the dialog box.
string filename = openFileDialog1.FileName;
// Pass the file name to the dll.
// lib name : find_id
// Class name : MTFindid
// Method name: find_id(filename)
// new object for class MTFindid
find_id.MTFindid mtfdid = new find_id.MTFindid();
// 不使用MT 返回值的调用
mtfdid.find_id(2, filename);
// 使用MT 返回值的调用
MWArray[] result = mtfdid.find_id(2,filename);
MWNumericArray centers = (MWNumericArray)result[0];
double[,] centersData = (double[,])centers.ToArray(MWArrayComponent.Real);
MWNumericArray angle_degree = (MWNumericArray)result[1];
double[,] angle_degreeData = (double[,])angle_degree.ToArray(MWArrayComponent.Real);
}
}
上面是一段,已经编译验证过的,成功将MT 输出参数导出到 C# 的例子:
引入的库如上:
解释几个有意思的东西:
3.2.1 MT dll的输入,输出的解释
MWArray[] result = mtfdid.find_id(2,filename);
n the code
MWArray[] result = obj.findid_v1(2, "your_file_path");
, the number2
represents the number of output arguments that the MATLAB functionfindid_v1
is expected to return.In your MATLAB function definition
function [centers,angle_degree] = findid_v1(filename)
, there are two output arguments:centers
andangle_degree
. Therefore, when calling this function from C#, you specify2
as the first argument to indicate that you expect two outputs from the function.The
MWArray[] result
will then be an array ofMWArray
objects, whereresult[0]
corresponds tocenters
andresult[1]
corresponds toangle_degree
. This allows you to access and use these results in your C# code.
3.2.2 MWArray的解释和使用:
3.2.2.1 首先,务必仔细处理好改通用动态库的版本和你的MT一致
我是直接在MT的安装位置找到的,
比如:
D:\Program Files\Polyspace\R2019b\toolbox\dotnetbuilder\bin\win64\v4.0\
3.2.2.2 解释一下:
MWNumericArray centers = (MWNumericArray)result[0];
double[,] centersData = (double[,])centers.ToArray(MWArrayComponent.Real);
MWNumericArray angle_degree = (MWNumericArray)result[1];
double[,] angle_degreeData =
MWNumericArray centers = (MWNumericArray)result[0];
出来是第一个MT输出参数,一个二维数组,centersdouble[,] centersData = (double[,])centers.ToArray(MWArrayComponent.Real);
双精度格式转化
3.2.2.3 转为最终能用的格式:
- 化为整形:
int angledata = Convert.ToInt32(Math.Truncate(angle_degreeData[0, 0]));
- 化为双精度:
double angledata2 =Math.Truncate(angle_degreeData[0, 0]);
4 如何显示出Matlab里面的图像数据:
【案,有两种从MT传递到C#的方法,1 通过内存的变量数组,2通过本地暂存的图片】
方法一,好处是,本地不留图片,问题是,会占用大量的内存,影响系统效率
方法二,保存为本地暂存图片,本文详述后一种方法:
方法二,保存为图像:
% Display the grayscale image
figure; imshow(grayImage);
MT 源程序展示为figure,改为:
imwrite(grayImage, 'grayImage.png');
这里,grayimage,就是个暂存的图片,他存哪里呢?
如果,我们是C#调用的话,比如,我的C#的工程设定 为:
那么,暂存的图片,会放在:
笔者项目地址\bin\x64\Debug
遇到的问题:
文件再次打开的问题:
解决Matlab,文件再次打开报错的问题:
1 笔者先增加一个表征图像情况的输出参数:
2 MATLAB 增加判错的代码 :
% check the right to write
if newimage == 0
fid = fopen('grayImage.png', 'w');
if fid == -1
disp('Error: Unable to write to file grayImage.png.');
newimage = 1;
else
% Write to file
imwrite(grayImage, 'grayImage.png');
fclose(fid);
newimage = 0;
end
end
if newimage == 1
fid2 = fopen('grayImage_1.png', 'w');
if fid2 == -1
disp('Error: Unable to write to file grayImage_1.png.');
newimage = 0;
else
% Write to file
imwrite(grayImage, 'grayImage_1.png');
fclose(fid2);
newimage = 1;
end
end
【案,这段代码,笔者每次写暂存图像文件之前,都尝试打开这个文件,如果,这个文件目前不能被开,那么是被占用了,我们存到另外一个暂存文件,并以此反复,这样,
C#中,即使不小心打开两次,一般都没有问题,这样MT的处理的图像就可以在C#中,你在利用C#的图形工具打开即可】
// show the pictures
if (fileopenFlag == 0)
{
pictureBox1.Image = Image.FromFile("grayImage.png");
}
else
{
pictureBox1.Image = Image.FromFile("grayImage_1.png");
}
// Get the Graphics object from the Reading image
Graphics graphics = Graphics.FromImage(pictureBox1.Image);
下面,这段C#的代码,就是配合MT来对暂存的图片进行展示的。