本次试验主要是针对AVI的处理,了解AVI的基本概念,并且掌握AVI文件常用的程序读写方法。知道AVI视频文件的帧的读取方法,以及了解BMP和AVI的基本关系。

本文作者:i春秋签约作家——天天

一 AVI的基本概念

1. 什么是AVI

AVI是音频视频交错(Audio Video Interleaved)的英文缩写,它是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先用于Microsoft Video for Windows (简称VFW)环境,现在已被Windows 95/98、OS/2等多数操作系统直接支持。

2. AVI格式的优缺点

图像质量好、可跨平台使用、体积庞大、压缩标准不统一。

3. AVI的文件结构

AVI是符合RIFF文件规范的数字音频与视频文件格式,在介绍AVI文件前,我们要先来看看RIFF文件结构。AVI文件采用的是RIFF文件结构方式,RIFF(Resource Interchange File Format,资源互换文件格式)是微软公司定义的一种用于管理windows环境中多媒体数据的文件格式,波形音频wave,MIDI和数字视频AVI 都采用这种格式存储。构造RIFF文件的基本单元叫做数据块(Chunk),每个数据块包含3个部分:4字节的数据块标记(或者叫做数据块的ID)、数据块的大小、数据。整个RIFF文件可以看成一个数据块,其数据块ID为RIFF,称为RIFF块。

一个RIFF文件中只允许存在一个RIFF块。RIFF块中包含一系列的子块,其中有一种字块的ID为”LIST”,称为LIST,LIST块中可以再包含一系列的子块,但除了LIST块外的其他所有的子块都不能再包含子块。RIFF和LIST块分别比普通的数据块多一个被称为形式类型(Form Type)和列表类型(List Type)的数据域,其组成为: 4字节的数据块标记(Chunk ID)、数据块的大小、4字节的形式类型或者列表类型、数据。

下面我们看看AVI文件的结构。AVI文件是目前使用的最复杂的RIFF文件,它能同时存储同步表现的音频视频数据。AVI的RIFF块的形式类型是AVI,它包含3个子块,如下所述:

(1)信息块,一个ID为”hdrl”的LIST块,定义AVI文件的数据格式。

(2)数据块,一个ID为 “movi”的LIST块,包含AVI的音视频序列数据。

(3)索引块,ID为 “idxl”的子块,定义 “movi”LIST块的索引数据,是可选块。

AVI文件的结构如下图所示,下面将具体介绍AVI文件的各子块构造。

模糊测试之AVI文件分析-LMLPHP

AVIBMP的关系

一个AVI文件是由多个BMP文件构成。如下图:

模糊测试之AVI文件分析-LMLPHP

AVI视频序列中的“帧”即为位图。

AVI视频序列中的“帧”的数量和位图数量是对应的。

三.分析analysis目录里AVI操作函数的写法

目录 “Easy_BMP2AVI”   

在此目录下运行如下:

模糊测试之AVI文件分析-LMLPHP

上图的一些命令是用来合成avi的,在一下函数的调用:
cvLoadImage:从文件中读取图像 。
cvCreateVideoWriter:创建视频文件写入器 。
cvWriteFrame:写入一帧到一个视频文件中。
cvGetCaptureProperty:获得视频获取结构的属性,如视频序列的帧数等。
cvCaptureFromFile 为从文件中读取而打开文件
cvQueryFrame从摄像头或者文件中抓取并返回一帧
可以很方便的把BMP合成AVI。一下是合成的过程:

模糊测试之AVI文件分析-LMLPHP

最终可以打开结果看一下:

模糊测试之AVI文件分析-LMLPHP

.OpenCV 及MFC实现AVI的合成和分解

1. 创建一个MFC对话框应用程序(Dialog-based Application),在名称栏输入创建项目的名称,点击“确定”。

2. 在出现的“MFC应用程序向导”对话框内,选择“基于对话框”,并取消“使用Unicode库(N)”其他选项不做修改,单击“下一步”如下图:

模糊测试之AVI文件分析-LMLPHP

3. 一直点击“下一步”到“生成的类”对话框,选择基类为“CDialog”单击完成即可创建一个MFC对话框。如下图:

模糊测试之AVI文件分析-LMLPHP

4. 删除“TODO:在此放置对话框控件。”、“确定”和“取消”控件。然后点击“工具”中的“Button”添加以下五个控件并改名。结果如下下图:

模糊测试之AVI文件分析-LMLPHP

5. 接着点击属性栏中的“控制事件“按钮 ,弹出如下对话框:

模糊测试之AVI文件分析-LMLPHP

选择模糊测试之AVI文件分析-LMLPHP分别添加OnBnClickedButton1()•••OnBnClickedButton5()

6. 在工具箱中点击模糊测试之AVI文件分析-LMLPHP在图中添加后如下图:

模糊测试之AVI文件分析-LMLPHP

7. 在工具箱中点击“Group Box” ,从左上角往右下角拖拉到 “开始分解”止,同时修改其属性栏里的 的“静态”为“AVI的合成”,完成后如图:

模糊测试之AVI文件分析-LMLPHP

同理可得完成下一部分:

模糊测试之AVI文件分析-LMLPHP

8. 依次右击“示例编辑框”按钮分别添加变量,如图:

模糊测试之AVI文件分析-LMLPHP

变量名分别为m_1, m_2,  m_3,  m_4。

9. 在“解决方案资源管理器下”打开“BY_MFC_YJCDlg.h”,在“#pragma one”下面添加:

#include “cv.h”

#include “highgui.h”

#include “cxcore.h”

#include <stdio.h>

#include “shlwapi.h”

#include <direct.h>

#include <io.h>

#include <vector>

using namespace std;

#define UM_PROGRESS WM_USER+1

10. 在最后的“}”内添加:

afx_msg LRESULT Onrogress(WPARAM, LPARAM);

public:

char* choisebmppath(HWND hWnd,CHAR *szTitle, CHAR *szPath);

public:

int bmptoavi();

public:

afx_msg void OnBnClickedButton10();

void GetFile(CString sPath, vector<CString> *filePaths);

CString getFileExt(CString filePath);

BOOL __cdecl isSameType (char *Ext,const char *format, …);

11. 打开源文件“BY_MFC_YJCDlg.cpp”,在“#ifdef_DEBUG”上添加:

#include <stdlib.h>

在 “#endif”下添加:

CString bmp_path1;

CString bmp_path;//bmp路径(hecheng)

CString AviFileName;//AVIL路径

CString AVI_path;

int i = 1;

//int bmptoavi(LPVOID lpParameter);//shengming

CEdit dlg;

12. 在“END_MESSAGE-MAP()”的上面和

模糊测试之AVI文件分析-LMLPHP

下面添加:

ON_MESSAGE(UM_PROGRESS,Onrogress)

13. 在模糊测试之AVI文件分析-LMLPHP下面添加:

char* COPENCV_AVI_BMPDlg::choisebmppath(HWND hWnd,CHAR *szTitle, CHAR *szPath)//获得文件夹

{

BROWSEINFO bi;

LPCITEMIDLIST pItemIDList;

bi.hwndOwner = AfxGetMainWnd()->GetSafeHwnd();

bi.pidlRoot = NULL;

bi.pszDisplayName = szPath;

bi.lpszTitle = szTitle;

bi.ulFlags = BIF_RETURNONLYFSDIRS;

bi.lpfn = NULL;

bi.iImage = 0;

pItemIDList = SHBrowseForFolder(&bi);

if(NULL == SHGetPathFromIDList(pItemIDList, szPath) )

{

AfxMessageBox(“路径为获取“);

}

return (char*)szPath;

}

14. 在OnBnClickedButton2()下添加:

if (0 == m_1.GetWindowTextLength())

{

AfxMessageBox(“please CHOISE bmp_filename!”);

exit(1);

}

if (0 == m_1.GetWindowTextLength())

{

AfxMessageBox(“please INPUT bmp_filename!”);

exit(1);

}

//AfxBeginThread((AFX_THREADPROC)bmptoavi, (void*)this);

PostMessage(UM_PROGRESS);

bmptoavi();

}

int COPENCV_AVI_BMPDlg::bmptoavi()

{

CString FileName;

CvVideoWriter *writer;

IplImage *frame;

vector<CString> imgFilePaths;

GetDlgItemText(IDC_EDIT1,bmp_path);//BMP的文件路径

GetFile(bmp_path, &imgFilePaths);

for (int i=0 ;i< imgFilePaths.size();++i)

{

frame = cvLoadImage(imgFilePaths.at(i).GetBuffer());

if(i == 0)

{

int AviForamt = -1;//设置压缩格式

int FPS = 25;

int AviColor = 1;

writer=cvCreateVideoWriter(AviFileName,-1,FPS,cvGetSize(frame),AviColor);

}

cvWriteFrame(writer,frame);

cvWaitKey(1);

cvReleaseImage(&frame);

}

cvReleaseVideoWriter(&writer);

return 0;

}

/*

* 功能:获取文件夹下的所有图片文件

*/

void COPENCV_AVI_BMPDlg::GetFile(CString sPath, vector<CString> *filePaths)

{

if (sPath.IsEmpty())

{

return;

}

CFileFind   ff;

CStringszDir = sPath;

CStringExt;

if(szDir.Right(1) != “\\”)

szDir += “\\”;

szDir += “*.*”;

BOOL res = ff.FindFile(szDir);

while( res )

{

res = ff.FindNextFile();

CString sFileName;

if (ff.IsDirectory() && !ff.IsDots())//文件夹

{

CString sFilePath = ff.GetFilePath();

sFileName = ff.GetFileTitle();

GetFile(sFilePath, filePaths);

}

else if (!ff.IsDirectory() && !ff.IsDots())//文件

{

CString   strFilePath = ff.GetFilePath();

Ext = getFileExt(ff.GetFilePath());

if (isSameType(Ext.GetBuffer(),“ss”,“bmp”,“jpg”))

{

filePaths->push_back(strFilePath);

}

}

}

ff.Close();

}

CString COPENCV_AVI_BMPDlg::getFileExt(CString filePath)

{

filePath.ReleaseBuffer();

int pos = filePath.ReverseFind(‘.’);

if (pos >= 0)

{

return filePath.Right(filePath.GetLength() – pos -1);

}

}

BOOL __cdecl COPENCV_AVI_BMPDlg::isSameType (char *Ext,const char *format, …)

{

va_list ap;

intbuffing;

intretval;

charch;

char*ext;

va_start(ap, format);

_ASSERTE(format != NULL);

ext = strlwr(Ext);

while(ch = *(format++))

{

switch(ch)

{

case ‘s’:

{

char *p = va_arg(ap, char *);

if (0 == strcmp(Ext,p))

{

return 1;

}

break;

}

default:

{

printf(“input parameter is error \n”);

return -1;

}

break;

}

}

return(0);

15. 在OnBnClickedButton1()下添加:

char path[MAX_PATH];

choisebmppath(m_hWnd,“bmp文件夹“,path);

bmp_path = path;

SetDlgItemText(IDC_EDIT1,bmp_path);

AviFileName = “E:\\avi.avi”;

SetDlgItemText(IDC_EDIT2,“E:\\avi.avi”);

16. 在OnBnClickedButton3()下添加:

CFileDialog dlg(true,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,NULL,NULL,0);

if(!dlg.DoModal())

{

AfxMessageBox(“创建失败“);

exit(1);

}

AVI_path = dlg.GetPathName();

SetDlgItemText(IDC_EDIT3,AVI_path);

}

LRESULT  COPENCV_AVI_BMPDlg::Onrogress(WPARAM, LPARAM)

{

pgctrl.OffsetPos(2);

pgctrl.SetStep(1);//设置步长

pgctrl.SetRange32(1,1000);

pgctrl.SetPos(i);

pgctrl.SetBkColor(RGB(255, 0, 0));

pgctrl.StepIt();//让进度条动起来

return 1;

17. 在OnBnClickedButton4()下添加:

if (m_3.GetWindowTextLength() == 0)

{

AfxMessageBox(“AVI文件出错“);

exit(1);

}

CvCapture *capture;

IplImage *frame;

capture = cvCaptureFromAVI(AVI_path);

CString FileName;

CString strtemp;

CString newFileName;

int i=1;

UpdateData(FALSE);

for (i = 1;i<101;i++)

{

FileName.Format(“Frame-%03d.bmp”, i);

if (i==1)

{

GetDlgItemText(IDC_EDIT4,strtemp);

}

newFileName = strtemp + FileName;

frame = cvQueryFrame(capture);

if (!cvSaveImage( newFileName,frame))

{

AfxMessageBox(“提取失败“);

}

cvWaitKey(1);

}

}

18. 然后修改相应位置的所有“COPENCV-AVI-BMPDlg”为“C(工程名)Dlg”。并且在“项目属性”的“附加依赖项”添加 “cv.lib highgui.lib cvaux.lib cxcore.lib”运行 得:

模糊测试之AVI文件分析-LMLPHP

五.实验心得与总结

本次试验主要是针对AVI的处理,了解AVI的基本概念,并且掌握AVI文件常用的程序读写方法。
   知道AVI视频文件的帧的读取方法,以及了解BMP和AVI的基本关系。

由于试验目的需要对一些代码的理解,对C++有更深度的认识,建议各位同学多看一些有关C++的内容。以更深一步的理解Opencv的机制与功能,将Opencv透彻的系统学习,加深对其的理解。

05-26 15:45