本次试验主要是针对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与BMP的关系
一个AVI文件是由多个BMP文件构成。如下图:
AVI视频序列中的“帧”即为位图。
AVI视频序列中的“帧”的数量和位图数量是对应的。
三.分析analysis目录里AVI操作函数的写法
在此目录下运行如下:
上图的一些命令是用来合成avi的,在一下函数的调用:
cvLoadImage:从文件中读取图像 。
cvCreateVideoWriter:创建视频文件写入器 。
cvWriteFrame:写入一帧到一个视频文件中。
cvGetCaptureProperty:获得视频获取结构的属性,如视频序列的帧数等。
cvCaptureFromFile 为从文件中读取而打开文件
cvQueryFrame从摄像头或者文件中抓取并返回一帧
可以很方便的把BMP合成AVI。一下是合成的过程:
最终可以打开结果看一下:
四.OpenCV 及MFC实现AVI的合成和分解
1. 创建一个MFC对话框应用程序(Dialog-based Application),在名称栏输入创建项目的名称,点击“确定”。
2. 在出现的“MFC应用程序向导”对话框内,选择“基于对话框”,并取消“使用Unicode库(N)”其他选项不做修改,单击“下一步”如下图:
3. 一直点击“下一步”到“生成的类”对话框,选择基类为“CDialog”单击完成即可创建一个MFC对话框。如下图:
4. 删除“TODO:在此放置对话框控件。”、“确定”和“取消”控件。然后点击“工具”中的“Button”添加以下五个控件并改名。结果如下下图:
5. 接着点击属性栏中的“控制事件“按钮 ,弹出如下对话框:
选择分别添加OnBnClickedButton1()•••OnBnClickedButton5()
6. 在工具箱中点击在图中添加后如下图:
7. 在工具箱中点击“Group Box” ,从左上角往右下角拖拉到 “开始分解”止,同时修改其属性栏里的 的“静态”为“AVI的合成”,完成后如图:
同理可得完成下一部分:
8. 依次右击“示例编辑框”按钮分别添加变量,如图:
变量名分别为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()”的上面和
下面添加:
ON_MESSAGE(UM_PROGRESS,Onrogress)
13. 在下面添加:
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;
CString szDir = sPath;
CString Ext;
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;
int buffing;
int retval;
char ch;
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的处理,了解AVI的基本概念,并且掌握AVI文件常用的程序读写方法。
知道AVI视频文件的帧的读取方法,以及了解BMP和AVI的基本关系。
由于试验目的需要对一些代码的理解,对C++有更深度的认识,建议各位同学多看一些有关C++的内容。以更深一步的理解Opencv的机制与功能,将Opencv透彻的系统学习,加深对其的理解。