Matlab与C++混合编程(依赖OpenCV)

     之前在运行别人论文的代码的时候,经常有遇到Matlab与C++混合编程的影子。实际上就是通过Matlab的Mex工具将C++的代码编译成Matlab支持调用的可执行文件和函数接口。这样一方面可以在Matlab中利用已经编写好的函数,尽管这个函数是用C++编写的。实现了交流无国界,没有江山一统的谁,只有四海之内皆兄弟的豪气。另一方面,取C++所长补己之短。Matlab擅长矩阵运算,但对循环操作的效率不及C++来得高效,例如Hilbert矩阵的创建。所以对于具有大循环的运算,可以借C++之力来完成。

   

一、初级

   

   

mexAdd.cpp

1.  
2.  usingnamespacestd;  
3.  
4.  doubleadd(doublex, doubley)  
5.  
6.    returnx + y;  
7.




1、修改代码文件

1)添加头文件mex.h

   

#include"mex.h"

2)添加接口函数mexFunction()

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

     首先,这个函数是没有返回值的。它不是通过返回值把c++代码的计算结果传回Matlab的,而是通过对参数plhs的赋值。例如我们在Matlab中,调用这个add函数一般是这样:

     

     

     

       nlhs: 感觉是number of left hand size parameters,也就是Matlab调用语句左边的变量个数,实际上就是需要返回给Matlab的返回值变量有多少个。例如上面c = add(a, b);就只有一个返回参数c,所以nlhs就是1;

       plhs: 感觉是pointer of left hand size parameters,也就是函数返回参数的指针。但它是一个指针数组。换句话说,它是一个数组,每个元素是个指针,每个指针指向一个数据类型为mxArray的返回参数。例如上面c = add(a, b);就只有一个返回参数c,所以该数组只有一个指针,plhs[0]指向的结果会赋值给c。

       nrhs: 这个是number of right hand size parameters,也就是Matlab调用语句右边的变量个数。例如上面c = add(a, b),它给c++代码传入了两个参数a和b,所以nrhs为2;

       

     因为Matlab最基本的单元为array,无论是什么类型也好,如有doublearray、 cell array、struct array……所以a,b,c都是array,b = 1.1便是一个1x1的double array。而在C语言中,Matlab的array使用mxArray类型来表示。所以就不难明白为什么plhs和prhs都是指向mxArray类型的指针数组(参考资料[1])。

     那mexFunction函数的函数体要怎么写呢?怎么样通过这个接口函数将Matlab的参数和c++代码中的相对应的参数联系起来呢?我们先把这个代码全部展现出来。

     最后的mexAdd.cpp是这样:

mexAdd.cpp


1. "opencv2/opencv.hpp" 
2. "mex.h" 
3.  
4.  doubleadd(doublex, doubley)  
5.  
6.    returnx + y;  
7.  
8.  
9.   
10.  voidmexFunction(intnlhs, mxArray *plhs[], intnrhs, constmxArray *prhs[])  
11.  
12.    double*a;  
13.    doubleb, c;  
14.    plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);  
15.    a = mxGetPr(plhs[0]);  
16.    b = *(mxGetPr(prhs[0]));  
17.    c = *(mxGetPr(prhs[1]));  
18.    *a = add(b, c);  
19.


   

   

   

     实际上mexFunction是没有这么简单的,我们要对用户的输入自变量的个数和类型进行测试,以确保输入正确。如在add函数的例子中,用户输入char array便是一种错误了。

     从上面的讲述中我们总结出,MEX文件实现了一种接口,把C语言中的计算结果适当地返回给Matlab罢了。当我们已经有用C编写的大型程序时,大可不必在 Matlab里重写,只写个接口,做成MEX文件就成了。另外,在Matlab程序中的部分计算瓶颈(如循环),可通过MEX文件用C语言实现,以提高计算速度(参考资料[1])。

2、编译修改后的c++文件

     文件修改完后,我们需要将他编译,生成Matlab支持的可执行文件。这里需要的是Matlab自带的Mex工具。但在编译器,我们需要配置下这个工具,告诉它你要采用什么编译器来编译我们的c/c++代码。在Matlab中运行:

     >> mex -setup

     就会出现叫你选择一个默认的编译器。例如我这里是叫选择Matlab自带的Lcc或者我自己在电脑上安装的Microsoft Visual C++ 2010。一般都是选择后者。配置这个就可以编译了。编译也有以下几种情况:

>> mex XXX.cpp

>> mex X1.cpp X2.cpp X3.cpp %多个cpp文件,且有依赖。生成的库名字叫X1

>> mex -largeArrayDims X1.cpp %对64位系统,通过这个选项来指定使用处理大容量数组的API。因为Matlab与C++之间的接口是以32位系统作为标准的,这就导致了人们在处理大容量数据时没办法利用C和C++语言的速度优势。但对64位系统来说,系统资源一般都比32位系统要充足,所以指定该接口,让它对大容量数据处理更游刃有余。

     还有一些编译选项,和gcc一样。例如-I指定额外需要include的目录,-L指定额外需要连接的库的目录,-l指定额外需要链接的库等。

     对于我们的程序就简单了。在MATLAB命令窗口输入以下命令:mexmexAdd.cpp,即可编译成功。编译成功后,在同文件夹下会出现一个同名的,但后缀是mexw32(32位的系统)或者mexw64(64位的系统)的文件,例如mexAdd.mexw32。然后在Matlab中就可以直接调用它来运算了:

     >> ans = mexAdd(0.5, 0.8);

二、进阶

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

     

     也就是将一个图像文件名,传递给c++的代码,然后c++代码将这个图像读入,再转成灰度图,然后返回给Matlab。而c++代码里面的图像读入和灰度转换的操作通过调用OpenCV的库函数来实现。是不是很傻瓜呢?因为Matlab已经有实现同样功能的函数了。对,没错,就是多此一举。但我们只是为了说明二维数组的传递过程,没有什么用意。不过,如果要计算两个图像的光流的话,Matlab可能就真正需要OpenCV的帮助了。

     另外,因为cpp文件要链接OpenCV的库,所以为了统一或者规范编译工程,我写了一个make.m文件,它的功能类似于Makefile,实际上就实现了mex编译这个工程时候的编译规则。具体可以看后面的代码,然后就知道在里面做了什么了。

     首先是RGB2Gray.cpp代码:



1. Interface: convert an image to gray and return to Matlab 
2. Author : zouxy 
3. Date   : 2014-03-05 
4
6.  
7. "opencv2/opencv.hpp" 
8. "mex.h" 
9.  
10.  usingnamespacecv;  
11.  
12.  
13.  
14.  
15.  voidexit_with_help()  
16.  
17.    mexPrintf(  
18.    "Usage: [imageMatrix] = DenseTrack('imageFile.jpg');\n" 
19.    );  
20.  
21.  
22.  staticvoidfake_answer(mxArray *plhs[])  
23.  
24.    plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL);  
25.  
26.  
27.  voidRGB2Gray(char*filename, mxArray *plhs[])  
28.  
29.    // read the image 
30.    Mat image = imread(filename);  
31.    if(image.empty()) {  
32.        mexPrintf("can't open input file %s\n", filename);  
33.        fake_answer(plhs);  
34.        return;  
35.    }  
36.      
37.    // convert it to gray format 
38.    Mat gray;  
39.    if(image.channels() == 3)  
40.        cvtColor(image, gray, CV_RGB2GRAY);  
41.    else 
42.        image.copyTo(gray);  
43.      
44.    // convert the result to Matlab-supported format for returning 
45.    introws = gray.rows;  
46.    intcols = gray.cols;  
47.    plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);  
48.    double*imgMat;  
49.    imgMat = mxGetPr(plhs[0]);  
50.    for(inti = 0; i < rows; i++)  
51.        for(intj = 0; j < cols; j++)  
52.            *(imgMat + i + j * rows) = (double)gray.at(i, j);  
53.      
54.    return;  
55.  
56.  
57.  voidmexFunction(intnlhs, mxArray *plhs[], intnrhs, constmxArray *prhs[])  
58.  
59.    if(nrhs == 1)  
60.    {  
61.        charfilename[256];  
62.        mxGetString(prhs[0], filename, mxGetN(prhs[0]) + 1);  
63.        if(filename == NULL)  
64.        {  
65.            mexPrintf("Error: filename is NULL\n");  
66.            exit_with_help();  
67.            return;  
68.        }  
69.  
70.        RGB2Gray(filename, plhs);  
71.    }  
72.    else 
73.    {  
74.        exit_with_help();  
75.        fake_answer(plhs);  
76.        return;  
77.    }  
78.



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

     

     

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


    1.  %// This make.m is for MATLAB 
    2. // Function: compile c++ files which rely on OpenCV for Matlab using mex 
    3. // Author : zouxy 
    4. // Date   : 2014-03-05 
    5. // HomePage :  
    6. // Email  : zouxy09@qq.com 
    7.  
    8. Please modify your path of OpenCV  
    9. If your have any question, please contact Zou Xiaoyi  
    10.  
    11. Notice: first use "mex -setup"to choose your c/c++ compiler  
    12. all;  
    13.  
    14.  
    15. get the architecture of thiscomputer  
    16. = strcmp(computer,'MACI64') || strcmp(computer,'GLNXA64') || strcmp(computer,'PCWIN64');  
    17.  
    18.  
    19.  
    20. the configuration of compiler  
    21. You need to modify thisconfiguration according to your own path of OpenCV  
    22. Notice: ifyour system is 64bit, your OpenCV must be 64bit!  
    23. './';  
    24. = ' -O -DNDEBUG -I.\ -ID:\OpenCV_64\include'; % your OpenCV "include"path  
    25. = ' -LD:\OpenCV_64\lib';                       % your OpenCV "lib"path  
    26. = ' -lopencv_core240 -lopencv_highgui240 -lopencv_video240 -lopencv_imgproc240';  
    27.  ifis_64bit  
    28.    CPPFLAGS = [CPPFLAGS ' -largeArrayDims'];  
    29.  
    30. add your files here!  
    31. = {   
    32.    % the list of your code files which need to be compiled  
    33.    'RGB2Gray.cpp' 
    34.  
    35.  
    36.  
    37.  
    38. compiling...  
    39.  fork = 1 : length(compile_files)  
    40.    str = compile_files{k};  
    41.    fprintf('compilation of: %s\n', str);  
    42.    str = [str ' -outdir 'out_dir CPPFLAGS LDFLAGS LIBS];  
    43.    args = regexp(str, '\s+', 'split');  
    44.    mex(args{:});  
    45.  
    46.  
    47. 'Congratulations, compilation successful!!!\n');



    三、使用方法和结果

    1、编译

       

       

       

       


    matlab与opencv matlab与opencv的区别_c++代码

    注:以上Matlab的说明都是在你的cpp文件所在目录下。