MATLAB具有很高的计算性能,在该平台上很多算法易于实现,而有时需要使用C++调用Matlab编写的程序,可以将该函数编译成dll库文件,之后在C++中对其进行调用。
本文详细讲解在Matlab中将函数编译成库文件,并在C++中进行调用的方法。
第一步:在matlab的命令行窗口中输入mex -setup,会出现以下界面。
第二步:点击界面的“mex -setup C++”,会出现如下提示。
以上两步说明Matlab的编译环境可行,并且是使用VS2015进行编译。不同版本的matlab使用的编译器可能不同,请确保matlab配置的编译器与之后使用的C++环境保持一致。
第三步:在Matlab中创建一个函数。这里我们采用最简单的求和函数,输入两个参数a和b,得到它们的和c,并且用图形绘制出来,matlab代码如下:
function c = myadd(a,b)
c = a+b;
stem(c);
第四步:对matlab的函数进行编译。注意:这里只能对matlab的function进行编译,并不能对一般的.m脚本进行编译。我们采用mcc的方式进行编译,命令如下:
mcc -W cpplib:addtest -T link:lib myadd.m -C
解释:
-W是控制编译之后的封装格式;
cpplib是指编译成C++的lib,如果需要编译成C的lib,就用lib即可,去掉cpp;
cpplib后面的是需要生成文件的文件名,是自己取的,我们取名为addtest;
-T表示目标,link:lib表示要连接到一个库文件的目标,目标的名字即是.m函数的名字。
其他具体含义可以通过mcc –help命令查看,注意参数的大小写。
第五步:等待matlab进行编译,这个过程可能需要几分钟。编译完成后的命令行显示如下信息:
编译生成的文件如下:
需要用到的文件只有四个:addtest.ctf,addtest.dll,addtest.lib,addtest.h。
打开VS2015,新建一个“win32控制台应用项目”,例如命名为test。打开项目“属性”,进行以下配置。
注意:测试环境使用的是64位操作系统,因此务必在x64环境下进行以下配置。
第一步:VC++目录
假设Matlab安装目录为G:\Matlab2016b,这个根据本机安装目录自行调整。
第二步:链接器->输入->附加依赖项,添加以下库
mclmcrrt.lib,libmat.lib,libmx.lib
第三步:配置系统变量
“我的电脑”右键,打开“属性”;选择“高级系统设置”;找到系统属性页面的“高级”选项,右下方“环境变量”;页面中下方是系统变量,将Path设置为G:\Matlab2016b\bin\win64
进行完以上配置后,将电脑进行重启。
第四步:重新打开VS2015的项目,将Matlab生成的四个文件放入该项目所在文件夹下,并将这四个文件添加到项目中。
至此完成VS的配置。
在VS中新建一个cpp文件,作为main函数,提供如下测试程序:
#include "addtest.h"
using namespace std;
int main()
{
bool isOk = 0;
if (!mclInitializeApplication(NULL, 0))
{
cout << "Could not initialize the application.\n";
return -1;
}
cout << "isOk = " << isOk << endl;
isOk = addtestInitialize();
cout << "isOk = " << isOk << endl;
mwArray a(1, 1, mxDOUBLE_CLASS);
mwArray b(1, 1, mxDOUBLE_CLASS);
a(1, 1) = 1.8;
b(1, 1) = 2.9;
mwArray z(1, 1, mxDOUBLE_CLASS);
myadd(1, z, a, b);
cout << a << "+" << b << "=" << z << endl;
mclWaitForFiguresToDie(NULL);
addtestTerminate();
mclTerminateApplication();
return 0;
}
上面是命令行的输出,下面是matlab中绘图函数的输出。
1,上面的结果是在顺利的情况下得到的,不同的环境可能会出现不同的问题,其他问题请自寻查询,这里提一个卡了很久的问题。
不少人都遇到使用C++进行调用时,addtestInitialize()这一步初始化出现问题,报错是内存出错,程序中断,还会抛出以下N行报错信息。
如果是这个问题,经过多方查询,暂时没有找到原理性的解释和本质的解决方案,但是可以在程序中断时选择继续执行,不管这些报错信息,程序仍然可以继续执行下去,并最终得到正确的结果。在VS中设置不提示该错误,下次运行时就不会弹出中断,不过报错信息依然存在,但是不影响结果。
2,在VS中运行时可能提示没有安装一些matlab相关的依赖文件,错误提示类似下面的描述。
这里需要安装Matlab 2016b runtime,即matlab 2016b的运行环境。下面是matlab runtime下载地址,可根据自己matlab版本进行下载。
https://ww2.mathworks.cn/products/compiler/matlab-runtime.html
以上方法仅基于最简单的matlab函数,考虑到图像处理的需要,往往需要将图像作为参数输入,这里以一个最简单的例子做介绍。
设计一个最简单的显示图像的matlab函数。
function ImgShow(img)
imshow(img);
matlab编译:
mcc -W cpplib:imgshow -T link:lib ImgShow.m -C
VS配置过程同上,这里提供C++测试主函数。
#include "cv.h"
#include "highgui.h"
#include "imgshow.h"
#include // Gaussian Blur
#include // Basic OpenCV structures (cv::Mat, Scalar)
#include // OpenCV window I/O
using namespace std;
using namespace cv;
int main() {
bool isOk = 0;//判断动态库是否初始化成功
if (!mclInitializeApplication(NULL, 0))
{
cout << "Could not initialize the application.\n";
return -1;
}
cout << "isOk = " << isOk << endl;// 0
isOk = imgshowInitialize(); // 动态库初始化成功
cout << "isOk = " << isOk << endl;// 1
Mat disp_image = imread("lena.jpg", 1);//调用opencv读取图片
/*将opencv图像数据转为Matlab图像数据*/
//Mat数据类型转为mwArray
mwSize mdim[3] = { disp_image.rows,disp_image.cols,3 };
mwArray mdisp_image(3, mdim, mxDOUBLE_CLASS, mxREAL);
//C++输入转matlab 接口矩阵转化及像素归一化
for (int j = 0; j < disp_image.rows; ++j) {
for (int i = 0; i (j, i); //C++用向量存储像素值
double B = mp.val[0] * 1.0 / 255; //像素归一化到0-1之间
double G = mp.val[1] * 1.0 / 255;
double R = mp.val[2] * 1.0 / 255;
mdisp_image(j + 1, i + 1, 1) = R; //matlab中用三个面R,G,B存储像素值
mdisp_image(j + 1, i + 1, 2) = G; //C++图像矩阵像素值赋给matlab矩阵
mdisp_image(j + 1, i + 1, 3) = B;
}
}
ImgShow(mdisp_image); //调用matlab函数
mclWaitForFiguresToDie(NULL); //等待图像显示,不加此句无法显示图像
imgshowTerminate(); //关闭动态库
mclTerminateApplication();
return 0;
}
输出结果: