收藏 分销(赏)

vfw开发总结.doc

上传人:仙人****88 文档编号:8784398 上传时间:2025-03-02 格式:DOC 页数:16 大小:74.54KB 下载积分:10 金币
下载 相关 举报
vfw开发总结.doc_第1页
第1页 / 共16页
vfw开发总结.doc_第2页
第2页 / 共16页


点击查看更多>>
资源描述
VFW开发总结 使用VFW写的C#控制摄像头最大的问题就在于需要自己手动另起一个线程。(这里,我们定义一个叫AviCapture.cs的类,用于引入avicap32.dll以及相关的内容)在avicap32.dll中,CAPTUREPARMS结构里有一个fYield的东东,代表的意思是另起线程标志位,如果为真,则程序重新启动一个线程用于视频流的捕获,默认值是假。但是如果你是为了真,你必须要在程序中处理一些潜在的操作,因为当视频捕获时,其他操作并没有被屏蔽。。在AviCapture这个类的基础上定义一个叫Video的类(自己定义的),实现控制设想头的一些方法,如打开摄像头,关闭摄像头,开始录像,结束录像,拍照片等等。。。。在Video类中还要定义两个C#控制摄像头函数如下:     使用如下代码加入视频捕获, function    capCreateCaptureWindow(     lpszWindowName      : LPCSTR;     dwStyle             : DWORD;     x, y                : int;     nWidth, nHeight     : int;     hwndParent          : HWND;     nID                 : int     ): HWND; lpszWindowName: 是捕获窗口的名称,可以是任意字符串,delphi中是pchar类型 dwStyle: 是捕获窗口的类型,一般设为WS_CHILD | WS_VISIBLE. x, y, nWidth, nHeight: 是显示捕获视频的rect hwndParent: 是显示视频窗口的句柄 nID : 默认为0; 函数返回值Hwnd类型,即摄像设备的句柄,以后的操作基本上都是针对设备句柄进行的.   然后用function capDriverConnect(hwnd: HWND; i: INT): BOOL;连接设备的驱动, hwnd为设备句柄,i是驱动的序号(如果系统装有多个设备驱动,要选择一个正确的驱动才行,这些稍后在介绍).     驱动连接成功后,用function capPreviewRate(hwnd: HWND; wMS: WORD): BOOL;函数设置预览时的帧速,wMS为帧速值(单位:帧/秒),一般取值范围(10-30).   设置完速率后   ,用function      capPreview(hwnd: HWND; f: BOOL): BOOL;函数进行预览了,设置f为true后就可以看到摄像头的画面了.   以上简单的显示了视频画面,但是在显示视频画面的过程中还要进行一些参数的设置.今天就Ok了.   窗口句柄 (HWND)   m_hCapWnd=capCreateCaptureWindow((LPTSTR)TEXT("视频捕捉测试程序"),WS_CHILD|WS_VISIBLE|WS_EX_CLIENTEDGE|WS_EX_DLGMODALFRAME,0,0,rect.Width(),rect.Width(),pWnd->GetSafeHwnd(),0); // 设置预示窗口    ASSERT(m_hCapWnd);       if(capDriverConnect(m_hCapWnd,0)){// 连接第0 号驱动器      m_bInit=TRUE;       // 得到驱动器的性能    capDriverGetCaps(m_hCapWnd,sizeof(CAPDRIVE  RCAPS), &m_CapDrvCap);  if(m_CapDrvCap.fCaptureInitialized){  // 如果初始化成功 capGetStatus(m_hCapWnd, &m_CapStatus,sizeof(m_CapStatus)); // 得到驱动器状态 capPreviewRate(m_hCapWnd,30); // 设置预示帧频 capPreview(m_hCapWnd,TRUE); // 设置预示方式  }  else{// 初始化未成功  AfxMessageBox("视频捕捉卡初始化失败!");  AfxGetMainWnd() ->PostMessage (WM_CLOSE);     }       } else{// 未能连接到驱动器        AfxMessageBox("与视频捕捉卡连接失败!");        AfxGetMainWnd() ->PostMessage(WM_CLOSE);       } m_CapFileName="c://Capture.avi";// 设置捕获文件 capFileSetCaptureFile(m_hCapWnd,m_CapFileName.GetBuffer(255));   5、在对话框类中加入响应“设置格式”消息的函数OnFormat()。   capDlgVideoFormat(m_hCapWnd);// 设置格式对话框   6、在对话框类中加入响应“设置图像源”消息的函数OnSource()。   capDlgVideoSource(m_hCapWnd);// 设置图像源对话框   7、在对话框类中加入响应“设置压缩”消息的函数OnCompress()。   capDlgVideoCompression(m_hCapWnd);// 设置压缩对话框   8、在对话框类中加入响应“捕捉”消息的函数OnCapture()。   capCaptureGetSetup(m_hCapWnd, &m_Parms,sizeof(m_Parms));// 得到设置参数   if(capCaptureSetSetup(m_hCapWnd, &m_Parms,sizeof(m_Parms))==TRUE){             BOOL suc=TRUE;     suc=capCaptureSequence(m_hCapWnd); // 捕捉到文件             return suc};        else     return FALSE;        注意点:在VideoCaptureDlg.h中把afx_msg void OnCapture();改为afx_msg BOOL OnCapture();                      在VideoCaptureDlg.cpp 修改为BOOL CVideoCaptureDlg::OnCapture() //捕捉   9、在对话框类中加入响应“定帧”消息的函数OnFreezed()。   capPreview(m_hCapWnd,FALSE);// 定帧   10、在对话框类中加入响应“单帧捕获”消息的函数OnImage()。   capGrabFrameNoStop(m_hCapWnd);// 截获当前图像   capEditCopy(m_hCapWnd);// 将图像拷贝到剪贴板   11、在对话框类中加入响应“停止”消息的函数OnStop()。   capCaptureStop(m_hCapWnd);// 停止捕捉   12、在对话框类中加入响应“退出”消息的函数OnExit()退出前断开捕捉器与驱动器的连接,并关闭窗口。   capDriverDisconnect(m_hCapWnd);     CDialog::OnCancel();         附加说明:以上添加的按钮用于捕捉图像(button);                          另外添加(Static Text)用于创建并设置捕获窗口;         保存图像的方法:点击“单帧捕获”——>打开附近里的“画图”——>“编辑”中的“粘贴”——>即可保存捕捉的位图。          以上方法主要是采用了VFW的函数和宏,有兴趣的朋友可以尝试用消息的方法,同样也可以实现。                       如:SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L);//用于连接设备 要做的就是根据相关的功能改变相应的参数就可以了。 capCreateCaptureWindow(     lpszWindowName      : LPCSTR;     dwStyle             : DWORD;     x, y                : int;     nWidth, nHeight     : int;     hwndParent          : HWND;     nID                 : int     ): HWND; lpszWindowName: 是捕获窗口的名称,可以是任意字符串,delphi中是pchar类型 dwStyle: 是捕获窗口的类型,一般设为WS_CHILD | WS_VISIBLE. x, y, nWidth, nHeight: 是显示捕获视频的rect hwndParent: 是显示视频窗口的句柄 nID : 默认为0; 函数返回值Hwnd类型,即摄像设备的句柄,以后的操作基本上都是针对设备句柄进行的. 然后用function capDriverConnect(hwnd: HWND; i: INT): BOOL;连接设备的驱动,hwnd为设备句柄,i是驱动的序号(如果系统装有多个设备驱动,要选择一个正确的驱动才行,这些稍后在介绍). 驱动连接成功后,用function capPreviewRate(hwnd: HWND; wMS: WORD): BOOL;函数设置预览时的帧速,wMS为帧速值(单位:帧/秒),一般取值范围(10-30). 设置完速率后,用function capPreview(hwnd: HWND; f: BOOL): BOOL;函数进行预览了,设置f为true后就可以看到摄像头的画面了. 以上简单的显示了视频画面,但是在显示视频画面的过程中还要进行一些参数的设置.今天就Ok了.   1、 int intDevice = 0;是什么意思?起初我猜测0表示第一个摄像头,那么1不就表示第二个摄像头吗?经过我的尝试发现并非如此,那么0到底是什么意思呢? 回答:这只是捕获窗口的窗口名称而已。你可以看到实际上他转换成了String类型,在capCreateCaptureWindow里面作为第一个参数,这个参数就是表示窗口名称。 2、hHwnd = Camera1.capCreateCaptureWindowA(ref refDevice, 1342177280, 0, 0, 640, 480, this.panel1.Handle.ToInt32(), 0);各个参数的含义,这个我从msdn中查到了,但是对于第二个参数和最后一个参数还是不理解,希望大家给以解答。 回答:第二个参数表示窗体风格,也就是CreateWindow的时候都要使用的,具体的说就是表示你窗体有没得边框啦,支持不支持拖放拉等等。这个可以去MSDN里面查 CreateWindowEx 函数便知。 最后一个参数表示窗口标识符。只要每个窗体不一样,能区分即可。 3、Camera1.SendMessage(hHwnd, 0x40a, 0, 0),第一个参数是句柄,第二个参数表示的是一个消息,是哪个消息呢?后两个参数是到底为消息提供什么样的东西呢? 回答:0x40a 表示 WM_CAP_DRIVER_CONNECT消息,后面两个参数表示消息附带的两个参数,每个消息都有两个参数的。按照VFW的规定,第一个参数是捕获设备的索引。这儿是0,表示你的第一个捕获设备。第二参数恒为0。为什么?VFW就是这么规定的。 你要连接两个摄像头,着眼点就是这儿,只需要重新创建一个铺货窗口,并连接到另外一个摄像头的驱动上,事先知道设备ID就行了。这个可以通过设备枚举获得,具体参见MSND中Enumerating Installed Capture Drivers相关章节。就OK了。 4、 Camera1.SendMessage(this.hHwnd, 0x435, -1, 0); Camera1.SendMessage(this.hHwnd, 0x434, 0x42, 0); Camera1.SendMessage(this.hHwnd, 0x432, -1, 0); 分别表示的意思是什么? 回答:0x435表示WM_CAP_SET_SCALE,可以打开或者关闭预览的比例缩放,后面是参数,这些都可以在MSDN中查到。我就不说了。比如这里是-1就是表示为真,也就是说图像会根据窗口大小进行缩放。 0x434表示WM_CAP_SET_PREVIEWRATE,这个是设置显示速度的。也就是帧率。 0x432表示WM_CAP_SET_PREVIEW,这个标识打开关闭预览模式。这儿是-1,自然表示为真,也就是打开了。 5、在一个程序中,能不能同时使用两个摄像头,需不需要第三方软件的支持? 回答:当然可以。不需要三方支持。这是微软的活儿,只有别人依赖他支持,怎么可能微软还需要依赖别人。 解释一下,什么是回调函数呢,它有什么用处?   回调函数,就是你自己写的函数,符合规定的参数和返回值类型,符合规定的调用约定,比如上面这个函数 就是回调函数,参数和返回值类型都是规定好的,调用约定为CALLBACK,CALLBACK   其实是一个宏 #define CALLBACK__stdcall 满足一定条件时,此函数可以被系统自动调用,在回调函数当中,你可以写自己的代码完成一定功能。     比如在这里,用capSetCallbackOnFrame(m_hVideo, FrameCallbackProc)注册后,当每得到一桢数据后,系统就 调用函数FrameCallbackProc。       但是,我的目的还没有达到,我其实想在每一桢显示之前,能处理一下这一桢的数据,那么,去哪里找这桢数据存 放的位置呢? (8)为了完成我的目标,我把步骤(7)中的两句代码先注释掉。在对话框上加一个按钮,并在对单击做出响应的响应函数 中写下面代码: capGrabFrame(m_hVideo); 这是一个宏,将鼠标移动到这段代码上,右键单击,选择Go To Definition of capGrabFrame,你会看到 #define capGrabFrame(hwnd)((BOOL)AVICapSM(hwnd, WM_CAP_GRAB_FRAME, (WPARAM)0, (LPARAM)0L)) 而继续察看AVICapSM宏你会看到其实是在调用SendMessage函数呢,对吧,其实就是在发送消息。至于消息谁处理了,我们就不去 关心了,我们关心的是,发送消息后,系统会调用我们刚才注册的回调函数 LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) ; (9)好了,如果你单击按钮,capGrabFrame(m_hVideo)就发送消息了,然后,我们就进入回调函数了,这太好了。   看到回调函数传递的两个参数了么?我们更关心第二个参数,这个就是单击按钮我们捕捉到的一桢数据的入口啊! LPVIDEOHDR 是结构体VIDEOHDR的指针,而在MSDN中察看结构体VIDEOHDR,我们就可以找到桢数据的存贮位置指针了。 VIDEOHDR定义如下: typedef struct videohdr_tag { LPBYTElpData; DWORDdwBufferLength; DWORDdwBytesUsed; DWORDdwTimeCaptured; DWORDdwUser; DWORDdwFlags; DWORD_PTRdwReserved[4]; } VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR; 看到结构体中第一个参数了么?这个就是我们想要的桢数据的指针!后面参数,包括缓冲区长度等。 (10)终于得到了缓冲区的数据,可是,又一个问题出现了,缓冲区中的数据到底具体是啥含义啊? 这桢图像多大啊?size 是多少乘多少的啊?就是我们想要的像素信息么? 好的,我先告诉你,缓冲区中全部是像素信息,我们照着我的步骤这样做,其实是默认了一些参数,包括图像的 长度,宽度,色彩数,等等,那么,这个默认的值是多少呢? (11)用一下capGetVideoFormat宏吧,你会得到想要的东西。 在BOOL CGraspDlg::OnInitDialog()中继续添加如下代码: BITMAPINFO bmpInfo; capGetVideoFormat(m_hVideo,&bmpInfo,sizeof(BITMAPINFO)); BITMAPINFO结构体内容自己看MSDN.定义如下 typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUADbmiColors[1]; } BITMAPINFO, *PBITMAPINFO; 而BITMAPINFOHEADER定义如下: typedef struct tagBITMAPINFOHEADER{ DWORDbiSize; LONGbiWidth; LONGbiHeight; WORDbiPlanes; WORDbiBitCount; DWORDbiCompression; DWORDbiSizeImage; LONGbiXPelsPerMeter; LONGbiYPelsPerMeter; DWORDbiClrUsed; DWORDbiClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 加入步骤(11)的两句话,调试运行,我发现,bmpInfo.bmiHeader.biWidth为320,就是采集的图像宽度; bmpInfo.bmiHeader.biHeight为240,就是采集的图像高度,这些都是默认值,也可以改变这些值,通过 capSetVideoFormat宏来实现。 (12)那么,我们在步骤(9)中,回调函数第二个参数对应的结构体VIDEOHDR中,图像数据缓冲区的大小 dwBufferLength是多少呢?我们可以在回调函数中加一个MessageBox函数,输出这个值, 我们就可以发现,为230400,这个数很好,正好等于图像宽度X图像高度的3倍, 也就是说,是图像像素数目的3倍,这就对了,每个像素用3个字节存储的嘛。好啦,我们知道了,桢缓冲区中,存储 的完全是图像的像素信息,那么,具体哪个值对应哪个像素呢? 存储顺序是这样的:先从图像最下面一行开始,从左向右,依次存储,每一个像素用连续的3个字节,分别为B(蓝色分量), G(绿色分量),R(红色分量)。然后存储倒数第二行,仍然按照图像从左向右存储,然后倒数第三行, 倒数第四行。。。。。。等等,最后存储正数第一行。 事件委托和委托有啥区别啊?委托不是就够了吗?为什么还要事件委托呢 最佳答案 1. 事件在本类型外部只能用“+=”和“-=”去订阅/取消订阅代理, 委托不管在本类型外部还是内部都可以用“+=”、“-=”和“=”订阅/取消订阅代理。 2. 事件只能在本类型内部“触发”, 委托不管在本类型内部还是外部都可以“调用”。 即:事件,只有本类才能激发这个事件,如果用委托取代的话,可想而知,举个例子,按钮的Click事件,只有你的鼠标点击按钮才能由按钮触发,如果Click是委托的话,不管鼠标点击不单击那个按钮,我只要用程序调用这个委托,就可以使得按钮激发Click事件,完全不符合事实,
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 教育专区 > 小学其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服