Visual C++与Matlab混合编程方法在图像处理中的应用
1 引言 LUPA开源社区5x5y3p*u-U.T$ZMA
Matlab 是美国Mathworks公司于20世纪80年代中期推出的一套数值计算软件,可以实现数值分析、矩阵运算、自动控制、信号处理和图像处理等若干领域的计算和图形显示功能。它不仅包含大量高度集成的函数可供直接调用以解决各种复杂的计算,而且提供了简洁的人机界面、丰富的图形用户界面GUI(Graphical User Interfaces)开发功能以及求解特定学科问题的求解工具箱ToolBox。与此同时编写程序Matlab所需时间往往能比使用其他编程语言缩短许多倍。自面世以来,Matlab在教学和科研等领域受到了广泛的重视和应用, 在解决复杂的数学计算和新算法的研究中具有极大的优势。
G ^[H,~+bj$K.iY#B01.1 软件的特点归纳 [4]LUPA开源社区)CrBMd q&eR Se1f B
(1) 易学易用性 LUPA开源社区 j Z J\W L/M-k
Matlab是一门编程语言,其语法规则和C语言大同小异,因而具有一般编程语言基础的用户很快就可以掌握。 LUPA开源社区'A;LS"gO6Yt9Q
(2) 代码短小高效 LUPA开源社区RoiXr$aN-r
Matlab将不同数学分支的算法以函数的形式分类成库,使用时直接调用这些函数并赋予实际参数就可以解决问题,快速而准确。
`sb)N.hn[ Y0(3) 计算功能强大 LUPA开源社区Hi1AJ6q,P xt
该软件具有强大的矩阵计算功能,除了一般的加、减、乘、除、转置和求逆运算外,还非常适合处理稀疏矩阵等特殊矩阵运算以及有限元等大型数值算法的编程。 LUPA开源社区+]-z.\O0Lg\M e}
(4) 可扩展性能
li7k'[!DC$}m0Matlab最重要的特点是易于扩展,它允许用户自行编写指定功能的m文件,组成自己的工具箱。当前支持Matlab的商用ToolBox(工具箱)有数百种之多。Matlab支持DDE和ActiveX自动化等机制,可以与同样支持该技术的应用程序接口。利用m文件或MEX文件可以实现也VB、VC等程序的进程内无缝集成;利用web服务器,可实现Matlab与网络程序的接口;利用API函数,还可以实现Matlab与硬件的接口。
2^r Id]Tc7|O01.2 Matlab 自身所存在的某些不足也限制了它的应用范围
9P*r:[.U#U6Y0(1) Matlab 是一种解释性语言,所以它的代码执行速度慢,这对于实时性要求较高的领域,如自动控制、信号处理等,其实时效率是较差的。
\M]"kU _5w6v0(2) Matlab 程序不能脱离其环境运行,其功能只能在它本身所提供的平台上使用,因而不能被用于开发商用软件。
~ j.Py#So"?\0(3) Matlab 程序可被直接看到源代码,因而算法和数据的保密性欠佳。 LUPA开源社区 T2To(g'Nk]r
Visual C++( 以下简称VC)是Microsoft公司推出的强大的可视化集成编程环境, 从底层软件到上层直接面向用户的软件都可以用VC开发, 而且强大的调试功能也为大型复杂软件的开发提供了高效的排错手段。使用VC开发的系统具有界面友好、代码效率高和执行速度快等一系列优点. 同时C++语言支持面向对象的程序设计, 而利用面向对象方法设计的软件具有良好的可重用性、可维护性和可扩充性等特点。它是目前综合性最高、最强大、也是最复杂的软件开发工具之一, 应用极为广泛。
k~6ei't3b0因此实现VC与Matlab混合编程,使两者结合起来,协同工作,将大大减少编程的工作量、提高程序执行效率。利用VC这一优秀编程工具可以弥补Matlab在处理具体问题时的缺陷,利用Matlab的强大数值运算等功能则可以增强VC对信号处理、三维处理、自动控制等方面的能力。两者结合将会充分发挥各自的优势,必将提高软件开发效率,使开发的软件具有更高的性能,更广阔的应用领域。LUPA开源社区h)`l1Q^E l,uH
2 VC++ 与Matlab的接口技术简介
Rz4UV1s*c(|%`02.1 在VC环境中调用Matlab程序主要方法
G H(d0t7hwE]0为将Matlab的强大功能融入到各种应用程序中,通过高级语言编译器将Matlab的m文件编译为二进制代码已成为研究热点。
e+_2f.vH(MI2o._0(1) 在VC中启用MATLAB EN GINE(引擎),采用客户机/服务器(Client/Server)的计算模式。这种方式需要Matlab在后台运行,离不开Matlab环境。但是它可以调用Matlab工具箱函数和图形函数。
$b,}f,Mnj g;D$c0(2) 利用MATLAB COMPIL ER(编译器),将Matlab 的函数编译成脱离Matlab环境的可执行程序(.exe文件), 在VC中调用。
$Z0X i y^6K0(3) 利用Matlab的数学函数和图形库。6.1版本的Matlab软件包中提供了C/C++的数学函数和图形库,通过其编译器可以将Matlab中编写的m文件转换成以C/C++代码的文件,而且可以将m文件生成dll库,甚至可以直接调用其库函数,生成不依赖Matlab的可执行文件。 LUPA开源社区-z-?BLD
(4) MIDEVA(Matcom) 是第一个由Matlab到C++的编译开发软件平台.使用Matcom编译器可以将Matlab源代码译成同等功能的C++代码,并编译为EXE或DLL文件。既保持了Matlab的优良算法,又提高了执行速度。它还支持一定的图形显示,生成代码的可读性很好。因其简单便捷、功能强大、应用灵活,本文采用这种实现方案。
E N ]&of3zZJ5fA03 Matcom 在图像处理程序中的具体使用方法和技巧
3S Wb_5`Ov0下面以实现图像处理中绘制直方图和快速傅立叶变换的编程过程为例,介绍通过Matcom实现VC对Matlab程序的调用过程,运行环境为:WindowsXP、Matlab6.5、Matcom4.5、Visual C++6.0。 LUPA开源社区tc6Ea vwYt3g1S
3.1 编译环境设置 [2] LUPA开源社区)n6C@6~6g&Si:H
首先安装MIDEVA,然后运行Visual C++6.0,从菜单条中选择Tools/Customize/Add-ins and Macro Files,在出现的对话框中选中Visual Matcom Add-in,关闭对话框,这时在Visual C++[1]的开发环境中看到一个Visual Matcom工具栏,表明安装成功。LUPA开源社区+Dv;Kxpz1?.q i
3.2 代码编写 LUPA开源社区.g3xh|Ub:f/oX!V
在MATCOM或者Matlab环境中编写实现绘制图像直方图的文件myhist.m代码如下 :
8hOU/N$UY4eXF0function h=myhist(x) %绘制灰度图像的直方图 LUPA开源社区2l S itV+{3N9Vs,C0b(H
h=zeros(1,256);[m,n]=size(x)LUPA开源社区\oIB"fe+JJ[2G
for i=1:mLUPA开源社区`V$U"RA6N!hT0HE5Q
for j=1:nLUPA开源社区!gP0g9Rv
h(1,x(i,j)+1)=h(1,x(i,j)+1)+1;
%S~:N*s`*~0endLUPA开源社区"u1b5L\L;Ck
endLUPA开源社区F;eD4U[@
plot(0:255,h)LUPA开源社区w O~#[Tc{)GH I
建立一个新的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”。
L;l!x_!nM.dN0在调用matcom的Matrix<LIB> C++库函数之前用initM(MATCOM_VERSION)初始化类库调用,并用exitM()结束对类库的调用。在MFC工程文件中,一般是在一个类(例如CDipDoc)的构造函数中添加initM(MATCOM_VERSION)以完成类库的初始化工作,在该类的析构函数中调用exitM()结束对类库的调用。 LUPA开源社区#f/ZL*T]6Rd
3.3 创建图像数据矩阵和将数据矩阵赋给图像数据区的功能核心代码
'X.]k BW W7\!Bd0在matcom中,提供了一个双精度Matrix类型-Mm,因为所有的操作均为矩阵运算。为利用矩阵运算完成图像变换,首先应将其图像数据赋给矩阵变量,而且能将运算后的矩阵变量再回赋到图像数据区[5]。为此,在CFreqPro中添加成员函数GetMatData()和SetMatData(),分别实现创建图像数据矩阵和将数据矩阵赋给图像数据区的功能。这两个函数对常用的8位灰度图像处理具有较好的通用性和参考价值。核心代码如下(有删节 ):
Ud4\CDAf0Mm CFreqPro::GetMatData()
PD9Lf!~[0v-H aBF0{ DWORD SizeImage = nWidth * nHeight;
9g&i&?V U;K4?0//创建图像数据矩阵,并将其元素初始值设为 0
[#Th4u%kI1o0m_matBits = zeros(1, SizeImage);LUPA开源社区q$H'uHuvq
//默认的矩阵数据类型是double,首先将其转换为unsigned char型 LUPA开源社区fs xT(^$w
// 以便用memcpy内存拷贝命令将图像数据赋给矩阵。 LUPA开源社区,xy{ T5xl7^
m_matBits = muint8(m_matBits);LUPA开源社区%`5Pun)f5[
// 通过Matrix<LIB>C++库的.addr()函数返回矩阵变量的内存指针,完成对矩阵单元的访问 LUPA开源社区 J`{F2cY
// 用memcpy内存拷贝命令将图像数据赋给矩阵。 LUPA开源社区(oLQY"A
memcpy(m_matBits.addr(), pBits, SizeImage);LUPA开源社区-~E&q1Ta1or
// 由于Mm类型的矩阵是按先列后行的顺序排列, LUPA开源社区"N6tCu:X0oX,t.j!O
// 在此用reshape()函数将创建的一维矩阵m_matBits变为nWidth×nHeight的二维矩阵。
bbNbX1|N0// 再用rot90()函数将二维矩阵逆时针旋转90度,将矩阵变为nHeight×nWidth的二维矩阵,
Q"mTk e0vfz0// 并使矩阵的第nHeight行对应图像数据的第一行数据。 LUPA开源社区7kyVa(tg
m_matBits = rot90(reshape(m_matBits, nWidthBytes, nHeight));
6ll*hBd0// 将矩阵由unsigned char型转换为double型,以便进行运算 LUPA开源社区Hh%o)XH
m_matBits = mdouble(m_matBits);
a.Q v%dl0}LUPA开源社区)@:b']"k!p
///////////////////////////////////LUPA开源社区Vg mA8H d?
BOOL SetMatData() LUPA开源社区*x!s$yEG4U0i0p:O
{ // 将矩阵数据范围限定于(0— 255)
5ZDS Co6Y`0~7V"b0m_matBits = mabs(m_matBits);LUPA开源社区,}2r&z_ @ L[[Gs3R
Mm L = m_matBits > 255.0;
6S!W$Tn9UwF;D0Mm NotL = !L;LUPA开源社区b-] p'BQ:^I
m_matBits = times(m_matBits, NotL) + L * 255.0;
iBc |O2olN0//将矩阵由double型转换为unsigned char型,以便将图像数据赋给矩阵
WM%P&B];v&P7K([0m_matBits = muint8(m_matBits);LUPA开源社区`*T7^+N]5k
// 对矩阵进行转置操作 LUPA开源社区 H4jmA^ e
m_matBits = rot90(m_matBits, -1);
_(Fk [1C0// 将图像数据赋给矩阵 LUPA开源社区WIbiX'WT
memcpy(pBits, m_matBits.addr(), (nWidth * nHeight)*sizeof(unsigned char));
YR#d4HM-H0return( TRUE );}
:C5E-D;Im5d7TeaU M-BW]03.4 图像的傅立叶变换和绘制直方图函数
l jpOA9h:}0图像数据矩阵建立后,就可以利用Matrix<Lib>C++库函数通过各种矩阵运算完成图像变换的工作。下面的函数仅实现图像的傅立叶变换和绘制直方图,用同样方法,离散余弦变换、离散沃尔什-哈达玛变换也都能实现。在图像频域处理类CFreqPro中添加成员函数 FFT2()
+fo^{-r }7S0Mm CFreqPro::FFT2(CDib *pDibObject)LUPA开源社区2C@/cC2v Dbj$M
{if( pDib!= NULL ) m_pDib = pDib;
H*r)^Tdqp7T!r0//创建图像数据矩阵 LUPA开源社区|9Pd]$f+r4Kv8B
GetMatData();
*Z'gOtLGiB9A0// 获得矩阵的行数和列数 LUPA开源社区D6v*NM1U
Mm mSize = size(m_matBits);LUPA开源社区B _ K&yo5p
// 调用Matrix<Lib>C++库函数fft2()完成二维离散傅立叶变换 LUPA开源社区D!_u~2D8v5Y9} pC%U
Mm ff = fft2(m_matBits);
%jHw p j+eT0Mm matTransed = ff;LUPA开源社区ss$v+@S6x6qj
// 调用Matrix<Lib>C++库函数fftshift()将频域中心移到矩阵中心 LUPA开源社区k(Js],S4e [2l]
ff = fftshift(ff);
8X Xj vv{0// 调用Matrix<Lib>C++库函数mabs()计算频谱 LUPA开源社区5@?Y)G6\9dQv
m_matBits = mabs(ff) / sqrt(mSize.r(1,1)*mSize.r(1,2));LUPA开源社区 s9}&fR3]8@%bXw$Q
// 绘制直方图
uwk:QMw0myhist(m_matBits);
R%n*H:Mr:T]0P0// 将矩阵数据赋给图像数据区 LUPA开源社区Fo8_B%[i f X0Y~k
SetMatData();LUPA开源社区_j FgR!D
return matTransed;}
"d!QM @ y v0运用本方法对一8位灰度图像(如图2)进行了离散傅立叶变换,结果如图3所示,利用myhist()函数实现直方图绘制。
+FJ U,kP D;r04 结束语
:tc5YJ:Dl r+Xi0在图像处理程序中,既可以直接调用调用Matrix<Lib>C++库函数,也可以在VC环境中转换.m函数文件。如果需要转换的.m文件不是一个函数,而是脚本文件(Script),则要在工程目录下找到转换文件的.cpp文件,将其中的C代码拷贝到需要调用它的函数里面,也可以按照MATCOM的语法(类似Matlab的语法)直接进行编写。Matcom还可以实现函数的嵌套。当所编译的.m文件依赖于其他.m文件时,只要把被调用的.m文件与要编译的.m文件放在同一目录下,把生成的被调用文件的.h和.cpp文件插入到VC开发的工程中就可以了。