用DirectShow抓取影片帧

用DirectShow抓取影片帧
作者:youken

下载本文示例源代码

很多视频播放软件或视频编辑软件都提供了抓帧的功能,利用这类软件,视频工作者可以很轻松地将一个电影某一时刻的帧抓取出来并保存为图片文件,那么,我们如何自己编程实现这样的功能呢?如果你熟悉MPEG或者AVI等常见视频格式,你可以直接对影片文件进行操作,如果你不知道这些视频格式,而希望使用更简单的方法来抓取影片的帧,微软的DirectShow将会给你极大的惊喜。
DirectShow属于DirectX家族(DirectX还包括Direct3D、DirectInput、DirectDraw、DirectSound等组件),在使用DirectShow开发抓取帧的程序前,你必须要安装DirectX SDK,这个开发包可以在微软的网站上下载得到,目前最新版本是9.0b;另外,由于DirectX SDK是用COM的方式发布的,所以对于开发人员而言,他还必须要了解COM的基本原理。如果大家没有使用过COM,可以先从网上找一些COM方面的入门教程看看,VC知识库上就有很多好文章,推荐VC知识库第九期赵湘宁的《COM编程入门》系列。

一、编程工具的设置:

先说说我使用的VC 6.0的设置,一般而言,安装完DirectX 9.0b SDK后,会自动设置好VC,用户无需手动干预,如果编译过程中出现错误,请检查VC是否包含了DirectX SDK的头文件和库文件,方法是选择菜单“Tools->Options…”,在弹出的Options对话框中选择Directories选项卡,看看Include files和Library files中是否包含有DirectX SDK的Include路径和Lib路径,如果没有,将这两个路径添加上去即可。

二、主要的实现步骤:

在实现抓取影片帧的过程中,DirectShow的IMediaDet接口将是主角,这个接口包含了一些方法能够从媒体源文件中提取一些重要信息,比如媒体类型、帧速率甚至是视频流的单个帧。
·注意
要正确使用IMediaDet接口,工程中需要包含下列文件:
头文件:dshow.h, qedit.h
库文件:strmiids.lib
因为使用CComPtr模板来声明接口实例,所以还要在工程中包含atlbase.h头文件。
下面我们将一步步利用IMediaDet接口实现抓取影片帧的功能。

第一步:新建一个基于对话框的应用程序,为应用程序添加两个编辑控件和三个按钮控件,程序界面如图所示:



第二步:为对话框类添加一个HRESULT类型的成员函数GrabFramFromMovie,它将实现抓帧功能。在函数体内创建IMediaDet接口实例,创建实例需要调用CoCreateInstance函数,并给函数的第一个参数传入CLSID_MediaDet类标识符。

第三步:调用IMediaDet::put_Filename方法为接口指定一个媒体文件,该方法只有一个参数,这个参数描述了媒体文件的路径,注意参数类型为BSTR。

第四步:调用IMediaDet::get_OutputStreams方法以得到影片输出流的数目,一个影片的输出由多个流组成,但是get_OutputStreams方法只关心影片输出的视频流和音频流而自动忽略其它流,所以,如果一个影片输出包含有视频流、音频流和数据流,get_OutputStreams只返回视频流和音频流的数目。

第五步:调用IMediaDet::put_CurrentStream方法指定一个用于编辑和操作的流,因为我们的目的是要将影片的单个帧保存为图片,这就需要对视频流进行操作,所以要利用put_CurrentStream定位影片文件输出的视频流。

第六步:调用IMediaDet::get_StreamMediaType方法得到一个VIDEOINFORHEADER结构,这个结构与当前指定的视频流关联。VIDEOINFORHEADER结构中包含有一个BITMAPINFORHEADER结构类型的成员,它描述了视频影像对应位图的尺寸、颜色等有用的信息。

第七步:调用IMediaDet::WriteBitmapBits方法将影片的帧保存为位图,若想指定保存哪一帧,只需要给第一个参数传递一个合适的时间即可。这里,我传递给第一个参数的时间为0,因此程序将保存影片第一帧的位图。

下面是GrabFramFromMovie函数的完整代码,其中,变量m_editOpenDir和m_editSaveDir分别指定了影片路径及保存的位图路径,请对照上面的步骤阅读:

HRESULT CFrameGrabberDlg::GrabFrameFromMovie()
{
	HRESULT hr;
	// 定义IMediaDet接口实例
	CComPtr< IMediaDet > pDet;
	hr = CoCreateInstance( CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, 
		IID_IMediaDet, (void**) &pDet );
	if (FAILED(hr))
		return hr;
	
	// 将影片文件名转换成BSTR类型
	CComBSTR openBSTR(m_editOpenDir);
	// 设置IMediaDet接口的文件关联
	hr = pDet->put_Filename(openBSTR);
	if (FAILED(hr))
		return hr;
	
	// 从影片中检索视频流和音频流
	long lStreams;
    hr = pDet->get_OutputStreams(&lStreams);
    if (FAILED(hr))
		return hr;
	
	// 取出影片的视频流,因为帧的信息是保存在视频流中的
	bool bFound = false;
	for (int i=0; i<lStreams; i++)
	{
		GUID major_type;
		hr = pDet->put_CurrentStream(i);
		if (SUCCEEDED(hr))
			hr = pDet->get_StreamType(&major_type);
		if (FAILED(hr))
			break;
		if (major_type == MEDIATYPE_Video)
		{
			bFound = true;
			break;
		}
	}
	if (!bFound)
		return VFW_E_INVALIDMEDIATYPE;
	
	long width = 0, height = 0; // 存储位图的宽和高(单位:象素)
    AM_MEDIA_TYPE mt;
    hr = pDet->get_StreamMediaType(&mt);
    if (SUCCEEDED(hr))
    {
        if ((mt.formattype == FORMAT_VideoInfo) && 
            (mt.cbFormat >= sizeof(VIDEOINFOHEADER)))
        {
			// 得到VIDEOINFOHEADER结构指针,VIDEOINFOHEADER结构包含一些与视频
			// 有关的信息,其中含有BITMAPINFORHEADER结构
            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;
        }
        else
            hr = VFW_E_INVALIDMEDIATYPE;
        MyFreeMediaType(mt); // 释放AM_MEDIA_TYPE结构
    }
    if (FAILED(hr))
		return hr;
	
	CComBSTR saveBSTR(m_editSaveDir);
	// 将第一帧保存为指定路径的位图文件
	hr = pDet->WriteBitmapBits(0, width, height, saveBSTR);
	if (FAILED(hr))
		return hr;

	return S_OK;
}
三、程序运行:

程序运行后,选择一个影片,然后指定保存路径,点击“抓取”按钮,就可以将影片第一帧保存到指定路径下,我们也可以修改IMediaDet接口的WriteBitmapBits方法中的第一个参数来保存我们指定的帧。源代码的DEBUG文件夹下包含了一个测试影片,供测试使用。该程序在Windows XP、Visual C++ 6和DirectX 9.0b环境下编译并运行通过。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 AForge.Video.DirectShow 中使用 VB.NET 减少 NewFrame 的帧数和分辨率的方法如下: 1. 实例化 VideoCaptureDevice 类,并使用 DesiredFrameSize 和 DesiredFrameRate 属性设置分辨率和帧率。 2. 在 NewFrame 事件处理程序中,可以使用 ResizeBilinear 方法调整图像的大小。 示例代码: ``` Dim videoSource As New VideoCaptureDevice(videoDevices(selectedDevice).MonikerString) videoSource.DesiredFrameSize = New Size(320, 240) videoSource.DesiredFrameRate = 10 AddHandler videoSource.NewFrame, AddressOf video_NewFrame Private Sub video_NewFrame(sender As Object, eventArgs As NewFrameEventArgs) Dim img As Bitmap = DirectCast(eventArgs.Frame.Clone(), Bitmap) Dim imgSmall As Bitmap = New Bitmap(320, 240) Using g As Graphics = Graphics.FromImage(imgSmall) g.InterpolationMode = InterpolationMode.HighQualityBilinear g.DrawImage(img, New Rectangle(0, 0, 320, 240)) End Using img.Dispose() PictureBox1.Image = imgSmall End Sub ``` ### 回答2: 在VB.NET中减少AForge.Video.DirectShow库中的NewFrame帧数和分辨率的方法如下: 1. 减少帧数: 使用AForge.Video.DirectShow库,可以通过设置设备的帧率来减少NewFrame的帧数。可以通过VideoCaptureDevice类的属性来实现。首先实例化VideoCaptureDevice对象,然后使用该对象的Property集合中的FrameRate属性来设置帧率。例如,可以将帧率设置为15fps(每秒15帧): ``` Dim videoPlayer As New VideoCaptureDevice(videoDevices(videoDeviceIndex).MonikerString) videoPlayer.DesiredFrameRate = 15 ``` 2. 减少分辨率: 减少AForge.Video.DirectShow库中的NewFrame的分辨率可以通过设置VideoCaptureDevice类的Resolution属性来实现。首先实例化VideoCaptureDevice对象,然后使用该对象的Property集合中的Resolution属性来设置分辨率。例如,可以将宽度设置为640像素,高度设置为480像素: ``` Dim videoPlayer As New VideoCaptureDevice(videoDevices(videoDeviceIndex).MonikerString) videoPlayer.DesiredFrameSize = New Size(640, 480) ``` 通过上述方法,可以减少AForge.Video.DirectShow库中NewFrame的帧数和分辨率。这将有助于在处理视频时减少计算资源的消耗,并提高程序的运行效率。 ### 回答3: 在使用VB.NET的AForge.Video.DirectShow库时,我们可以通过减少NewFrame方法中的帧数和分辨率来实现。具体步骤如下: 1. 减少帧数:可以使用VideoSource类中的DesiredFrameRate属性来设置希望的帧率。默认情况下,帧率是最大可能的。我们可以通过将此属性设置为较低的值来降低帧数,例如: ``` Dim videoSource As New VideoCaptureDevice(videoDevices(comboBoxSelectionIndex).MonikerString) videoSource.DesiredFrameRate = 10 '设置为10帧每秒 ``` 2. 减小分辨率:可以使用VideoSource类中的DesiredFrameSize属性来设置希望的分辨率。默认情况下,分辨率是最大可能的。我们可以通过将此属性设置为较小的值来降低分辨率,例如: ``` Dim videoSource As New VideoCaptureDevice(videoDevices(comboBoxSelectionIndex).MonikerString) videoSource.DesiredFrameSize = New Size(640, 480) '设置为640x480分辨率 ``` 需要注意的是,具体可用的帧数和分辨率取决于摄像头设备的支持。我们可以使用VideoCaptureDevice类中的SupportedFrameSizes和SupportedFrameRates属性来获取设备支持的帧数和分辨率选项,然后根据需要进行设置。 总结:通过设置VideoSource类中的DesiredFrameRate属性和DesiredFrameSize属性,我们可以减少AForge.Video.DirectShow库中NewFrame方法的帧数和分辨率。这有助于在需要节省资源或降低数据处理难度的情况下进行实时视频处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值