Matlab测试C函数模块

来源:互联网 发布:萧山网络问政平台临浦 编辑:程序博客网 时间:2024/06/05 03:51

 Matlab测试C函数模块

matlab具有强大的数据处理和数据可视化能力,对于一些已经基本确定的c函数模块(不需要再单步调试找bug),又需要对数据进行一定分析处理(作图,拟合等)才能确定c函数模块的一些性能,那就合适使用matlab来测试c函数模块了。

1.1 软件环境

Windows 7 Service Pack 1
Matlab版本2016a
VS版本VS professional 2015

其他版本的软件差别不大。

1.2具体步骤

1.2.1 matlab配置C编译器

Matlab命令行窗口里键入mex -setup,选择你要编译C++的编译器。
第一次使用需要配置编译器,可能不同版本有偏差,按照提示配置就好。配置好了后会提示(在本文的环境下提示如下):
MEX 配置为使用 'Microsoft Visual C++ 2015 Professional (C)' 以进行 C 语言编译。

1.2.2 编写mex函数

mex文件是由matlab生成,其源文件是.c/.cpp文件。因此需要先将代码写成.c/.cpp文件。这里需要注意的是,mex源文件以C/C++语言格式书写(数组下标自0起,如果是纯C程序,还要记得在函数开始的时候声明所有变量),但是需要留出matlab这边的接口,即:
(1)必须#include “mex.h”  
#include “mex.h” 
(2)主函数格式固定,作为接口,主函数内部可以调用其他的C函数:
void mexFunction(int nlts, mxArray *plhs[],
intnrhs, const mxArray * prhs[]) 
{  


这里的参数包括:
nlts 输出参数的个数;
plhs 输出参数的指针;
nrhs 输入参数的个数;
prhs 出入参数的指针;
(3)主函数的参数使用
获取指针:
p = mxGetPr(prhs[0]);
获取矩阵行数:
N = mxGetN(prhs[0]);  
为输出矩阵分配空间:
plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL);//plhs[0]为m×n的矩阵  


需要说明的是,Matlab提供的API中,函数前缀有mex-和mx-两种。带mx-前缀的大多是对mxArray数据进行操作的函数,如mxIsDouble,mxCreateDoubleMatrix等等。而带mex前缀的则大多是与Matlab环境进行交互的函数,如mexPrintf,mexErrMsgTxt等等。了解了这一点,对在Apiref.pdf中查找所需的函数很有帮助。
需要注意的是,matlab矩阵数据的存储数据是“从上到下,从左到右”,即matlab里的A(i, j)在mex源文件里为A[ m * (j - 1) + i - 1]。这点需要留意。

1.2.3 VC调试(非必须)

matlab无法调试mex的源文件,只能检测语法错误(在编译的时候检查),但是对于数组越界这类内存泄露问题,matlab无法提供检查,而是强退(WTF?!)。因此最好现在VS下调试通过再到matlab下编译。在VS下编译mex源文件需要添加头文件及附加依赖项(类似于OpenCV的配置):
(1)属性->C++目录->包含目录,加入MATLAB安装目录下的\extern\include 路径。
(2)属性->C++目录->库目录,加入MATLAB的\extern\lib\win64\microsoft 路径。
(3)属性->链接器->输入->附加依赖项,添加libmx.liblibeng.lib libmat.lib libmex.lib四个lib文件。

1.2.4 matlab编译mex文件

 编辑好mex源文件后,在matlab下输入:
mex 文件名(包含后缀),即可生成.mex(.mexw64)文件。这是就可以在matlab下直接调用C函数了,函数名就是文件名。


1.3 示例1:c语言与matlab单值传递


1.3.1 编写c实现文件和头文件

C函数add实现两个数的求和,在examle.c文件中定义如下


/*example.c*/
/*Sum of two numbers*/
double add(doublea, doubleb)
{
returna + b;
}
建立一个头文件myfunction.h中声明函数
/*mmyfunction.h*/
#ifndef MYFUNCTION_H_
#defineMYFUNCTION_H_
/*********************************
功能:计算两个数的和
参数:
a,第一个加数
b,第二个加数
返回值:
a,b的和。
*********************************/
#endif

1.3.2 编写mexFunction函数

/*test_add.c*/
#include"mex.h"
#include"myfunction.h"
void mexFunction( intnlhs, mxArray *plhs[],
intnrhs, constmxArray *prhs[] )
{
double a=0;
double b=0;
double *c;
/*Allocate memory for output,important!*/
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
/*get input value*/
    a=mxGetScalar(prhs[0]);
    b=mxGetScalar(prhs[1]);
/*get the adress of output plhs[0],and then can change the value
with c functions*/
c=mxGetPr(plhs[0]);
    *c=add(a,b);
}

1.3.3 编译运行

(1)编译
>> mex test_add.c example.c
生成名为test_add.mexw64的dll库,新版本的matlab生成的dll以mexwin64或mexwin32为后缀名。
可以看到,mexFunction是没返回值的,它不是通过返回值把结果传回Matlab的,而是通过对参数plhs的赋值。mexFunction的四个参数皆是说明Matlab调用MEX文件时的具体信息,如这样调用函数时(注意,函数名称与生成的.mexwin64文件同名):
(2)运行
>> c=test_add(1.2,2.5)
c =
3.7000

1.4 示例2:C语言与matlab二维数组(矩阵)专递

1.4.1 写好mexFunction接口

Matlab与C语言矩阵在内存中存储最大的区别是,所有Matlab阵列,不论其维数的大小在内存中,都是按照一维的方式存储,且在内存中是按照列的方式进行存储的。例如下面图1的2行5列的字符阵列,在内存中存储方式和线性索引表如图2所示。本文档主要使用Matlab来测试c模块,不应该对待测试的C函数有任何更改,matlab里使用m代码也不必考虑c语言的矩阵存储方式,因此对于matlab与c语言存在的一些数据处理的区别,必须在mexFunction函数里进行数据转换,以便可以专注于编写C程序和使用m程序处理测试结果。


 

图12行5列的字符阵列


 
图2 Matlab存储方式及其线性索引示意图


下面示例实现功能:在matlab定义一个矩阵,调用c程序对矩阵每个元素加上一个数x,然后返回矩阵到matlab。

1.4.2 编写c实现文件和头文件

/*example.c*/
void addx(double *a, double *b, size_trow, size_tcolumn, doublex)
{
for (size_t i = 0; i <row; i++)
{
for (size_t j=0; j <column; j++)
*(b + i*column + j) = *(a + i*column + j) + x;
}
}
头文件:
/*mmyfunction.h*/
#ifndef MYFUNCTION_H_
#defineMYFUNCTION_H_
void addx(double *a, double *b, size_trow, size_tcolumn, doublex);
#endif

1.4.3 编写mexFunction函数

注意matlab输入的矩阵要在mexFunction转换成c语言形式后,再传入c函数接口(这里是addx())。C函数接口传回来的矩阵,也要转换为matlab的形式,以便在matlab里使用。
/*test_addx.c*/
#include"mex.h"
#include"myfunction.h"
void mexFunction(intnlhs, mxArray *plhs[],
intnrhs, constmxArray *prhs[])
{
double *a;
double *b;
double x;


/*Get the address of the input matrix*/
a = mxGetPr(prhs[0]);
size_t input_row = mxGetM(prhs[0]);//get input_row of metrix
size_t input_column = mxGetN(prhs[0]);//get input_column of metrix
mexPrintf("input_row=%d,input_column=%d\n", input_row, input_column);
double *c = (double *)malloc(input_row*input_column * sizeof(double));


/*The form of MATLAB is translated into C language form*/
for (size_t i = 0; i < input_row; i++)
{
for (size_t j = 0; j < input_column; j++)
{
*(c + i*input_column + j) = *(a + i + j*input_row);
}
}
/*Output two-dimensional array in C form*/
mexPrintf("输入参数:\n");
for (size_t i = 0; i < input_row; i++)
{
mexPrintf("\n ");
for (size_t j = 0; j < input_column; j++)
mexPrintf("%f  ", *(c + i*input_column + j));
}


/*Get the address of the output matrix*/
    b=mxGetPr(prhs[1]);
x = mxGetScalar(prhs[2]);
size_t output_row = input_row;//get output_row of metrix
size_t output_column = input_column;//get output_column of metrix


/*Allocate memory for output*/
plhs[0] = mxCreateDoubleMatrix((mwSize)output_row, (mwSize)output_column, mxREAL);


/*get the adress of output plhs[0],and then can change the value
with c functions*/
    b=mxGetPr(plhs[0]);


double *d = (double *)malloc(output_row*output_column * sizeof(double));
    addx(c,d,input_row,input_column,x);

/*The form of C language  is translated into MATLAB*/
mexPrintf("\n输出参数:\n");
for (size_t i = 0; i < output_row; i++)
{
mexPrintf("\n");
for (size_t j = 0; j < output_column; j++)
{
*(b+ i+ j*output_row) = *(d + i*output_column + j);
mexPrintf("%f ", *(b + i + j*output_row));
}
}
free(c);
free(d);
}


1.4.4 编译运行

(1)编译
>> mex test_addx.c example.c
(2)运行,建立一下test.m文件,调用addx函数接口
%test.m
a=[1,2,3,4,5;
    6,7,8,9,10;
    11,12,13,14,15]
b=zeros(3,5);
x=0.5;
c=test_addx(a,b,x)


>> test


a =


     1     2     3     4     5
     6     7     8     9    10
    11    12    13    14    15


input_row=3,input_column=5
输入参数:


 1.000000  2.000000  3.000000  4.000000  5.000000  
 6.000000  7.000000  8.000000  9.000000  10.000000  
 11.000000  12.000000  13.000000  14.000000  15.000000  
输出参数:


1.500000 2.500000 3.500000 4.500000 5.500000 
6.500000 7.500000 8.500000 9.500000 10.500000 
11.500000 12.500000 13.500000 14.500000 15.500000 
c =


    1.5000    2.5000    3.5000    4.5000    5.5000
    6.5000    7.5000    8.5000    9.5000   10.5000
   11.5000   12.5000   13.5000   14.5000   15.5000


1.5 示例3:C语言与matlab结构体传递

这需要把mxArray数据类型与结构体进行相互转换,较为复杂。每次测试的c模块的结构体往往不同,那么必须重新编写mxArray数据类型与结构体转换的函数模块。因此这里使用的方法是,不直接在matlab与c函数间进行数据传递,而是把数据按照一定格式写入txt文件,然后再进行读取。
关于mxArray与结构体的转换可以参考这个博客:VC与Matlab混合编程(http://www.cnblogs.com/xpvincent/archive/2013/02/05/2893046.html)


1.6 小结

1.6.1 单值传递关键点

matlab ->c   a = mxGetScalar(prhs[0]);//该函数获取matlab传递过来的数值;
c ->matlab   有3步:
(1)plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);//为输出创建存储空间
(2) c = mxGetPr(plhs[0]);//获取返回plhs[0]的数据地址,其后可以修改y的值就可以返回了;
(3) *c=add(a,b);//调用C函数,把返回值赋给输出,传给matlab

1.6.2 二维数组(矩阵)传递关键点

在单值传递的基础上增加以下2步
(1)matlab ->c,把输入矩阵转换为按行存储,再传递给C函数模块;
(2)c->matlab把C函数模块的输出矩阵转换为按行存储,再传递给Matlab。

1.6.3 结构体的传递

(1)可以通过编写mxArray和c结构体相互转换的函数来实现,需要熟练掌握对mxArray数据的操作。
(2)把输入和输出数据写入txt文件来传递,本文采用此方法。
原创粉丝点击