http://blog.csdn.net/chenyusiyuan/article/details/4640827
PS:增加了保存視頻的功能~~~(chenyusiyuan 2010-01-23)
一、系統環境
Windows XP SP3
Visual Studio 2008
OpenCV-2.0.0a-win32
二、main 函數輸入參數 argc、argv 的意義(參見[1])
在學習筆記(1)中最後寫到:「在Debug完成後,應該把原始圖像放到項目文件夾的 debug 文件夾中,使圖像與exe程序在同一文件夾內,才能在運行程序時正確讀入並顯示圖像。」其實是有誤的,圖像不一定要與exe程序在同一文件夾內,在 cvLoadImage( argv[1] ) 中,可以把 argv[1] 改成項目文件夾裡的圖片的具體地址,若圖片與程序同一文件夾,直接寫圖片的文件名即可。
但是,這種在程序代碼中寫文件名的方式很不方便,每次更改圖片文件後都要重新編譯,實際上應該在跑程序時不改argv[1],而是在調用時輸入具體的文件名。要解決這個問題,就要理解好main 函數輸入參數 argc、argv 的意義。
int main(int argc, char** argv)
int main(int argc, char* argv[])
argc 是指命令行輸入參數的個數,argv 則存儲了所有的命令行參數。假如我的一個程序是video_test.exe,如果在命令行運行該程序(首先應該在命令行下用 cd 命令進入到 video_test.exe 文件所在目錄),要處理的文件有 v1.avi、v2.mpg,運行命令為:
video_test.exe v1.avi v2.mpg
那麼,argc的值是 3,argv[0]是"video_test.exe",argv[1]是"v1.avi",argv[2]是"v2.mpg"。
三、視頻文件的讀取與基本處理
這裡主要是依照《Learning OpenCV》一書的例程修改實現的,其功能是讀取2個視頻文件,分別在兩個窗口中播放,每個窗口都加入一個進度條,可以自行用鼠標控制播放進度。代碼如下:
- // test2_video.cpp : Defines the entry point for the console application.
- //
- #include "stdafx.h"
- #include <iostream>
- #include <cv.h>
- //#include <cvaux.h>
- #include <cxcore.h>
- #include <highgui.h>
- // 使用標准命名空間
- using namespace std;
- // 初始化進度條的位置
- int g_slider_position1 = 0;
- int g_slider_position2 = 0;
- CvCapture* g_capture1 = NULL;
- CvCapture* g_capture2 = NULL;
- // 定義回調函數用於播放進度的控制
- void onTrackbarSlide1( int pos1 )
- {
- cvSetCaptureProperty( g_capture1, CV_CAP_PROP_POS_FRAMES, pos1 );
- }
- void onTrackbarSlide2( int pos2 )
- {
- cvSetCaptureProperty( g_capture2, CV_CAP_PROP_POS_FRAMES, pos2 );
- }
- int main(int argc, char** argv )
- {
- // 建立播放窗口
- cvNamedWindow( "Video Test 1", CV_WINDOW_AUTOSIZE );
- cvNamedWindow( "Video Test 2", CV_WINDOW_AUTOSIZE );
- // 捕捉視頻文件
- g_capture1 = cvCreateFileCapture( argv[1] );
- g_capture2 = cvCreateFileCapture( argv[2] );
- // 讀取、顯示視頻文件的幀數
- int frames1 = (int) cvGetCaptureProperty( g_capture1, CV_CAP_PROP_FRAME_COUNT );
- cout << "frames1 = " << frames1 << endl;
- // 建立進度條
- if( frames1 != 0 )
- cvCreateTrackbar(
- "Position",
- "Video Test 1",
- &g_slider_position1,
- frames1,
- onTrackbarSlide1
- );
- int frames2 = (int) cvGetCaptureProperty( g_capture2, CV_CAP_PROP_FRAME_COUNT );
- cout << "frames2 = " << frames2 << endl;
- if( frames2 != 0 )
- cvCreateTrackbar(
- "Position",
- "Video Test 2",
- &g_slider_position2,
- frames2,
- onTrackbarSlide2
- );
- // 讀取視頻文件信息
- double fps1 = (int) cvGetCaptureProperty( g_capture1, CV_CAP_PROP_FPS );
- double fps2 = (int) cvGetCaptureProperty( g_capture2, CV_CAP_PROP_FPS );
- CvSize size1 = cvSize(
- (int)cvGetCaptureProperty(g_capture1, CV_CAP_PROP_FRAME_WIDTH),
- (int)cvGetCaptureProperty(g_capture1, CV_CAP_PROP_FRAME_HEIGHT));
- CvSize size2 = cvSize(
- (int)cvGetCaptureProperty(g_capture2, CV_CAP_PROP_FRAME_WIDTH),
- (int)cvGetCaptureProperty(g_capture2, CV_CAP_PROP_FRAME_HEIGHT));
- // 創建 VideoWriter
- CvVideoWriter* wrVideo1 = cvCreateVideoWriter(argv[3], CV_FOURCC('M','J','P','G'), fps1, size1);
- CvVideoWriter* wrVideo2 = cvCreateVideoWriter(argv[4], CV_FOURCC('M','J','P','G'), fps2, size2);
- int frs = 0;
- // 開始播放並保存視頻
- IplImage* frame1;
- IplImage* frame2;
- while( frs < frames1 && frs < frames2 )
- {
- // 獲取、顯示源文件的幀畫面
- frame1 = cvQueryFrame( g_capture1 );
- if( !frame1 ) break;
- cvShowImage( "Video Test 1", frame1 );
- frame2 = cvQueryFrame( g_capture2 );
- if( !frame2 ) break;
- cvShowImage( "Video Test 2", frame2 );
- // 保存:將當前幀寫入到目標視頻文件
- cvWriteFrame( wrVideo1, frame1 );
- cvWriteFrame( wrVideo2, frame2 );
- // 若按下 ESC 鍵,則退出程序
- char c = cvWaitKey(33);
- if( c==27 ) break;
- }
- // 釋放內存,關閉窗口
- cvReleaseCapture( &g_capture1 );
- cvReleaseCapture( &g_capture2 );
- cvReleaseVideoWriter( &wrVideo1 );
- cvReleaseVideoWriter( &wrVideo2 );
- cvDestroyWindow( "Video Test 1" );
- cvDestroyWindow( "Video Test 2" );
- return 0;
- }
這裡有幾點需要注意的:
1、在菜單Project -> Properties -> Configuration Properties -> Linker –> Input 的 additional dependencies中加入 cxcore200.lib cv200.lib highgui200.lib opencv_ffmpeg200.lib 等庫。opencv_ffmpeg200.lib 庫可以支持多數主流視頻文件格式(包括 rm、rmvb、flv 等)。
2、在這個程序中,進度條控制視頻播放的功能貌似只對 AVI 文件有效,如果讀入的是其它文件(例如MPG),則進度條失效。
3、關於 iostream 和 標准命名空間(namespace)
(1)和是不一樣,在編譯器include文件夾裡面可以看到,二者是兩個文件,裡面的代碼是不一樣的。 後綴為.h的頭文件c++標准已經明確提出不支持了,c++標准為了和C區別開,也為了正確使用命名空間,規定頭文件不使用後綴.h。
因此,當使用時,相當於在c中調用庫函數,使用的是全局命名空間,也就是早期的c++實現;當使用的時候,該頭文件沒有定義全局命名空間,必須使用namespace std;這樣才能正確使用cout。
(2)所謂namespace,是指標識符的各種可見范圍。 c++標准程序庫中的所有標識符都被定義於一個名為std的namespace中。 由於namespace的概念,使用C++標准程序庫的任何標識符時,可以有三種選擇:
—— 直接指定標識符。
std::cout << std::hex << 3.4 << std::endl;
—— 使用using關鍵字。
using std::cout;
using std::endl;
以上程序可以寫成
cout << std::hex << 3.4 << endl;
—— 使用using namespace std。
#include
#include
#include
using namespace std;
這樣命名空間std內定義的所有標識符都有效。就好像它們被聲明為全局變量一樣。那麼以上語句可以如下寫:
cout << hex << 3.4 << endl;
因為標准庫非常的龐大,所程序員在選擇的類的名稱或函數名時就很有可能和標准庫中的某個名字相同。所以為了避免這種情況所造成的名字沖突,就把標准庫中的一切都被放在名字空間std中。
但這又會帶來了一個新問題。無數原有的C++代碼都依賴於使用了多年的偽標准庫中的功能,他們都是在全局空間下的。所以就有了和等等這樣的頭文件,一個是為了兼容以前的C++代碼,一個是為了支持新的標准。 命名空間std封裝的是標准程序庫的名稱,標准程序庫為了和以前的頭文件區別,一般不加".h"。
(參見:http://www.kuqin.com/language/20080107/3532.html 和http://www.cnblogs.com/walkingmu/archive/2007/11/06/951400.html)