3--怎样从视频影像文件中提取帧(参考模板)

发布时间:2021-03-11

如不慎侵犯了你的权益,请联系告知!
怎样从视频影像文件中提取指定的帧
武汉工业学院 计算机与信息工程系 管庶安
在视频图像识别中,首先要解决的问题是从视频文件(如MPEG1~4AVI等)中提取任一时刻的一帧图像,简称为帧。以便分析该帧中感兴趣的目标。Microsoft DirectShow提供了这一功能。
1 DirectShow概述
DirectShow属于DirectX家族中的组件之一DirectX还包括Direct3DDirectInputDirectDrawDirectSound等组件)在使用DirectShow前,必须安装DirectX SDK开发包。这里推荐安装的版本是DirectX8.0 SDK,可以从微软的网站上下载得到。安装前,你的计算机上应已安装了VC++6.0
安装后,将在C盘的根目录下生成一个mssdk目录,该目录下有lib子目录(含DirectX的函数库)include子目录(含头文件)samples子目录(各种源程序例子)doc子目录(使用说明文挡)等。同时,还会自动在VC++系统中设置好DirectX SDKInclude路径Lib路径,你可以启动VC查看。方法是选择菜单“Tools/Options…”,在弹出的Options对话框中选择Directories选项卡,看看Include filesLibrary files中是否包含有DirectX SDKInclude路径和Lib路径。
由于DirectX SDK是用COM的方式发布的,所以还 应对COM的基本原理有所了解。关于此,你可以从下面 的实现步骤中领悟。
2 实现步骤
21 建立MFC AppWizard[exe]工程。
以下假设工程名为Mpg。在第1步选Single document (单文档,第2~5步选默认设置,第6 步的Base Class CformView,完成工程的设置。上述操作中,未提及的 选项一律取默认值。于是,MFC为我们建立了如右图所示 的文件。
22 创建IMediaDet接口实例
为了实现用DirectShow抓帧,要使用DirectShowIMediaDet接口。因此工程中需要包含dshow.hqedit.hatlbase.h文件及其库文件。请按如下方式操作:
1)在MpgView.h文件的class CGrabView : public CformView前添加(粗体字为自己添加,以下同)
……
#include "dshow.h" #include "qedit.h" #include "atlbase.h"
class CGrabView : public CFormView {……
2)选Project/Setings菜单,在弹出的Project Setings对话框中选Link卡,在1 / 7
如不慎侵犯了你的权益,请联系告知!
Object/library modules栏填写
2 / 7
如不慎侵犯了你的权益,请联系告知!
Strmiids.lib,点击OK按钮。

3)定义在程序中要用到的DirectShow COM接口实例MediaDet
先在MpgView.cpp文件中定义MediaDet接口实例。然后,在CMpgView类的构造函数CMpgView:::CMpgView( 中对此接口进行初始化和创建。

……
IMediaDet *pDet=NULL; // 定义IMediaDet接口实例,并赋以空指针值 CMpgView::CMpgView(
: CFormView(CMpgView::IDD {……
// TODO: add construction code here CoInitialize(NULL; HRESULT hr;
hr = CoCreateInstance( CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, IID_IMediaDet, (void** &pDet ;
if (FAILED(hr { } ……
//创建失败
MessageBox("创建IMediaDet接口实例失败!","错误",MB_ICONSTOP|MB_OK;

//初始化COM接口
23 从视频文件中得到视频流信息
本步骤要作的工作是:
将待抓帧的影片文件与接口实例关联起来,使得以后调用接口函数操作,就是针对与之关联的影片文件进行的。
从之关联的影片文件中的视频流中提取抓帧所需要的信息,如帧图像的高宽、帧速率、影片流的持续时间、帧间隔时间等。
上述工作相对独立,可以设计一个函数来完成。将此函数命名为LoadMovie( ,并且还需要定义存储上述参数的变量。为此,
1)在MpgView.h文件中添加如下定义:
class CMpgView : public CFormView { …… // Attributes public:
long CurFrame; //当前正在处理第几帧CMpgDoc* GetDocument(; struct {

char double double MpegFile[200]; //影片文件名 Duration;
FrameRate; //影片播完需要的时间

//帧速率,即每秒播放多少帧
long TotalFrames; //影片中包含的总帧数
}MpgInfo;
BITMAPINFOHEADER BmpInfo; //帧图像为BMP格式,本处定义其信息头。 char *DibBit;
//存放一帧图像的像素数据,每一像素需要3个字节存放其GBR2 / 7
如不慎侵犯了你的权益,请联系告知!

BOOL LoadMovie(; //定义从视频文件中得到视频流信息的函数
……
2)在MpgView.cpp文件的CMpgView类构造函数中对上述变量赋初值:
CMpgView::CMpgView(
: CFormView(CMpgView::IDD {……
// TODO: add construction code here DibBit=(char*new char [40+921600]; strcpy(MpgInfo.MpegFile,""; BmpInfo.biSize=40; BmpInfo.biWidth=640; BmpInfo.biPlanes=1; //DWORD

//LONG

//LONG //WORD //DWORD
BmpInfo.biHeight=480; BmpInfo.biBitCount=24; BmpInfo.biCompression=0; BmpInfo.biSizeImage=0; //WORD //DWORD

BmpInfo.biXPelsPerMeter=2000; //LONG BmpInfo.biYPelsPerMeter=2000; //LONG BmpInfo.biClrUsed=0; CoInitialize(NULL; ……



//DWORD //DWORD
BmpInfo.biClrImportant=0;

//初始化COM接口
3)在MpgView.cpp文件的CMpgView类的析构函数中删除为DibBit申请的内存
CMpgView::~CMpgView( { delete[] DibBit; }
4)在MpgView.cpp文件后写出LoadMovie( 函数的实体
BOOL CMpgView::LoadMovie( {
HRESULT hr;
CComBSTR FileName(MpgInfo.MpegFile; //将影片文件名转换成BSTR型字符串 hr = pDet->put_Filename(FileName; if (FAILED(hr return(FALSE;

long Streams;
hr = pDet->get_OutputStreams(&Streams; //从影片中检索视频流和音频流 if (FAILED(hr return(FALSE;

hr = pDet->get_StreamLength(&MpgInfo.Duration; //得到影片流的持续时间(秒) if (FAILED(hr return(FALSE;

//因帧的信息保存在视频流中,故先取出视频流,再得到帧信息 BOOL bFound = FALSE;
for (int i=0; i //设置与IMediaDet接口关联的影片文件
3 / 7
如不慎侵犯了你的权益,请联系告知!
GUID major_type;


}
if (!bFound return(FALSE;

// 得到VIDEOINFOHEADER结构指针,该结构包含视频的信息,BITMAPINFORHEADER结构
bFound = TRUE; break; }
hr = pDet->put_CurrentStream(i;
if (SUCCEEDED(hr hr = pDet->get_StreamType(&major_type; //得到媒体类型 if (FAILED(hr break;
if (major_type == MEDIATYPE_Video{

if (FAILED(hr break;
MpgInfo.FrameRate=1.0/MpgInfo.FrameRate;
//计算帧间隔时间
MpgInfo.TotalFrames=(int(MpgInfo.Duration/MpgInfo.FrameRate+1; // //若媒体是视频
hr = pDet->get_FrameRate(&MpgInfo.FrameRate; //得到帧速率(帧/秒)
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt;
if (FAILED(hr return(FALSE;
if ((mt.formattype == FORMAT_VideoInfo && (mt.cbFormat >= sizeof(VIDEOINFOHEADER {
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*(mt.pbFormat; BmpInfo.biWidth = pVih->bmiHeader.biWidth; BmpInfo.biHeight = pVih->bmiHeader.biHeight;

// 释放AM_MEDIA_TYPE结构
if (mt.cbFormat != 0 {
CoTaskMemFree((PVOIDmt.pbFormat; mt.cbFormat = 0; mt.pbFormat = NULL;

}
if (mt.pUnk != NULL { mt.pUnk->Release(; mt.pUnk = NULL; }
}
return(TRUE; }
else return(FALSE;

24 抓帧的实现

1)建立“文件/打开” 项的点击响应函数,目的是打开一个影片文件,再调用LoadMovie( 函数获得此影片文件的信息,为抓帧作好准备。4 / 7
如不慎侵犯了你的权益,请联系告知!
View/Class Wizard,弹出如下对话框;在Class name栏选择CmpgView,在Object IDs栏选择ID_FILE_OPEN,在Messages栏选择COMMAND,点击Add Function按钮,点击OK按钮,于是,MFC自动在MpgView.cpp文件后生成响应函数OnFileOpen(在此函数中添加如下代码:

void CMpgView::OnFileOpen( { }
// TODO: Add your command handler code here char* pcsun="视频文件(*.mpg|*.mpg||";
CFileDialog fd (TRUE,NULL,0,OFN_OVERWRITEPROMPT,pcsun,NULL; if(fd.DoModal( == IDCANCEL return;
strcpy(MpgInfo.MpegFile,OpenDialog.GetPathName(; GetDocument(->SetTitle(MpgInfo.MpegFile;
GetDocument(->UpdateAllViews(NULL; //触发所有视图类的Update(函数 BOOL OK = LoadMovie(; if(!OK { }
CurFrame=0; //设当前帧为第0
MessageBox("无法抓取帧!请检查文件格式是否正确。","错误",MB_ICONSTOP|MB_OK;
return;


2)抓帧。MediaDet提供了抓取帧、保存帧的接口函数,其原型如下:
HRESULT GetBitmapBits(
double StreamTime, //Time at which to retrieve the poster frame.
long *pBufferSize, //Pointer to a variable that receives the required buffer size. char *pBuffer, //Pointer to a buffer that receives the poster frame. long Width, //Width of the poster frame image, in pixels. long Height //Height of the poster frame image, in pixels. ;
HRESULT WriteBitmapBits(
double StreamTime, //Time at which to retrieve the poster frame long Width, //Width of the poster frame image, in pixels.5 / 7
如不慎侵犯了你的权益,请联系告知!
long Height, //Height of the poster frame image, in pixels. BSTR Filename //Path of the file in which to save the poster frame. ;

现计划把影片中的所有帧都抓出来,每抓一帧,立即显示出来。操作如下:
●在MpgView.cpp文件的CMpgView::OnFileOpen( 函数中建立定时器,每隔120ms应一次:
void CMpgView::OnFileOpen( { ……
CurFrame=0; //设当前帧为第0 }
SetTimer(1,120,NULL; //定时抓帧
●建立定时器响应函数,实现定时抓帧和显示
void CMpgView::OnTimer(UINT nIDEvent
{ // TODO: Add your message handler code here and/or call default }

long s=0; CClientDC dc(this;
dc.SetStretchBltMode(COLORONCOLOR; ::StretchDIBits( dc.GetSafeHdc(,



0,0,320,240,
0,0,BmpInfo.biWidth,BmpInfo.biHeight, (LPBYTE&DibBit[40],(LPBITMAPINFO&BmpInfo, DIB_RGB_COLORS,SRCCOPY ;
pet->GetBitmapBits(CurFrame*0.12,&s,DibBit,BmpInfo.biWidth,BmpInfo.biHeight;
CurFrame++;
if(CurFrame>=MpgInfo.TotalFrames KillTimer(1; CFormView::OnTimer(nIDEvent;
运行程序,打开一个电影文件,可以看到播放效果。注意,常见的播放器并不是这样实现的。这里的意义在于我们捕捉到了帧,可以进一步逐帧分析感兴趣的目标,尤其是运动的目标。


(本资料素材和资料部分来自网络,仅供参考。请预览后才下载,期待您的好评与关注!)


6 / 7

3--怎样从视频影像文件中提取帧(参考模板)

相关推荐