C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)

MATLAB具有很高的计算性能,在该平台上很多算法易于实现,而有时需要使用C++调用Matlab编写的程序,可以将该函数编译成dll库文件,之后在C++中对其进行调用。

本文详细讲解在Matlab中将函数编译成库文件,并在C++中进行调用的方法。

实验环境

  • 操作系统:windows10
  • C++ IDE:Visual Studio 2015
  • Matlab版本:Matlab 2016b

Matlab编译

第一步:在matlab的命令行窗口中输入mex -setup,会出现以下界面。
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第1张图片
第二步:点击界面的“mex -setup C++”,会出现如下提示。
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第2张图片
以上两步说明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进行编译,这个过程可能需要几分钟。编译完成后的命令行显示如下信息:
这里写图片描述
编译生成的文件如下:
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第3张图片

需要用到的文件只有四个:addtest.ctf,addtest.dll,addtest.lib,addtest.h。

配置VS2015

打开VS2015,新建一个“win32控制台应用项目”,例如命名为test。打开项目“属性”,进行以下配置。

注意:测试环境使用的是64位操作系统,因此务必在x64环境下进行以下配置。

第一步:VC++目录
假设Matlab安装目录为G:\Matlab2016b,这个根据本机安装目录自行调整。

  • 包含目录:G:\Matlab2016b\extern\include
  • 库目录:G:\Matlab2016b\extern\lib\win64\microsoft

第二步:链接器->输入->附加依赖项,添加以下库
mclmcrrt.lib,libmat.lib,libmx.lib

第三步:配置系统变量
“我的电脑”右键,打开“属性”;选择“高级系统设置”;找到系统属性页面的“高级”选项,右下方“环境变量”;页面中下方是系统变量,将Path设置为G:\Matlab2016b\bin\win64

进行完以上配置后,将电脑进行重启。

第四步:重新打开VS2015的项目,将Matlab生成的四个文件放入该项目所在文件夹下,并将这四个文件添加到项目中。

至此完成VS的配置。

使用C++调用matlab的函数

在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;
}

运行成功的输出为:
这里写图片描述
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第4张图片

上面是命令行的输出,下面是matlab中绘图函数的输出。

可能存在的问题

1,上面的结果是在顺利的情况下得到的,不同的环境可能会出现不同的问题,其他问题请自寻查询,这里提一个卡了很久的问题。

不少人都遇到使用C++进行调用时,addtestInitialize()这一步初始化出现问题,报错是内存出错,程序中断,还会抛出以下N行报错信息。
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第5张图片
如果是这个问题,经过多方查询,暂时没有找到原理性的解释和本质的解决方案,但是可以在程序中断时选择继续执行,不管这些报错信息,程序仍然可以继续执行下去,并最终得到正确的结果。在VS中设置不提示该错误,下次运行时就不会弹出中断,不过报错信息依然存在,但是不影响结果。

2,在VS中运行时可能提示没有安装一些matlab相关的依赖文件,错误提示类似下面的描述。
C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第6张图片
这里需要安装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;
}

输出结果:

C++调用Matlab生成的DLL动态链接库进行混合编程(win10+VS2015+Matlab2016b)_第7张图片

你可能感兴趣的:(Matlab)