MATLAB调用C/C++进行混合编程

原创 2017年07月10日 22:35:31

1 引言


第一次接触Matlab和C/C++混合编程是在阅读BM3D代码时,那时候对.mexw32、.mexw64文件还不是太懂。后来了解到这是C/C++写的。目的有两个,其一是加快程序的运行,C/C++循环效率高、运行快,Matlab运行慢,擅长矩阵运算。混合编程,相当于取长补短。其二是防止算法的核心部分外泄,mex生成的源码不可以被用户看到,只能被调用。最近在做超分辨率项目时,发现MATLAB程序运行的太慢,每次都要25分钟左右,为了加速又重新看了一下混合编程的知识。主要参考了zouxy09(http://blog.csdn.net/zouxy09)和有来有去-CV(http://blog.csdn.net/shaoxiaohu1)两位大佬的博客,在此表示感谢。
本文的开发环境是VS2015 Professional+Matlab 2015b+opencv3.2.0,如果读者的开发环境不同的时候可能会有错误。

2 初出茅庐

我最初是按照zouxy09的博客进行混合编程,在这个过程中发现了很多的bug,最终顺利实现了两个代码。下面先讲解一个简单的代码。
我们需要先写一个C++代码(mexAdd.cpp),计算两个double型数字的和。
#include <iostream>
using namespace std;
double add(double x, double y)
{
    return x + y;
}
为了能够在Matlab中调用我们需要做以下几个修改:

(1)、在C++文件开头处添加头文件:#include”mex.h”
(2)、添加接口函数mexFunction()

void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
}

其中,nlhs:(number of left hand size parameters)代表了函数返回的变量个数,例如上面c = add(a, b);就只有一个返回参数c,所以nlhs就是1。
plhs:(pointer of left hand size parameters)代表了函数返回的指针数组。换句话说,它是一个数组,每个元素是个指针,每个指针指向一个数据类型为mxArray的返回参数。例如上面c = add(a, b);就只有一个返回参数c,所以该数组只有一个指针,plhs[0]指向的结果会赋值给c。
nlrs:(number of right hand size parameters)代表了函数输入的变量个数,例如上面c = add(a, b),它给c++代码传入了两个参数a和b,所以nrhs为2。
prhs:(pointer of right hand size parameters)和plhs类似,因为右手面有两个自变量,即该数组有两个指针,prhs[0]指向了a,prhs[1]指向了b。要注意prhs是const的指针数组,即不能改变其指向内容。
改完之后的mexAdd.cpp如下:


#include "opencv2/opencv.hpp"
#include "mex.h"

double add(double x, double y)
{
    return x + y;
}


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    double *a;
    double b, c;
    plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
    a = mxGetPr(plhs[0]);
    b = *(mxGetPr(prhs[0]));
    c = *(mxGetPr(prhs[1]));
    *a = add(b, c);
}

mxCreateDoubleMatrix函数,其返回指向刚建立的mxArray的指针,然后令plhs[0]指向它。接着令指针a指向plhs [0]所指向的mxArray的第一个元素(使用mxGetPr函数,返回指向mxArray的首元素的指针)
修改后编译过程如下:
这个过程主要就是用来选择C++程序的编译器,我这里用的是VS2015
这个过程主要就是用来选择C++程序的编译器,我这里用的是VS2015。
紧接着编译文件
紧接着就是编译目标文件。
这就是调用的过程
这就是调用的过程,这里我第一次弄的时候一直出错,就是因为我搞混了几个概念。调用的这个函数名称是你的cpp文件的名字,而不是cpp文件里面的具体函数名。

3 牛刀小试

这一部分主要是复制了 zouxy09的博文(其实上一部分也有,尴尬),后面我会写一部分我在运行这部分代码时出错的地方以及改正的方法。

上面我们针对的是处理标量的情况,也就是数a,b或者c。这节我们让它处理二维数组,也就是图像。为了验证,我们很傻瓜地完成以下功能:

[grayImage] =RGB2Gray(‘imageFile.jpeg’);

也就是将一个图像文件名,传递给c++的代码,然后c++代码将这个图像读入,再转成灰度图,然后返回给Matlab。而c++代码里面的图像读入和灰度转换的操作通过调用OpenCV的库函数来实现。是不是很傻瓜呢?因为Matlab已经有实现同样功能的函数了。对,没错,就是多此一举。但我们只是为了说明二维数组的传递过程,没有什么用意。不过,如果要计算两个图像的光流的话,Matlab可能就真正需要OpenCV的帮助了。
另外,因为cpp文件要链接OpenCV的库,所以为了统一或者规范编译工程,我写了一个make.m文件,它的功能类似于Makefile,实际上就实现了mex编译这个工程时候的编译规则。具体可以看后面的代码,然后就知道在里面做了什么了。
首先是RGB2Gray.cpp代码:


// Interface: convert an image to gray and return to Matlab
// Author : zouxy
// Date   : 2014-03-05
// HomePage : http://blog.csdn.net/zouxy09
// Email  : zouxy09@qq.com

#include "opencv2/opencv.hpp"
#include "mex.h"

using namespace cv;

/*******************************************************
Usage: [imageMatrix] = RGB2Gray('imageFile.jpeg');
Input: 
    a image file
OutPut: 
    a matrix of image which can be read by Matlab

**********************************************************/


void exit_with_help()
{
    mexPrintf(
    "Usage: [imageMatrix] = DenseTrack('imageFile.jpg');\n"
    );
}

static void fake_answer(mxArray *plhs[])
{
    plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL);
}

void RGB2Gray(char *filename, mxArray *plhs[])
{
    // read the image
    Mat image = imread(filename);
    if(image.empty()) {
        mexPrintf("can't open input file %s\n", filename);
        fake_answer(plhs);
        return;
    }

    // convert it to gray format
    Mat gray;
    if (image.channels() == 3)
        cvtColor(image, gray, CV_RGB2GRAY);
    else
        image.copyTo(gray);

    // convert the result to Matlab-supported format for returning
    int rows = gray.rows;
    int cols = gray.cols;
    plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);
    double *imgMat;
    imgMat = mxGetPr(plhs[0]);
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            *(imgMat + i + j * rows) = (double)gray.at<uchar>(i, j);

    return;
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if(nrhs == 1)
    {
        char filename[256];
        mxGetString(prhs[0], filename, mxGetN(prhs[0]) + 1);
        if(filename == NULL)
        {
            mexPrintf("Error: filename is NULL\n");
            exit_with_help();
            return;
        }

        RGB2Gray(filename, plhs);
    }
    else
    {
        exit_with_help();
        fake_answer(plhs);
        return;
    }
}
    和上面的相比,里面多了几个东西。第一个就是传入参数的测试,看看Matlab传入的参数是否存在错误,还包括了些异常处理。第二个就是帮助信息。第三个就是主要的实现函数了。只有OpenCV的读图像和灰度转换这里就不讲了,就是两个函数的调用。关键的地方还是如果把一个图像,也就是二维数组,传递给mexFunction的参数,让它返回给Matlab。实际上,我们只要清楚一点:

    plhs[0] = mxCreateDoubleMatrix(2, 3,mxREAL);

    这个函数建立的矩阵的指针plhs[0]是按照列的方式来存储的。假设imgMat是它的指针,那么*(imgMat+1)就是矩阵元素[1, 0],*(imgMat+2)就是矩阵元素[0, 1],*(imgMat+4)就是矩阵元素[0, 2]。上面的代码就是按照这个方式,将图像gray中像素值赋值给参数plhs[0]相应的位置(实际上也许可以直接内存拷贝,但因为里面是指针操作,涉及到局部变量gray的销毁问题,所以就简单的用上面的笨但稳当的方式来实现了)。

   好了,下面是make.m文件。里面需要获取你的电脑的系统版本是32还是64位的,来选择编译选项。然后添加OpenCV的相关配置。如果您需要使用使用,请修改成您的OpenCV的相关目录。然后给出一个需要编译的文件的列表。最后分析这个列表,加上编译选项,用mex来编译列表里面的所有文件。

%// This make.m is for MATLAB
%// Function: compile c++ files which rely on OpenCV for Matlab using mex
%// Author : zouxy
%// Date   : 2014-03-05
%// HomePage : http://blog.csdn.net/zouxy09
%// Email  : zouxy09@qq.com

%% Please modify your path of OpenCV
%% If your have any question, please contact Zou Xiaoyi

% Notice: first use "mex -setup" to choose your c/c++ compiler
clear all;

%-------------------------------------------------------------------
%% get the architecture of this computer
is_64bit = strcmp(computer,'MACI64') || strcmp(computer,'GLNXA64') || strcmp(computer,'PCWIN64');


%-------------------------------------------------------------------
%% the configuration of compiler
% You need to modify this configuration according to your own path of OpenCV
% Notice: if your system is 64bit, your OpenCV must be 64bit!
out_dir='./';
CPPFLAGS = ' -O -DNDEBUG -I.\ -ID:\OpenCV_64\include'; % your OpenCV "include" path
LDFLAGS = ' -LD:\OpenCV_64\lib';                       % your OpenCV "lib" path
LIBS = ' -lopencv_core240 -lopencv_highgui240 -lopencv_video240 -lopencv_imgproc240';
if is_64bit
    CPPFLAGS = [CPPFLAGS ' -largeArrayDims'];
end
%% add your files here!
compile_files = { 
    % the list of your code files which need to be compiled
    'RGB2Gray.cpp'
};


%-------------------------------------------------------------------
%% compiling...
for k = 1 : length(compile_files)
    str = compile_files{k};
    fprintf('compilation of: %s\n', str);
    str = [str ' -outdir ' out_dir CPPFLAGS LDFLAGS LIBS];
    args = regexp(str, '\s+', 'split');
    mex(args{:});
end

fprintf('Congratulations, compilation successful!!!\n');
  直接在Matlab中运行make.m。即可生成RGB2Gray.mexw64。然后在Matlab中运行:

  >> img = RGB2Gray(‘d:\test.jpg’);

  >> imshow(uint8(img));

  即可显示转换结果。

4 遇到的问题及改正方法

在运行灰度图程序时,我的MATLAB一直在报lik2019错误,最终定位是这里出现了问题:
这里写图片描述
就是在配置库的时候出现了问题,原来是我把lib库的位置写错了,opencv3.0 的库的配置应该是这样的。
这里写图片描述
其中opencv3.0以上版本只有opencv_world320和opencv_world320d两个(320是我的opencv版本号,这个自己要改),还有lib路径应该是带VC**的路径(如VS2015 64位就是VC14),我当时用的cmake的那个路径,明显错误了。
我也是看了这个才懂的。这里写图片描述
最后运行文件,这里写图片描述
得到女神灰度图片这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

C/C++与Matlab混合编程初探

Matlab 拥有丰富的功能,编程简单。不过,有些情况下,Matlab程序的执行速度比较慢。C/C++编译执行的程序速度比较快,编程难度上比Matlab要高一些。因此存在一种方案,就是使用Matlab...
  • bendanban
  • bendanban
  • 2014年07月15日 21:42
  • 27465

MATLAB与C/C++混合编程之MATLAB调用C程序

通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度。Matlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种。如果你在安装Matlab时...
  • sbtdkj1017
  • sbtdkj1017
  • 2007年11月25日 15:28
  • 33180

[Matlab] matlab与C/C++混合编程汇总

matlab 与外部程序的编程接口两大类:                一是 如何在matlab里调用其他语言写的代码。 (见例子:使用C-MEX技术,ActiveX技术)             ...
  • fonjames
  • fonjames
  • 2016年06月01日 15:37
  • 5909

MATLAB和C/C++混合编程实现图像处理(一)

MATLAB具有丰富的图像处理函数库,运算速度慢,特别是在多重循环的情况下,不适合直接应用于工程当中。如果能把MATLAB和另一种适合工程的编程语言结合到一起运用到数字图像处理领域,则会更加方便的进行...
  • geekmanong
  • geekmanong
  • 2015年11月13日 22:22
  • 2717

matlab与C/C++的混合编程

matlab比较擅长矩阵运算,而运算for的速度较慢,如果将matlab与C或C++进行混合编程,则可以提升程序运行速度....
  • zhj_matlab
  • zhj_matlab
  • 2016年10月30日 16:12
  • 1410

matlab和c++混合编程---方法和步骤

摘要:Matlab具有很强的数值计算和分析等能力,而C/C++是目前最为流行的高级程序设计语言,两者互补结合的混合编程在科学研究和工程实践中具有非常重要的意义。从Matlab调用C/C++代码及C/C...
  • yimiyangguang185
  • yimiyangguang185
  • 2016年09月22日 19:38
  • 4834

c++ matlab混合编程时mxArray 内存泄露问题

对于mxArray在创建完后若使用完一定要注意回收空间,不然会memory leak.如果是mxstruct,只要对其中的field进行了赋值操作,那么其原先的field一定要先进行memory的回收...
  • z6491679
  • z6491679
  • 2015年05月16日 15:09
  • 425

Matlab 和 C/C++混合编程

说明: 这篇博文是对下面两边博文进行总结和整理,以及补充。 blog: http://blog.csdn.net/jtop0/article/details/7657227 http://blog....
  • Work_Hard_SB
  • Work_Hard_SB
  • 2015年01月09日 20:38
  • 3649

通过matlab引擎在VS下进行实时C++混合编程

相较于通过mex编译mexw32/mexw64和直接加载dll等混编方式,使用matlab引擎最大的优势在于可以进行实时的数据传输,并且能够通过命令行最大限度地使用matlab的功能,充分发挥matl...
  • qq_32408771
  • qq_32408771
  • 2017年06月09日 19:25
  • 327

C++/Matlab混合编程(vs2010和matlabR2014a)

这几天在使用matlab/c++混编时,遇见很多问题,解决起来很痛苦,分享一些经验给大家,希望大家少走些弯路。 首先这里说下,不建议使用matlab2010a和vs2010来混合编程,因为我自己尝试...
  • r1254
  • r1254
  • 2016年09月19日 17:55
  • 5270
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MATLAB调用C/C++进行混合编程
举报原因:
原因补充:

(最多只允许输入30个字)