引言
随着数字影像设备的普及,如数字摄像机、数字照相机、USB摄像头等等。逐渐人们已经不能满足于由厂家提供的对视频数据的处理,越来越多人希望自己来处理设备捕获的图像数据,或者将视频捕获整合到自己的程序中。
视频的捕获与实时处理是目前图像处理中的关键一步,其采集到图像质量的好坏决定着后期处理的难易,更严重的影响到产品的成败。如果从开发相应设备的驱动出发,确实能解决此类问题。但驱动开发繁琐,需要投入大量时间,一旦开发成功后,不能更换其他芯片的设备,对将来的升级换代带来不便。微软提供了相对上层的解决方案。VFW和Directshow提供了硬件不相关的一个解决方案。
市场上设备大致支持:VFW(video for Windows)和WDM(Windows Driver Model)。VFW因为其功能简单,实时速度差等原因,已经趋于废弃。WDM是微软推出的前者的替代模型。WDM支持更多的特性,比如直接支持电视接收、视频会议、1394接口设备、桌面摄像机、多条视频流同时输出等等[1]。
VFW虽然趋于废弃,还是有相当多的设备兼容这个模式,一般的摄像头都支持。因为VFW整体结构小,设计直接明了,一般的对捕获速度要求不高的应用还是值得考虑VFW的。比如说捕获视频流中单帧中指定的区域,在Delphi中实现相当快。加入引用的VFW SDK后几十行代码就可以完成。
DirectShow
DirectShow是一个开放性的应用框架,也是一套基于COM的编程接口。DirectShow系统功能如图(1)[2]。图中一大块被DirectShow占据,它基本工作原理就是“流水线”:[2] 将单元组件—filter—串联在一起,由Filter Graph Manager统一管理。系统的输入可以是本地文件系统、硬件插卡、网络等,输出可以是声卡、显卡、本地文件系统,也可向网络发送数据。实际上,计算机应用领域中的很多模块都可以和DirectShow交互。DirectShow可以实现不同格式的媒体文件的解码播放或者格式之间的相互转换,可以从本地机器中的采集设备采集音视频数据并保存为文件,可以接收、观看模拟电视等。从网络应用角度来说,DirectShow可以实现视频点播、视频会议、视频监控等。从广义上来说,DirectShow系统适合一切流式数据的处理,这些数据可以是音频、视频这样的多媒体数据,但又不局限于多媒体数据[2]。
具体实现
开发环境选的是Borland的Delphi7.0, DirectShow提供的只是一堆库文件,要在Delphi中引入,必须对其进行申明。这方面工作已经有人做了,而且还做了一定程度上的封装,对使用者来说更加简便。这个就是Dspack。Dspack是免费的资源,在MPL 1.1下发行。
首先,必须从http://www.progdigy.com/上下载Dspack,文中用到的是Dspack 2.3.4 。包中自带详实的例子,如果要实现的功能不复杂,完全可以参考例子快速实现。
安装Dspack:
1.添加以下路径到搜索目录
- (DSPackDir)/src/Directx9
- (DSPackDir)/src/DSPack
操作:Delphi(菜单) Tools->Envioronment Options->Library. 在Library Path处添加。
2.编译DirectX9_Dx.dpr(x为Delphi版本号)。
3.编译DSPack_Dx.dpr(x为Delphi版本号)。
4.编译并安装DSPackDesign_Dx.dpk(x为Delphi版本号)。
安装完毕,就可以在Delphi控件面板上Dspack目录下看到相应的控件了。
文中要实现的功能是:在USB摄像头中抓取指定区域(ROI),而且在视频流上标记出要抓取的范围。具体是对于采集进来640*480的视频流中,抓取中间320*240的区域。320*240的区域必须有标记.。
具体实现:
DirectShow提供了混合两种视频流的功能VMR(Video Mixing Renderer),标记的功能可由简单把USB设备的视频流与标记的视频流混合起来实现。
新建一个Application,File->New->Application
从控件面板的目录下拖放若干控件: TVideoWindow 用于显示视频流;TfilterGraph Tfilter TsampleGrabber封装了的抓图控件
枚举设备:
SysDev := TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
判断是否有设备连接,无连接则返回。
if SysDev.CountFilters = 0 then
begin
ShowMessage('请连接摄像头');
end;
创建混合图层
//create vmrbitmap
VMRBitmap := TVMRBitmap.Create(VideoWindow);
连接设备并显示视频流
VideoWindow.Visible := true;
VideoWindow.FilterGraph := FilterGraph ;
Filter.FilterGraph := FilterGraph ;
SampleGrabber.FilterGraph := FilterGraph ; // must or cause error
FilterGraph.Active := false ;
FilterGraph.ClearGraph ;
Filter.BaseFilter.Moniker := SysDev.GetMoniker(0) ; // device index
FilterGraph.Active := true ;
FilterGraph.Mode := gmCapture;
with FilterGraph as ICaptureGraphBuilder2 do
begin
RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter,SampleGrabber as IBaseFilter, VideoWindow as IBaseFilter);
end;
FilterGraph.Play ;
标记区域
with VMRBitmap, Canvas do
begin
LoadEmptyBitmap(640,480,pf24bit, clSilver);
Source := VMRBitmap.Canvas.ClipRect;
Options := VMRBitmap.Options + [vmrbSrcColorKey];
ColorKey := clSilver;
Brush.Color := clSilver;
Font.Color := clWhite;
Font.Style := [fsBold];
Font.Size := 10;
Font.Name := 'Arial';
TextOut(319, 239, '+'); //center
TextOut(159,119,'+'); //top left
//DrawTo(0,0,1,1,0.5);
TextOut(159, 359, '+'); //bottom left
//DrawTo(0, 0, 1, 1, 0.5);
TextOut(479,119,'+'); // top right
//DrawTo(0,0,1,1,0.5);
TextOut(479,359,'+'); //bottom right
DrawTo(0,0,1,1,0.5);
end;
抓图
SampleGrabber.GetBitmap(bitmap);
利用API函数获取指定区域
StretchBlt(Image_original.Canvas.Handle, 0, 0 , 320 , 240,
bitmap.Canvas.Handle, 159, 119, 320, 240, SRCCOPY);
Image_original.Repaint;
程序结束完成
小结
在Dspack下开发简单的应用可以在不太熟悉DirectShow的情况下,只参照实例就可以做出一些简单的应用。对于那些赶进度确实是不错的选择,对喜欢深挖内涵的人,研究Dspack内部代码也是不无裨益的。
参考文献
[1] 陆其明DirectShow应用——视频捕捉WDM Vs VFW
[2] 陆其明DirectShow 实物精选
[3] http://www.progdigy.com/