Opencv应用视频文件的打开及简单处理
院系:信息科学与技术学院
专业:电子信息工程
姓名:赵陈威
学号:2005160156
Opencv应用视频文件的打开及简单处理
赵陈威
(信息科学与技术学院电子信息工程专业 2005160156)
摘要:针对视频打开和处理这一具体应用用Opencv设计一个处理系统,简要介绍Opencv的安装、配置及其具体应用的开发过程及开发过程中遇到的问题以及解决方案。
关键字:安装 配置 设计方法 遇到的问题及其解决方案
目前,数字图像处理在国民生产中起到日益重要的作用,对其的研究也日益广泛和深入。OpenCV程序库作为数字图像研究的软件助手,具有简单易用、功能强大、移植方便等优越性能。OpenCV是Intel开源计算机视觉库(Open Computer Vision)的简称。它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV 拥有包括 300 多个C函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也可以使用某些外部库。OpenCV 对非商业应用和商业应用都是免费(FREE)的。OpenCV 为Intel® Integrated Performance Primitives (IPP) 提供了透明接口。 这意味着如果有为特定处理器优化的的 IPP 库, OpenCV 将在运行时自动加载这些库。
Opencv相当于一个含有丰富函数的程序库,当要处理图像及视频的处理时VC++将调用Opencv里的库函数直接进行处理。所谓程序库,一般是软件作者为了发布方便、替换方便或二次开发目的,而发布的一组可以单独与应用程序进行compile time或runtime链接的二进制可重定位目标码文件。通俗一点说,所谓一个库,就是一个文件,这个文件可以在编译时由编译器直接链接到可执行程序中,也可以在运行时由操作系统的runtime enviroment根据需要动态加载到内存中。一组库,就形成了一个发布包,当然,具体发布多少个库,完全由库提供商自己决定。Opencv里的程序库包含静态库和动态库两种:所谓静态库,就是在静态编译时由编译器到指定目录寻找并且进行链接,一旦链接完成,最终的可执行程序中就包含了该库文件中的所有有用信息,包括代码段、数据段等。所谓动态库,就是在应用程序运行时,由操作系统根据应用程序的请求,动态到指定目录下寻找并装载入内存中,同时需要进行地址重定向。 win32平台下,静态库通常后缀为.lib,动态库为.dll linux平台下,静态库通常后缀为.a,动态库为.so 从本质上来说,由同一段程序编译出来的静态库和动态库,在功能上是没有区别的。不同之处仅仅在于其名字上,也就是“静态”和“动态”。由上面的介绍不难看出,相对于动态库,静态库的优点在于直接被链接进可执行程序中,之后,该可执行程序就不再依赖于运行环境的设置了(当然仍然会依赖于 CPU指令集和操作系统支持的可执行文件格式等硬性限制)。而动态库的优点在于,用户甚至可以在程序运行时随时替换该动态库,这就构成了动态插件系统的基础。具体使用静态库和动态库,由程序员根据需要自己决定。
下面开始opencv的安装与配置:
首先在目录D:\\Program Files下安装opencv安装目录为D:\\Program Files\\OpenCV(在安装时选择\"将\\OpenCV\\bin加入系统变量\"(Add\\OpenCV\\bin to the systerm PATH))。
完成安装然后要进行opencv的配置。配置十分重要,如果配置不好程序运行时将无法正确的调用正确的头文件及各种库函数,那么程序将无法运行。首先要配置Windows环境变量:检查D:\\Program Files\\OpenCV\\bin是否已经被加入到环境变量PATH,如果没有,手动加入。加入后需要注销当前Windows用户(或重启)后重新登陆才生效。如下图所示
然后进行对Visual C++ 6.0的配置:打开Visual C++ 6.0选择菜单工具->选项->目录:先设置lib路径,选择Library files,在下方路径中填入路径d:\\Program Files\\Opencv\\lib,如图所示:
然后选择include files,在下方填入路径:(如图所示)
d:\\Program Files\\Opencv\\cxcore\\include
d:\\Program Files\\Opencv\\cv\\include
d:\\Program Files\\Opencv\\cvaux\\include
d:\\Program Files\\Opencv\\otherlibs\\highgui
d:\\Program Files\\Opencv\\otherlibs\\include
另外,还要进行项目设置:每创建一个将要使用OpenCV的VC Project,都需要给它指定需要的lib。创建一个工程之后,选择工程->设置,然后将“设置”选为“所有配置”,然后选择右边的连接标签,在对象/库模块附加上cxcore.lib cv.lib cvaux.lib highgui.lib cvcam.lib(如果不需要这么多lib,可以只添加你需要的lib)。如图所示:
以上就完成了Opencv的所以安装与配置,下面将介绍具体应用视频的打开及简单处理(运动检测)的设计方法、遇到的问题及其解决方案。
首先用VC6.0的MFC建立一个对话框程序,设工程名为aa,在对话框上添加三个按钮,一个用于打开AVI视频文件,一个用于做处理按钮,一个用于程序的退出。如图所示
对应的响应函数分别为如下:
void Cvision::OnOpen()
{
CFileDialog dlg(TRUE, _T(\"*.bmp\"), \"\
OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,
\"AVI files (*.avi) |*.avi|All Files (*.*)|*.*||\
char title[]= {\"Open AVI\
dlg.m_ofn.lpstrTitle= title;
if (dlg.DoModal() == IDOK) {
CString path= dlg.GetPathName();
//声明IplImage指针
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//打开AVI视频文件
if(path==\"\") //判断文件路径是否为空
{
MessageBox(\"请先选择AVI视频文件!\");
return;
}else
{
if(!(pCapture = cvCaptureFromFile(path)))
{
MessageBox(\"打开AVI视频文件失败!\");
return;
}
}
//创建窗口
cvNamedWindow(\"Video\
cvNamedWindow(\"Background\
cvNamedWindow(\"Foreground\
//使窗口有序排列,窗口宽330
cvMoveWindow(\"Video\
cvMoveWindow(\"Background\
cvMoveWindow(\"Foreground\
//逐帧读取视频
while(pFrame = cvQueryFrame( pCapture ))
{
nFrmNum++;
//如果是第一帧,需要申请内存,并初始化
if(nFrmNum == 1)
pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height),
IPL_DEPTH_8U,1); // 存放背景图像(灰度)
pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height),
IPL_DEPTH_8U,1); // 存放中间图像(灰度)
pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
//转化成单通道图像再处理(灰度)
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); //转化成单通道图像再处理(灰度)
cvConvert(pFrImg, pFrameMat);
//高斯滤波先,以平滑图像
//当前帧跟背景图相减(求背景差并取绝对值)
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图(这里采用特定阈值进行二值化)
cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);
//进行形态学滤波,去掉噪音
cvErode(pFrImg, pFrImg, 0, 1);
cvDilate(pFrImg, pFrImg, 0, 1);
//滑动平均更新背景(求平均)
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
// 保持原图像的旋转方向
pBkImg->origin = pFrImg->origin = pFrame->origin;
//显示图像
cvShowImage(\"Video\
cvShowImage(\"Background\
cvShowImage(\"Foreground\
//如果有按键事件,则跳出循环
//此等待也为cvShowImage函数提供时间完成显示
if( cvWaitKey(200) >= 0 )
break;
}
}
函数流程为:首先定义及初始化函数调用过程中用到的变量及指针,然后创建打开视频的窗口设置窗口的参数,其中包括窗口的长、宽、排列方式以及窗口在屏幕上的位置参数,然后逐帧读取视频。视频的读取和处理和图像的读取和处理原理一样,视频的读取是一帧一帧的进行的,视频的处理也是对视频的一帧进行处理然后连续播放形成连续播放的视频,所以说视频的处理过程也是图像的处理过程。读取视频后创建三个窗口分别显示视频、前景图像和背景图像。前景图像窗口用于视频运动检测。视频运动检测有两种方法:连续帧间差分法和背景差分法。连续帧间差分法的主要优点有算法实现简单;程序设计复杂度低;易于实现实时监视;由于相邻帧的时间间隔较短,因此该方法对场景光线的变化不太敏感,受目标阴影的影响也不太大,连续帧间差分法对动态环境有较好的适应性。这里采用背景差分法。背景差分法的主要特点是要求使用当前被监视环境中的一幅静态背景;利用背景图像与当前帧图像的差进行运动目标检测;基于背景差分的运动目标检测虽然较连续帧间差分法可以提取出更为完整的目标图像,但所采集到的背景图像随着时间的推移,会对光照和外部条件造成的场景变化比较敏感,会出现许多伪运动目标点,影响到目标检测的效果。背景差分法的流程如下所示:
(fk-bk)—>Dk-->二值化-->形态学滤波-->连通性滤波-->判别
退出对应的函数为:
void Cvision::OnBtnExit()
{
// TODO: Add your control notification handler code here
cvDestroyWindow(\"Video\");
cvDestroyWindow(\"Background\");
cvDestroyWindow(\"Foreground\");
CDialog::EndDialog(1);
}
退出时将所有窗口和对话框一起销毁,退出所执行的程序。下图为实验效果图:
实验中遇到的问题及其解决办法:在实验程序完成编写并改正语法及逻辑错误后,编译->连接->执行,无法打开视频,多次检查仍无法解决问题。上网查询问题的所在,经过反复尝试后发现计算机中没有安装匹配的视频播放器,从网上下载klcodecfbeta进行安装问题得到解决,顺利实现视频的读取及简单处理(视频运动检测)。
通过本次实验提高了自己的动手能力及独自发现问题解决问题的能力。在完成此次课程设计的过程中遇到了很多问题,通过自己的思考,和同学的讨论以及查找资料都一一得到解决,提高了自己的动手能力和独立思考问题解决问题的能力。在设计过程中复习了C、C++编程语言的语法,熟悉了VC++的基本操作并近一步理解了图像处理的各种处理方法及算法使书本知识和应用得到结合并达到了学以致用。在设计过程中由于粗心犯了很多错误特别是Opencv的配置方面,得到的教训是做事情一定要认真细心决不能敷衍应付。
本文力图用简洁明了的语言描述自己在完成此次课程设计的过程及经验总结,但仍有一些地方语言或表达不太清楚,望老师批评指正。
参考文献
姚敏 数字图像处理 机械工业出版社
冈萨雷斯 数字图像处理 电子工业出版社
钱能 C++ 程序设计教程 清华大学出版社
opencv-doc-cn-0.9.7 OpenCV说明文档
Hieu T.Nguyen, Arnold W.M. Smeulders, Fast Occluded Object Tracking by a
Robust Appearance Filter, 2004 IEEE
Gary R. Bradski, Microcomputer Research Lab, Santa Clara, CA, Intel Corporation, Computer Vision Face Tracking For Use in a Perceptual User Interface
源程序代码如下:
// aa.cpp : Defines the class behaviors for the application.
//
#include \"stdafx.h\"
#include \"aa.h\"
#include \"vision.h\"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAaApp
BEGIN_MESSAGE_MAP(CAaApp, CWinApp)
//{{AFX_MSG_MAP(CAaApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CAaApp construction
CAaApp::CAaApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CAaApp object
CAaApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CAaApp initialization
BOOL CAaApp::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
Cvision dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
// vision.cpp : implementation file
//
#include \"stdafx.h\"
#include \"aa.h\"
#include \"vision.h\"
#include \"cvapp.h\"
#include #include #include #include \"CBitmapEx.h\" #include \"Ipl2DIB.h\" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Cvision dialog Cvision::Cvision(CWnd* pParent /*=NULL*/) : CDialog(Cvision::IDD, pParent) { //{{AFX_DATA_INIT(Cvision) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } void Cvision::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(Cvision) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(Cvision, CDialog) //{{AFX_MSG_MAP(Cvision) ON_BN_CLICKED(IDC_BUTTON1, OnOpen) ON_BN_CLICKED(IDC_BUTTON2, OnProcess) ON_BN_CLICKED(IDC_BTN_EXIT, OnBtnExit) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Cvision message handlers void Cvision::OnOpen() { CFileDialog dlg(TRUE, _T(\"*.bmp\"), \"\ OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY, \"AVI files (*.avi) |*.avi|All Files (*.*)|*.*||\ char title[]= {\"Open Image\ dlg.m_ofn.lpstrTitle= title; if (dlg.DoModal() == IDOK) { CString path= dlg.GetPathName(); //声明IplImage指针 IplImage* pFrame = NULL; IplImage* pFrImg = NULL; IplImage* pBkImg = NULL; CvMat* pFrameMat = NULL; CvMat* pFrMat = NULL; CvMat* pBkMat = NULL; CvCapture* pCapture = NULL; int nFrmNum = 0; //打开AVI视频文件 if(path==\"\") //判断文件路径是否为空 { MessageBox(\"请先选择AVI视频文件!\"); return; }else { if(!(pCapture = cvCaptureFromFile(path))) { MessageBox(\"打开AVI视频文件失败!\"); return; } } //创建窗口 cvNamedWindow(\"Video\ cvNamedWindow(\"Background\ cvNamedWindow(\"Foreground\ //使窗口有序排列,窗口宽330 cvMoveWindow(\"Video\ cvMoveWindow(\"Background\ cvMoveWindow(\"Foreground\ //逐帧读取视频 while(pFrame = cvQueryFrame( pCapture )) { nFrmNum++; //如果是第一帧,需要申请内存,并初始化 if(nFrmNum == 1) { pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); // 存放背景图像(灰度) pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); // 存放中间图像(灰度) pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); //转化成单通道图像再处理(灰度) cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY); cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); cvConvert(pFrImg, pFrameMat); cvConvert(pFrImg, pFrMat); cvConvert(pFrImg, pBkMat); } else { cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); //转化成单通道图像再处理(灰度) cvConvert(pFrImg, pFrameMat); //高斯滤波先,以平滑图像 //cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0); //当前帧跟背景图相减(求背景差并取绝对值) cvAbsDiff(pFrameMat, pBkMat, pFrMat); //二值化前景图(这里采用特定阈值进行二值化) cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY); //进行形态学滤波,去掉噪音 cvErode(pFrImg, pFrImg, 0, 1); cvDilate(pFrImg, pFrImg, 0, 1); //滑动平均更新背景(求平均) cvRunningAvg(pFrameMat, pBkMat, 0.003, 0); //将背景转化为图像格式,用以显示 cvConvert(pBkMat, pBkImg); // 保持原图像的旋转方向 pBkImg->origin = pFrImg->origin = pFrame->origin; //显示图像 cvShowImage(\"Video\ cvShowImage(\"Background\ cvShowImage(\"Foreground\ //如果有按键事件,则跳出循环 //此等待也为cvShowImage函数提供时间完成显示 //等待时间可以根据CPU速度调整 if( cvWaitKey(200) >= 0 ) break; } } if (proc != 0) delete proc; proc= new ImageProcessor(path); } } void Cvision::OnProcess() { if (proc != 0) { proc->execute(); proc->display(); } } void Cvision::OnBtnExit() { // TODO: Add your control notification handler code here cvDestroyWindow(\"Video\"); cvDestroyWindow(\"Background\"); cvDestroyWindow(\"Foreground\"); CDialog::EndDialog(1); } 因篇幅问题不能全部显示,请点此查看更多更全内容