开发运行环境说明:
win7 x64、matlab2016、均值滤波、m文件、转成vs2013 cpp dll、cpp调用实例。
qt5.5.1 64位。vs cpp工程为64位。因为matlab2016只提供m文件对应的64位的dll。
开发过程中遇到的坑:
1、网上找的matlab均值滤波m文件是有问题的。问题在于它假定输入的3*1的矩阵,注释也不清楚,窗口大小用n表示,数据用array表示,我一开始一直以为n是标识array的size。
为了解决这个坑,只好自己学习matlab的m语言语法(学了一点,够解决当前问题即可)。均值滤波也是从零开始学(其实蛮简单)。
2、自己写的均值滤波m文件,在matlab中调试运行,结果是正常的浮点数。m转成dll后,通过cpp调用获得的结果却是整形值。
原因查了很久,最终定位到:matlab转m为cpp dll存在一个默认类型问题。感觉这算matlab的一个bug。
解决办法:m代码中将除数、被除数强行转换为double再相除。其实哪些该强转,哪些可以不强转,我也不清楚,一律强转后,输出正常了,也就未详细测试了。
对1*n矩阵进行均值滤波的m文件如下:
function [OutResult,error_msg] =junzhi(double_vec,win_size)
OutResult=double(double_vec); %转换后的数据赋给OutResult
error_msg='null'; %默认错误信息为null
if (win_size<3)
error_msg='error:win_size < 3.';
return; %窗口值3、5、7...
end
[row_count, col_count]=size(double_vec); %获取矩阵大小,row_count一般为1。win_size窗口大小一般为奇数3、5、7
if (row_count > 1)
error_msg='error:row_count > 1.';
return; %只处理一维浮点数
end
if (col_count < win_size)
error_msg='error:col_count < win_size.';
return; %浮点数总数不得小于窗口大小
end
%
double_vec1=double(double_vec); %数据类型转换
win_half=(win_size-1)/2; %窗口半径
d_win_size = double(win_size); %用于double除法,用win_size除可能会得到整数
d_win_half = double(win_half); %用于double除法
%return; %直接返回可以获得浮点数返回值。说明后面如果返回整数值,原因是处理过程中类型转换出了问题。
for j=1:win_half %数组最前面的数,计算均值
c=double_vec1(1,1:j+win_half); %在double_vec1中取窗口大小的块赋给c
s=double(sum(c)); %取一行的和,转换为double,否则可能为整数
OutResult(1,j)=s/double(j+d_win_half); %将模板各元素的均值赋给模板中心位置的元素
end
for j=(1+win_half):(col_count-win_half) %数组中间的数,计算均值
c=double_vec1(1,j-win_half:j+win_half); %在double_vec1中取窗口大小的块赋给c
s=double(sum(c)); %取一行的和,转换为double,否则可能为整数
OutResult(1,j)=s/d_win_size; %将模板各元素的均值赋给模板中心位置的元素
end
for j=(col_count-win_half+1):(col_count) %数组最后面的数,计算均值
c=double_vec1(1,j-win_half:col_count); %在double_vec1中取窗口大小的块赋给c
s=double(sum(c)); %取一行的和,转换为double,否则可能为整数
OutResult(1,j)=s/double(col_count-j+win_half+1); %将模板各元素的均值赋给模板中心位置的元素
end
实际上cpp中1 * n矩阵就是double的vector。我这个不针对图像,所以没有3 * 3这种窗口,窗口是一维数轴上的窗口。
matlab转换为cpp dll后,h文件中关键接口如下:
extern LIB_junzhi_CPP_API void MW_CALL_CONV junzhi(int nargout, mwArray& OutResult, mwArray& error_msg, const mwArray& double_vec, const mwArray& win_size);
cpp调用代码:
bool data_process::junzhi_m(const int iOut, std::string &error_msg,
double_vec &dVecOut, double_vec &dVecIn, const int iWinSize)
{
//junzhi(int nargout, mwArray& OutResult, mwArray& error_msg, const mwArray& double_vec, const mwArray& win_size);
if (!initialize_junzhi())
{
return false;
}
//iOut一般为1。此处加了error_msg,所以为2.
dVecOut.resize(dVecIn.size());//dVecOut输出参数与输入大小一样
//dVecIn输入参数一般为浮点数数组
//iWinSize均值滤波的窗口大小,一般为奇数3、5、7...
try
{
mwArray out_ptr(1, dVecOut.size(), mxDOUBLE_CLASS, mxREAL);
out_ptr.SetData(&(dVecOut[0]), dVecOut.size());//输出数据缓存赋值
//
mwArray in_ptr(1, dVecIn.size(), mxDOUBLE_CLASS, mxREAL);
in_ptr.SetData(&(dVecIn[0]), dVecIn.size());//输入数据缓存赋值
//
mwArray win_size_ptr(1, 1, mxINT32_CLASS, mxREAL);
int data1[1] = { iWinSize };//均值窗口大小
win_size_ptr.SetData(data1, 1);//窗口大小参数赋值
//
char *pcT = NULL;
mwArray error_msg_ptr(pcT);//错误信息
//
::junzhi(iOut, out_ptr, error_msg_ptr, in_ptr, win_size_ptr);//进入M dll处理
out_ptr.GetData((mxDouble*)(&(dVecOut[0])), dVecOut.size());//取出返回值,浮点数都变为整数了
error_msg = error_msg_ptr.ToString();//错误信息,null表示无错误
if (error_msg != "null")
{
return false;
}
}
catch (std::exception &e)
{
GuiLog("[ERROR]data_process::junzhi_m error.");
GuiLog(CodePageInfor::LocalToUtf8(e.what()));
return false;
}
return true;
}
初始化函数initialize_junzhi相关的环境初始化和终止:
bool data_process::initialize_junzhi()
{
if (!initialize_matlab())
{
return false;
}
if (!b_init_junzhi)
{
GuiLog("junzhiInitialize ......");
if (!junzhiInitialize())
{
GuiLog("junzhiInitialize error.");
return false;
}
else
{
GuiLog("junzhiInitialize ok.");
}
b_init_junzhi = true;
}
return true;
}
bool data_process::initialize_matlab()
{
if (!b_init_matlab)
{
if (!mclmcrInitialize())
{
GuiLog("mclmcrInitialize error.");
return false;
}
else
{
GuiLog("mclmcrInitialize ok.");
}
if (!mclInitializeApplication(NULL, 0))
{
GuiLog("mclInitializeApplication error.");
return false;
}
else
{
GuiLog("mclInitializeApplication ok.");
}
b_init_matlab = true;
}
return true;
}
bool data_process::terminate_matlab()
{
terminate_junzhi();
if (b_init_matlab)
{
if (!mclTerminateApplication())
{
GuiLog("mclTerminateApplication error.");
return false;
}
else
{
GuiLog("mclTerminateApplication ok.");
}
b_init_matlab = false;
}
return true;
}
bool data_process::terminate_junzhi()
{
if (b_init_junzhi)
{
junzhiTerminate();
b_init_junzhi = false;
}
return true;
}
junzhi_m的调用代码:
typedef std::vector<double> double_vec;
double_vec &vOut = get_mem_data_after()->val_vec_;
double_vec &vIn = get_mem_data_original()->val_vec_;
language_type lan_t = p_process->get_lan_type();
if (kLT_M == lan_t)
{
std::string error_msg;
bool b = data_process::junzhi_m(2, error_msg, vOut, vIn, p_process->get_win_size());
if (b)
{
GuiLog("data_process::junzhi_m ok.");
}
else
{
GuiLog("[ERROR]data_process::junzhi_m error:" + error_msg);
}
}
c语言版本的均值滤波代码:
bool data_process::junzhi_c(double_vec &dVecOut, double_vec &dVecIn, const int iWinSize)
{
//窗口大小iWinSize一般为奇数,3、5、7...
//假定窗口为奇数x,当前数据下标i,那么最左边元素下标i-(x-1)/2,最右边元素下标i+(x-1)/2
//下标i处理后的值由元素[i-(x-1)/2,i+(x-1)/2]总和除以x。越界下标不统计。
//窗口为3时,0下标由0、1求平均值,1下标由0、1、2求平均值...最后一个由倒数2个求。
//窗口大小0无效、1就是不处理、2不处理、3有效、4与3结果一致
int half_win = (iWinSize - 1) / 2;//窗口半径大小,取整
if (half_win <= 0)
{
dVecOut = dVecIn;
return true;
}
int iInSize = dVecIn.size();
dVecOut.resize(iInSize);
for (int i = 0; i < iInSize; ++i)
{
int iLeft = i - half_win;//窗口左值
int iRight = i + half_win;//窗口右值
double dSum = 0;//累加值
int iSumCount = 0;//累加个数统计
for (int j = iLeft; j <= iRight; ++j)
{
if (j >= 0 && j < iInSize)//窗口内有效下标
{
dSum += dVecIn[j];
++iSumCount;//实际累加的个数
}
}
dVecOut[i] = dSum / iSumCount;//均值
}
return true;
}
c语言版本的调用代码:
if (kLT_C == lan_t)
{
bool b = data_process::junzhi_c(vOut, vIn, p_process->get_win_size());
if (b)
{
GuiLog("data_process::junzhi_c ok.");
}
else
{
GuiLog("[ERROR]data_process::junzhi_c error.");
}
}