第10章 图象处理编程工具及简单的多媒体编程在前九章,我们主要是介绍一些数字图象处理的基本原理和算法,很多细节和优化的算法都没有提,所以程序的通用性和效率并不理想。其实有很多优秀的图象处理编程工具,功能强大,速度很快。在实际的编程中,可以调用它们提供的功能来满足我们自己的需求,提高了编程效率和代码的正确性。 在这一章里,将介绍图象处理编程工具LeadTools(OCX)。给出的例程中要用到Visual C++(以下简称VC)的AppWizard和MFC的一些特性,不熟悉C++的读者可以先看看相关的参考书。 10.1 LeadToolsLeadTools(OCX)是Lead Technologies公司出品的一款功能强大的图象处理编程工具,目前的最新版本是Lead8.0(1997.7.25)。LeadTools实质上是一个OCX控件。先来说说什么是OCX控件? 如果你用过Visual Basic或者Delphi一类的可视化编程工具,那么对控件这个概念一定不会陌生,就是那些工具条上的小按钮,如EditBox,Grid,ImageBox,Timer等等。每个控件都有自己的事件(事件) 、方法(方法)和属性(属性)。使用了控件的编程非常容易。首先,在程序的设计阶段可以设置一些属性,如大小,位置,标题(caption)等等;在程序运行阶段,可以更改这些属性,还可以针对不同的事件,调用不同的方法来实现对该控件的控制。控件就好象一块块的积木,程序要做的事只是将这些积木搭起来。控件的最大好处是可以重复使用,甚至可以在不同的编程语言之间使用,例如你可以在VB中嵌入用VC开发的控件。 控件的本质是微软公司的对象链接和嵌入(OLE)标准。由于它充分利用了面向对象的优点,使得程序效率得到了很大的提高,从而得到了广泛的应用。国外有很多公司就是专门制作各种各样控件的。控件的最早形式是以.VBX的格式出现的,后来变成了.OCX。由于Internet的广泛流行,微软公司推出了ActiveX技术,就是从OLE发展起来的,加入了WWW上的功能。所以目前最流行的是ActiveX控件。 介绍完了OCX控件的概念,下面我们以Lead Version5.1为例,来看看LeadTools究竟有哪些功能?Lead Version5.1可以在正版MicroSoft Visual C++4.1安装光盘中Msdev\samples\ocx\leadtools和Msdev\redist\redist下找到,主要是4个文件, Lead51n.dll,Lead32.ocx,Lead.lic,Ltocx.hlp。如果你想了解LeadTools的最新信息,可以访问以下www网址:http://www./。 LeadTools主要有以下几大功能: (1) 对显示设备的全面支持:在显示时,你的程序中无需考虑是哪种显示模式,如16色还是真彩色。LeadTools为你做了所有的事。当然你也可以通过设置参数获得对显示设备更多的控制。你还可以实现自己的调色板。 (2) 支持多种文件格式:表10.1是LeadTools所支持的常用文件格式,其中读表示用LeadTools能打开的文件格式,写表示LeatTools能存成的文件格式: 表10.1 LeadTools支持的常用文件格式
以上只是常用的文件格式,除此以外还有很多其它的文件格式,感兴趣的读者可以参考帮助。 (3) 图象处理:如二值化、平滑、加噪声、增加对比度、色调、饱和度、亮度、Gamma校正、中值滤波、半影调、抖动、橡皮筋、滚动、填充、反色、镜象、马赛克、浮雕、打印、扫描、拷贝、粘贴、裁剪、缩放、截屏、调色板、直方图、有关数据库的操作、还有制作幻灯的功能,如淡入、淡出、卷帘等等、真的很爽。表10.2是Version5.1的所有属性、事件、和方法列表,看看有没有你所需要的功能。以字母顺序排列,其中标[P]的表示只有专业级(Professional)和特殊级(Express)用户才能使用。 表10.2 LeadTools的事件、属性、方法
(4) 源代码例子:LeadTools的帮助做的特别好,这一点非常吸引人。每一个事件、方法、属性都有很详细的说明,后面都附有一个详细的例子。而且例子有很多种版本,如VC版、VB版、Delphi版、Access版,这样使用不同编程语言的程序员都能从中获得直接的帮助。 好了,介绍了这么多LeadTools的功能,下面我们给出一个最简单的利用LeadTools例子,其功能是显示任意一幅图(当然是Lead能支持的文件格式),图被缩放成和控件一样大小。 [例子10.1] 用Lead显示一幅图 步骤1:打开VC++5.0,用MFC AppWizard (exe)新建一个Project,起名为TestLead。要注意的是在Step1中,选定"Dialog based",Step2中,选定"ActiveX Controls",其它过程全部采用缺省值。 步骤2:把Lead控件添加到工具条中,方法是:(1)打开Project->Add To Project->Components and Controls菜单(其它的VC版本,如4.0、4.1、4.2,为Insert->Component…菜单),出现对话框,选择“Registered ActiveX Controls”那个目录(其它的VC版本为”OLE Controls”那一页);(2)选中"Lead Std Control"那一项,如图10.1所示。然后按下Insert菜单即可;
(3)如果没有该项,找到lead32.ocx文件所在的目录(一般为95的系统目录),选择该文件,按下Insert按钮即可(其它VC版本的操作方法是:按下Customize菜单,出现一个新的对话框,按下Import菜单,找到lead32.ocx文件所在的目录,选择该文件,按下Import按钮,再按下OK按钮,然后做(2)所做的工作即可)。按下Close按钮,关闭Component对话框。插入该控件后,会出现一个对话框,提示你新增加了两个类:Clead和Cpicture,选择Ok。打开对话框资源,选择ID为IDD_TESTLEAD_DIALOG的对话框,这时可以看到该控件已经添加到工具条中,如图10.2所示。 步骤3:将Lead控件从工具条拖到对话框中,设置好合适的大小,右击该控件,选择Property菜单,设置其ID属性为IDC_LEAD1,删除“TODO: Place form controls on this dialog”的静态文本。 步骤4:按Ctrl+w,出现ClassWizard对话框,选择Member Variables那一页,为该ID添加一个变量m_Lead1,Category为Control,Variable Type为Clead。 步骤5:编辑TestLeadDlg.cpp文件中的OnInitDialog()函数,在 // TODO: Add extra initialization here后加入如下的代码: m_Lead1.Load(“c:\\test.jpg”,0,0); m_Lead1.SetAutoScroll(TRUE); m_Lead1.SetDstRect(0,0,m_Lead1.GetScaleWidth(), m_Lead1.GetScaleHeight()); m_Lead1.SetDstClipRect(0,0,m_Lead1.GetScaleWidth(), m_Lead1.GetScaleHeight()); m_Lead1.SetAutoRepaint(FALSE); m_Lead1.ForceRepaint(); 步骤6:编译运行,结果如图10.3所示。
图10.3 例10.1的运行结果 可以看到,使用了控件的编程是多么的简单。更复杂的应用,读者可以参考帮助来完成,要注意的是,使用了LeadTools的应用程序一定要注意版权问题,另外,程序中如果用到了LeatTools的OCX,在制作安装程序(如用InstallShield)时一定要将OLE的信息进行注册,否则用户无法正常运行程序。 10.2 DirectDraw本节内容主要参考自微软公司的技术文档。 相信游戏玩家对DirectX这个词并不陌生。最近有越来越多的游戏用到了DirectX。那么DirectX究竟是什么呢? 在Windows3.x的时代,由于Microsoft提供的Windows API不能对硬件直接操作,使得Windows3.x下的游戏不仅效果差,而且运行速度极慢。而DOS4GW有很多很好的性能,例如:可对硬件直接操作;访问超过16比特大小的内存区;可对保护模式编程等等,使得大部分游戏是在DOS4GW下利用如WATCOMC一类的开发工具编制的,如老版本的《仙剑奇侠传》。为了吸引游戏商到Windows平台上来,Microsoft专门开发了Windows的游戏接口WinG,然而效果并不理想。在推出Windows95之后,Microsoft又开发了该平台的GAME SDK,这就是DirectX。 DirectX是非常成功的,很多优秀的游戏都又从DOS平台移植到Windows95平台,《仙剑奇侠传》出了Win95版本,Red Alert, Diablo,以及最近十分流行的Age of Empire,都用到了DirectX。 DirectX技术的最大特点是能直接对硬件抽象层(HAL)操作,实现视频、声音的输出、网络相互通信,特别能对游戏杆直接编程。与传统的GDI和MCI相比,不仅大大加快了速度,而且大大地提高了游戏的质量,有人甚至已将DirectX称为Windows95的GAME OS。目前的最新版DirectX5.0 SDK包括DirectDraw、DirectSound、DirectPlay、Direct3D、DirectInput、DirectSetup六个部分。其中DirectDraw管理游戏的视频输出,DirectSound管理游戏的声音输出,DirectPlay管理游戏的网络通信,Direct3D管理游戏的三维图形,DirectInput管理游戏的游戏杆控制,DirectSetup管理游戏的安装。 因为本书的题目是图象处理编程,我们这里只介绍DirectDraw的大致原理。有的读者可能会说:“跑题了,图象处理编程和编游戏有什么关系?”其实DirectDraw并不只是用在游戏中,由于它的显示处理速度快,在很多和视频有关的软件中(比如说Mpeg的解码器)都可以用到。 DirectDraw是为速度而设计的,它绕过与Windows的图形设备相连的多个层次,直接与硬件的底层打交道。这很适合游戏编程,因为它着重于快速产生平稳的图形。 但DirectDraw最重要的一点在于它对不同的显示适配器具有一个共同的接口。你不必管你的程序它是否会工作。DirectDraw利用包含在硬件抽象层(HAL)中的信息来决定显示适配器的功能。(HAL是由显示适配器厂商提供的)HAL为不同的硬件厂商和使用.DirectDraw的开发者提供了共同的接口。 然而,DirectDraw并不只限于利用显示适配器的硬件功能。如果你的程序指定了某一种特定的显示适配器,例如XXXX hardware blitter,但用户并没有该硬件,程序就会使用DirectDraw的硬件仿真层(HEL)。在这种情况下,DirectDraw利用内建的硬件仿真来仿真缺少的硬件。 下图一说明了DirectDraw和其他Windows显示构件的联系。
图10.4 DirectDraw和其他Windows显示构件 DirectDraw API由DirectDraw对象组成,它表示具体的显示适配器。另外,DirectDraw API还包括表示surface的DirectDrawSurface对象,表示surface调色板的DirectDrawPalette对象和表示剪接列表的DirectDrawClipper对象。可以用DirectDraw对象来创建DirectDrawSurface和DirectDrawPalette对象。 本节只介绍到这里,目的主要是想说明由于游戏对显示速度的要求非常高,所以DirectDraw是为速度而设计的。如果要编写一个对速度要求很高(如Mpeg的解码器)的图象处理系统中,可以考虑使用DirectDraw。 10.3 简单的多媒体编程前面讲过的内容都是针对数字图象处理这一领域的,现在来谈一些题外的东西,比如说多媒体和MPEG。 多媒体这几个字,近年来是非常流行的。它包含的东西也是非常多的,除了文本,静态图象,还包括音频,视频等媒体信息。有时候在程序中加入一小段多媒体的东西,能给你的软件增色不少。 其实编制一个多媒体播放程序非常简单,关键代码不超过10行代码,你相信吗?其实质是MCI。MCI是Media Control Interface(媒体控制接口)的缩写,它提供了一套与设备无关的命令消息和命令字来控制媒体的播放。MCI可以播放的文件类型有AVI,WAV,MIDI,MPEG(如果系统中已经安装了MPEG的驱动程序,如安装XING,ACTIVEMOVIE时带的),JPEG等等。MCI窗口底部有一个播放条,上面有播放/暂停(Play/Pause)按键、有显示播放进度的标尺,还有一个菜单,可调整窗口大小、声音大小,速度快慢等等,就和媒体播放器的界面一样。库VFW32.LIB中提供了MCI的所有功能,编译后生成的是真正的可执行文件,无需附加动态库和控件,如VBX和OCX等。 源代码如下,其中黑体部分为关键代码,可以采用如下的命令行编译: cl testmci.c user32.lib vfw32.lib //testmci.c #include <windows.h> #include <vfw.h> void SetClientRect(HWND hwnd, HWND hwndMCI) { RECT rect; GetWindowRect(hwndMCI, &rect); AdjustWindowRectEx(&rect, GetWindowLong(hwnd, GWL_STYLE), FALSE, GetWindowLong(hwnd, GWL_EXSTYLE)); MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left,rect.bottom - rect.top, TRUE); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam) { switch(uMsg) { case MCIWNDM_NOTIFYPOS: case MCIWNDM_NOTIFYSIZE: SetClientRect(hwnd, (HWND)wParam); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow) { MSG msg; HWND hwnd; WNDCLASS wndClass; if (hPrevInstance == NULL) { memset(&wndClass, 0, sizeof(wndClass)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = "HELLO"; if (!RegisterClass(&wndClass)) return FALSE; } hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); SetClientRect(hwnd, MCIWndCreate(hwnd, hInstance, WS_VISIBLE|WS_CHILD| MCIWNDF_SHOWALL| MCIWNDF_NOTIFYSIZE| MCIWNDF_NOTIFYPOS, “c:\\test.avi”)); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; } 运行时文件C:\test.avi必须准备好,当然,你可以改成其它的文件名。其运行结果如图10.5所示。
图10.5 TestMCI的运行结果 该程序非常简单,所有的代码加在一起不超过60行,其关键部分只有一个MCIWndCreate函数,细节就不介绍了,有兴趣的读者可以参看VC的帮助。 |
|