关键词: 图像处理 Matlab 混合编程
1 引言
Matlab是美国Mathworks公司于20世纪80年代中期推出的一套数值计算软件,可以实现数值分析、矩阵运算、自动控制、信号处理和图像处理等若干领域的计算和图形显示功能。它不仅包含大量高度集成的函数可供直接调用以解决各种复杂的计算,而且提供了简洁的人机界面、丰富的图形用户界面GUI(Graphical User Interfaces)开发功能以及求解特定学科问题的求解工具箱ToolBox。与此同时编写程序Matlab所需时间往往能比使用其他编程语言缩短许多倍。自面世以来,Matlab在教学和科研等领域受到了广泛的重视和应用, 在解决复杂的数学计算和新算法的研究中具有极大的优势。
1.1 软件的特点归纳[4]
(1) 易学易用性
Matlab是一门编程语言,其语法规则和C语言大同小异,因而具有一般编程语言基础的用户很快就可以掌握。
(2) 代码短小高效
Matlab将不同数学分支的算法以函数的形式分类成库,使用时直接调用这些函数并赋予实际参数就可以解决问题,快速而准确。
(3) 计算功能强大
该软件具有强大的矩阵计算功能,除了一般的加、减、乘、除、转置和求逆运算外,还非常适合处理稀疏矩阵等特殊矩阵运算以及有限元等大型数值算法的编程。
(4) 可扩展性能
Matlab 最重要的特点是易于扩展,它允许用户自行编写指定功能的m文件,组成自己的工具箱。当前支持Matlab的商用ToolBox(工具箱)有数百种之多。 Matlab支持DDE和ActiveX自动化等机制,可以与同样支持该技术的应用程序接口。利用m文件或MEX文件可以实现也VB、VC等程序的进程内无缝集成;利用web服务器,可实现Matlab与网络程序的接口;利用API函数,还可以实现Matlab与硬件的接口。
1.2 Matlab自身所存在的某些不足也限制了它的应用范围
(1) Matlab是一种解释性语言,所以它的代码执行速度慢,这对于实时性要求较高的领域,如自动控制、信号处理等,其实时效率是较差的。
(2) Matlab程序不能脱离其环境运行,其功能只能在它本身所提供的平台上使用,因而不能被用于开发商用软件。
(3) Matlab程序可被直接看到源代码,因而算法和数据的保密性欠佳。
Visual C++(以下简称VC)是Microsoft公司推出的强大的可视化集成编程环境, 从底层软件到上层直接面向用户的软件都可以用VC开发, 而且强大的调试功能也为大型复杂软件的开发提供了高效的排错手段。使用VC开发的系统具有界面友好、代码效率高和执行速度快等一系列优点. 同时C++语言支持面向对象的程序设计, 而利用面向对象方法设计的软件具有良好的可重用性、可维护性和可扩充性等特点。它是目前综合性最高、最强大、也是最复杂的软件开发工具之一, 应用极为广泛。
因此实现VC与Matlab混合编程,使两者结合起来,协同工作,将大大减少编程的工作量、提高程序执行效率。利用VC这一优秀编程工具可以弥补Matlab在处理具体问题时的缺陷,利用Matlab的强大数值运算等功能则可以增强VC对信号处理、三维处理、自动控制等方面的能力。两者结合将会充分发挥各自的优势,必将提高软件开发效率,使开发的软件具有更高的性能,更广阔的应用领域。
2 VC++与Matlab的接口技术简?
2.1 在VC环境中调用Matlab程序主要方法
为将Matlab的强大功能融入到各种应用程序中,通过高级语言编译器将Matlab的m文件编译为二进制代码已成为研究热点。
(1) 在VC中启用MATLAB EN GINE(引擎),采用客户机/服务器(Client/Server)的计算模式。这种方式需要Matlab在后台运行,离不开Matlab环境。但是它可以调用Matlab工具箱函数和图形函数。
(2) 利用MATLAB COMPIL ER(编译器),将Matlab 的函数编译成脱离Matlab环境的可执行程序(.exe文件), 在VC中调用。
(3) 利用Matlab的数学函数和图形库。6.1版本的Matlab软件包中提供了C/C++的数学函数和图形库,通过其编译器可以将Matlab中编写的m 文件转换成以C/C++代码的文件,而且可以将m文件生成dll库,甚至可以直接调用其库函数,生成不依赖Matlab的可执行文件。
(4) MIDEVA(Matcom)是第一个由Matlab到C++的编译开发软件平台.使用Matcom编译器可以将Matlab源代码译成同等功能的C++ 代码,并编译为EXE或DLL文件。既保持了Matlab的优良算法,又提高了执行速度。它还支持一定的图形显示,生成代码的可读性很好。因其简单便捷、功能强大、应用灵活,本文采用这种实现方案。
3 Matcom在图像处理程序中的具体使用方法和技巧
下面以实现图像处理中绘制直方图和快速傅立叶变换的编程过程为例,介绍通过Matcom实现VC对Matlab程序的调用过程,运行环境为:WindowsXP、Matlab6.5、Matcom4.5、Visual C++6.0。
3.1 编译环境设置[2]
首先安装MIDEVA,然后运行Visual C++6.0,从菜单条中选择Tools/Customize/Add-ins and Macro Files,在出现的对话框中选中Visual Matcom Add-in,关闭对话框,这时在Visual C++[1]的开发环境中看到一个如图1所示的Visual Matcom工具栏,表明安装成功。
图1 Visual MATCOM图标
3.2 代码编写
在MATCOM或者Matlab环境中编写实现绘制图像直方图的文件myhist.m代码如下:
function h=myhist(x) %绘制灰度图像的直方图
h=zeros(1,256);[m,n]=size(x)
for i=1:m
for j=1:n
h(1,x(i,j)+1)=h(1,x(i,j)+1)+1;
end
end
plot(0:255,h)
建立一个新的VC++工程或者使用现有的图像处理的MFC工程Dip.dsw,用C++实现DIB图像的读取,获取图像相关信息,图像显示,保存等基本功能 [3]。再建立一个用于图像频域处理的类CFreqPro,将来在此类中添加有关频域处理的成员函数。点击Visual Matcom工具栏上的m++图标,选择保存过的Matlab文件myhist.m进行转化。这时会在VC中出现一个转换完毕的说明文件,文件中如果报告有错误,可以双击C++files文件夹下的myhist.m直接进行修改,不必回到Matlab环境,再重新转化直到没有错误报告为止。在需要使用 myhist()函数的文件中添加头文件matlib.h和myhist.h,加入代码#include”matlib.h”,#include” myhist.h”。
在调用matcom的Matrix<LIB> C++库函数之前用initM(MATCOM_VERSION)初始化类库调用,并用exitM()结束对类库的调用。在MFC工程文件中,一般是在一个类(例如CDipDoc)的构造函数中添加initM(MATCOM_VERSION)以完成类库的初始化工作,在该类的析构函数中调用exitM()结束对类库的调用。
3.3 创建图像数据矩阵和将数据矩阵赋给图像数据区的功能核心代码
在matcom中,提供了一个双精度Matrix 类型-Mm,因为所有的操作均为矩阵运算。为利用矩阵运算完成图像变换,首先应将其图像数据赋给矩阵变量,而且能将运算后的矩阵变量再回赋到图像数据区 [5]。为此,在CFreqPro中添加成员函数GetMatData()和SetMatData(),分别实现创建图像数据矩阵和将数据矩阵赋给图像数据区的功能。这两个函数对常用的8位灰度图像处理具有较好的通用性和参考价值。核心代码如下(有删节):
Mm CFreqPro::GetMatData()
{ DWORD SizeImage = nWidth * nHeight;
//创建图像数据矩阵,并将其元素初始值设为0
m_matBits = zeros(1, SizeImage);
//默认的矩阵数据类型是double,首先将其转换为unsigned char型
//以便用memcpy内存拷贝命令将图像数据赋给矩阵。
m_matBits = muint8(m_matBits);
//通过Matrix<LIB>C++库的.addr()函数返回矩阵变量的内存指针,完成对矩阵单元的访问
//用memcpy内存拷贝命令将图像数据赋给矩阵。
memcpy(m_matBits.addr(), pBits, SizeImage);
//由于Mm类型的矩阵是按先列后行的顺序排列,
//在此用reshape()函数将创建的一维矩阵m_matBits变为nWidth×nHeight的二维矩阵。
//再用rot90()函数将二维矩阵逆时针旋转90度,将矩阵变为nHeight×nWidth的二维矩阵,
//并使矩阵的第nHeight行对应图像数据的第一行数据。
m_matBits = rot90(reshape(m_matBits, nWidthBytes, nHeight));
//将矩阵由unsigned char型转换为double型,以便进行运算
m_matBits = mdouble(m_matBits);
}
///////////////////////////////////
BOOL SetMatData()
{ //将矩阵数据范围限定于(0—255)
m_matBits = mabs(m_matBits);
Mm L = m_matBits > 255.0;
Mm NotL = !L;
m_matBits = times(m_matBits, NotL) + L * 255.0;
//将矩阵由double型转换为unsigned char型,以便将图像数据赋给矩阵
m_matBits = muint8(m_matBits);
//对矩阵进行转置操作
m_matBits = rot90(m_matBits, -1);
//将图像数据赋给矩阵
memcpy(pBits, m_matBits.addr(), (nWidth * nHeight)*sizeof(unsigned char));
return( TRUE );}
3.4 图像的傅立叶变换和绘制直方图函数
图像数据矩阵建立后,就可以利用Matrix<Lib>C++库函数通过各种矩阵运算完成图像变换的工作。下面的函数仅实现图像的傅立叶变换和绘制直方图,用同样方法,离散余弦变换、离散沃尔什-哈达玛变换也都能实现。在图像频域处理类CFreqPro中添加成员函数FFT2()
Mm CFreqPro::FFT2(CDib *pDibObject)
{if( pDib!= NULL ) m_pDib = pDib;
//创建图像数据矩阵
GetMatData();
//获得矩阵的行数和列数
Mm mSize = size(m_matBits);
//调用Matrix<Lib>C++库函数fft2()完成二维离散傅立叶变换
Mm ff = fft2(m_matBits);
Mm matTransed = ff;
//调用Matrix<Lib>C++库函数fftshift()将频域中心移到矩阵中心
ff = fftshift(ff);
//调用Matrix<Lib>C++库函数mabs()计算频谱
m_matBits = mabs(ff) / sqrt(mSize.r(1,1)*mSize.r(1,2));
//绘制直方图
myhist(m_matBits);
//将矩阵数据赋给图像数据区
SetMatData();
return matTransed;}
运用本方法对一8位灰度图像(如图2)进行了离散傅立叶变换,结果如图3所示,利用myhist()函数实现直方图绘制,结果如图4所示。
图2 测试图像test.bmp 图3 FFT变换结果
4 结束语
在图像处理程序中,既可以直接调用调用Matrix<Lib>C++库函数,也可以在VC环境中转换.m函数文件。如果需要转换的.m文件不是一个函数,而是脚本文件 (Script),则要在工程目录下找到转换文件的.cpp文件,将其中的C代码拷贝到需要调用它的函数里面,也可以按照MATCOM的语法(类似 Matlab的语法)直接进行编写。Matcom还可以实现函数的嵌套。当所编译的.m文件依赖于其他.m文件时,只要把被调用的.m文件与要编译的.m 文件放在同一目录下,把生成的被调用文件的.h和.cpp文件插入到VC开发的工程中就可以了。作者在图像识别程序中使用这种方法来提高程序编写的效率,取得了非常好的效果。
图4 测试图像的直方图
参考文献
[1] 张志涌等. 精通MATLAB6.5版. 北京:北京航空航天大学出版社,2003
[2] MathTools Ltd,MIDEVA.MATCOM&Visual MATCOM Installtion Guide 1999
[3] 郎锐. 数字图像处理学Visual c++实现. 北京:北京希望电子出版社,2003
[4] 阮沈勇等. MATLAB程序设计. 北京:电子工业出版社,2004
[5] Duane Hanselman,B ruce littlefield. 精通MATLAB综合辅导与指南. 西安:西安交通大学出版社,1998
VC++与Matlab混合编程的研究与实现
http://www.blogcn.com.cn/user1/2178/archives/2005/10407.shtml
摘要 在分析Visual C++和Matlab各自特点的基础上,提出混合编程的几种方法,分析对比各种方案的优缺点。并结合实例介绍比较简便实用的Matcom混合编程方案,该方案不仅能转化成C/C++函数,而且可以支持图形函数功能。可以为科学研究和工程技术提供更强的技术支持。
关键词 Visual C++; Matlab; Matcom; 混合编程
Matlab是Mathworks公司推出的数学软件,它将数值分析、矩阵计算、信号处理和图形显示结合在一起,包含大量高度集成的函数可供调用,命令语句功能十分强大,为科学研究、工程设计及众多学科领域提供了一种简洁、高效的编程工具。但是Matlab使用的是解释性语言,大大限制了它的执行速度;源代码的公开不利于算法和数据的保密;局限于Matlab运行环境而不能用于开发商用软件。Visual C++是Windows平台下主要的应用程序开发环境之一,它能方便实现软件开发,开发的系统具有界面友好、执行速度快、易维护和升级等优点。但是在工程计算方面,和Matlab相比编程显得复杂的多。因此实现VC与Matlab混合编程,使两者结合起来,协同工作,必将提高软件开发效率,使所开发的软件具有更高的性能,更大的应用范围,也可以为科学研究和工程技术提供更强的技术支持。
VC与Matlab混合编程的实现方案
在VC环境中调用Matlab程序主要有以下几种方法:
(1)用Matlab引擎,采用客户机/服务器(Client/Server)的计算模式。在VC中设计程序框架,作为前端客户机,通过调用 Matlab引擎与后台Matlab服务器建立连接,实现命令和数据信息的传递。这种方式需要Matlab在后台运行,离不开Matlab环境。不利于软件的开发,但是它可以充分利用Matlab的功能,包括调用工具箱函数和图形函数。
(2)用Matlab的编译器将.m源文件转化为 c、c++等各种不同类型的源代码,并在此基础上根据应用需要生成MEX文件、独立可执行应用程序等文件类型,大大提高程序的运行速度,提高代码的执行效率。主要是使用mcc命令实现文件的转化。但是这种方法不支持图形函数,不能编译一些Matlab的内建函数,并且转换的代码可读性不太好。
(3)使用Matcom编译器可以将Matlab源代码译成同等功能的C++代码,既保持了Matlab的优良算法,又提高了执行速度。它还支持一定的图形显示,生成代码的可读性很好。因其简单便捷、功能强大、应用灵活,本文主要探讨这种实现方案。
实现VC与Matcom的接口步骤
Matcom是Mathworks公司推出的第一个由Matlab到C++的编译开发软件平台,它的可视化界面,方便丰富的调试功能和对数学库的强大支持受到广大技术人员的重视。现在的最高版本为Matcom4.5。
我们可以通过在Matcom中file菜单下的compile to exe or dll调用.m文件编译,在Matcom的debug目录下找到生成的cpp、exe、dll文件。把需要的文件加到VC开发环境的工程中,再包含头文件就可以实现调用。也可以直接在VC环境下使用科学运算库,即Matcom的Matrix<LIB>,它是按照Matcom的语法在VC中实现类似于Matlab函数的方法。但是这里还有一种将Matcom集成到VC环境的方法,即安装Visual Matcom的方式,这种方式操作更加简便易行,只需要熟悉Matlab编程,经过简单的步骤就可以轻松实现VC环境中调用Matlab。下面介绍 Visual Matcom开发环境的安装。
(1)拷贝<matcom45>\bin\usertype.dat文件(<matcom45>指Matcom的安装路径)到<visual c++>\Common\MSDev98\Bin目录(<visual c++>指VC的安装路径)下。
(2)运行Visual C++,从菜单条中选择Tools/Customize/Add-ins and Macro Files,选择Browse,改变文件类型为Add-ins(.dll),选定<matcom45>\bin\mvcide.dll文件,确定。
(3)这样,可以在Visual C++的开发环境中看到一个如图1所示的Visual Matcom工具栏,表明安装成功。
图1 Visual Matcom工具栏
应用举例
下面以一个线性方程组的求解过程为例,介绍通过Matcom实现VC对Matlab程序的调用,运行环境:Win2000、Matlab6.1、Matcom4.5、Visual C++6.0
(1)Matlab运行环境中编写程序equation.m,其代码如下:
%equation 求解线性方程组的解
%线性方程组形如:A*X = B
function X = equation(A, B)
X = A\B;
(2)这里以一个简单的控制台程序为例,其它程序基本相同。在VC环境中建立一个名为Test的Win32 Console Application工程。
(3)点击Visual Matcom工具栏上的m++图标,选择保存过的Matlab文件equation.m进行转化。如果看到的转化信息提示没有错误就可以观察到此时在 FileView标签中多了m-files,C++files created from m-files,Matrix<lib>等文件。并且该工程目录下增加了equation.h,equation.cpp, equation.mak,equation.r等的4个文件。这时会在VC中出现一个转换完毕的文件,文件中如果报告有错误就要考虑是否程序有问题,可以双击C++files文件夹下的equation.m进行修改,再重新转化直到没有错误报告为止。
(4)在Test工程下建立一个文件test.cpp调用转化的Matlab函数,代码为:
#i nclude "stdio.h"
#i nclude "matlib.h" //提供转化后C++代码中使用的数据类型,函//数原型及常数
#i nclude "equation.h"
void main()
{
/*解线性方程组:
X =
*/
initM(MATCOM_VERSION); //初始化matlib库
Mm a,b,x; //使用矩阵类Mm构造矩阵a,b,x.
a = (BR(1),2,3,semi,4,5,6,semi,7,8,1);
//给矩阵a赋值,BR是Matrix<LIB>库的一个
//宏,用于定义一个矩阵的开始;semi是库的//一个常量,用于分隔不同行的矩阵元素
b = zeros(3,1); //初始化矩阵b为零矩阵3行1列
b(1,1) = 37; b(2,1) = 85; b(3,1) = 69; //给矩阵b赋值
x = equation(a,b); //调用转化的函数,求解线性方程组的解
for (int i = 1; i <= x.rows(); i++) //把解矩阵X的元素显示出来
{
for (int j=1;j<=x.cols();j++)
printf("x(%d,%d)=%f\n",i,j,x.r(i,j));
}
exitM(); //结束对matlib库的调用
return;
}
注意:程序中涉及到了两个成员函数.rows()和.cols(),它们分别返回矩阵的行数和列数;x.r(i,j)代表矩阵x的第i行第j列的元素。
(5)编译运行后结果为x(1,1) = 3.000000 x(2,1) = 5.000000 x(3,1) = 8.000000与实际结果一致。
总结:如果需要转换的.m文件不是一个函数,只是一些Matlab命令的集合,则要在工程目录下找到转换文件的.cpp文件,将其中的C代码拷贝到需要调用它的函数里面。Matcom克服了mcc命令只能编译一个独立.m文件的缺点,当所编译的.m文件依赖于其他.m文件时,只要把被调用的.m文件与要编译的.m文件放在同一目录下,生成的被调用文件的.h和.cpp文件插入到VC开发的工程中就可以了。
上面只是一个简单的控制台工程的例子,我们也可以建立其它类型的工程,只要在需要调用转换后函数的程序中包含matlib.h并且在响应函数中初始化matlib库initM(MATCOM_VERSION);结束调用后做结束工作exitM();就可以了。
结束语
本文讨论了VC与Matlab的混合编程,主要集中讨论在VC中调用Matlab的实现方案。通过一个Matcom编译实现的例子我们看到,利用 Visual Matcom转换代码非常方便,生成的代码可读性好,而且还支持多数图形函数,基本上使Matlab和VC能够得到充分的发挥。作者在海洋遥感图像处理中使用这种方法来提高程序的执行速度,取得了非常理想的效果。它不仅解决了Matlab的解释执行方式带来的执行速度过慢的缺陷,更重要的是它完全脱离了 Matlab系统,当代码被编译成可执行程序后,又可大大提高程序的安全性。
评论