首先聲明,這一系列博文是本人在學(xué)習(xí) OpenCV 的過程中對自身學(xué)習(xí)的一個總結(jié)而已,僅作為 OpenCV 入門級參考,并沒有什么內(nèi)容值得高手參考,所以,只適合和我一樣正值學(xué)習(xí)狀態(tài)者閱讀,高手無意者請飄過,但歡迎留言指教。
OpenCV 是一個開源的計算機視覺庫,其采用 C/C++ 編寫,被設(shè)計為可移植的庫,OpenCV 的設(shè)計目標(biāo)是執(zhí)行速度盡可能的快,其主要關(guān)注的是實時應(yīng)用,同時,OpenCV 的另一個目標(biāo)是構(gòu)建一個簡單易用的計算機視覺框架,以幫助開發(fā)人員更便捷地設(shè)計更復(fù)雜的計算機視覺相關(guān)的應(yīng)用程序。
OpenCV 的結(jié)構(gòu)和內(nèi)容
上面這幅截圖是 OpenCV 源碼的文件組成結(jié)構(gòu),可以看出其中包括 cv ,cvaux , cxcore , highgui , ml 這 5 個模塊。
如果以庫來體現(xiàn)這幾個模塊之間的關(guān)系的話,可以采用下面的結(jié)構(gòu)圖來展現(xiàn):
CV: 包含了基本的圖像處理函數(shù)和高級的計算機視覺算法,包括圖像處理,圖像結(jié)構(gòu)分析,運動描述和跟蹤,模式識別和攝像機標(biāo)定。
ML: 是機器學(xué)習(xí)庫,包含一些基于統(tǒng)計的分類和聚類工具。
HighGUI: 包含圖像和視頻的輸入/輸出函數(shù)。
CXCORE: 包含了 OpenCV 的一些基本的數(shù)據(jù)結(jié)構(gòu)和相關(guān)函數(shù)。
CVAUX: 該模塊則是一般用存放即將被淘汰的算法和函數(shù),同時也包含一些新出現(xiàn)的實驗性的函數(shù)和算法。
關(guān)于 OpenCV 的介紹到此介紹,下面呢,就是通過一個 Demo 來學(xué)習(xí)一些常用的 API 的使用。
Demo 學(xué)習(xí)
Demo01
下面的 Demo 將介紹如何顯示一張圖片,具體 API 的使用請注意注釋。
//在HighGUI 模塊中包含了圖像和視頻的輸入/輸出的基本函數(shù)
#include "highgui.h" //使用了命令行下運行的形式,其中要在命令行下傳遞參數(shù)
int main(int argc,char ** argv)
{
//通過cvLoadImage 將一幅指定路徑的圖片加載到內(nèi)存中 //同時會生成一個IplImage 類型的結(jié)構(gòu) //這個結(jié)構(gòu)會指向圖片所在的內(nèi)存區(qū)域
IplImage * image=cvLoadImage(argv[1]); //通過cvNamedWindow 來建立一個窗體 //因為圖片必須在指定的窗體中才能顯示
cvNamedWindow("Demo01",0); //通過cvShowImage 函數(shù)來指定在指定的窗口中顯示指定的圖片
cvShowImage("Demo01",image); //暫停程序的執(zhí)行 //只有當(dāng)用戶按下任意鍵后才執(zhí)行后面的代碼
cvWaitKey(0); //釋放掉加載到內(nèi)存中的圖片所占的內(nèi)存資源
cvReleaseImage(&image); //銷毀窗口
cvDestroyWindow("Demo01");
}
至于程序的執(zhí)行的話,必須到命令行下執(zhí)行,同時還需要將一張圖片拷貝到 exe 文件所在的目錄,然后再在命令行中指定該圖片作為參數(shù)傳遞到 Main 函數(shù)中。
Demo02
下面繼續(xù)看下一個 Demo,這個 Demo 將展示播放視頻文件:
//圖像和視頻的輸入/輸出均在HighGUI 模塊中
#include "highgui.h" //播放視頻文件只需要循環(huán)的順序的讀取視頻中的每一幀 //讀到幀后,便可以將這個幀作為普通的圖像一樣顯示即可
int main(int argc,char **argv) { //首先需要建立一個窗口來容納視頻的播放
cvNamedWindow("Demo02",CV_WINDOW_AUTOSIZE); //打開一個視頻文件,返回的 CvCapture 結(jié)構(gòu)中包含了視頻文件的信息
CvCapture *capture=cvCreateFileCapture(argv[1]);
IplImage *frame; //循環(huán)順序的讀取視頻中的幀
while(1)
{ //獲取當(dāng)前播放幀的下一個幀,并且將獲取到的幀加載到內(nèi)存中,覆蓋掉前面幀所占的內(nèi)存控件
frame=cvQueryFrame(capture); if(!frame) { //如果沒有讀取到幀的話則說明播放完畢了 //從而退出播放
break; } //將讀取到的幀顯示在窗口中
cvShowImage("Demo02",frame); //每播放一個幀就在此等待30 毫秒
char c=cvWaitKey(30); //如果在30 ms 中用戶按下了ESC 鍵 //(ESC 的ASCII 為27)則退出播放
if(c==27) { break; } } //分配的內(nèi)存需要手動釋放
cvReleaseCapture(&capture); //銷毀窗口
cvDestroyWindow("Demo02"); }
Demo03
上面呢,確實是可以成功的播放視頻了,但是一般的視頻播放器都是有個滾動條的,
允許用戶手動拖動滾動條從而定位到視頻指定的幀上,
下面我們就要實現(xiàn)這個功能了。
//CV 模塊中包含了圖像處理,圖像結(jié)構(gòu)分析, //運動描述和跟蹤,模式識別和攝像機標(biāo)定
#include "cv.h" //包含了圖像和視頻的輸入/輸出
#include "highgui.h" //用來標(biāo)定滾動條當(dāng)前的位置
int g_Pos=0; //將打開的視頻文件作為一個全局變量使用
CvCapture * g_Capture=NULL; //當(dāng)拖動滾動條后,會回調(diào)這個函數(shù) //同時會將當(dāng)前滾動條的位置以32 位形式傳遞過來
void CallBackTrackBarSlide(int pos) { //這里便是重新設(shè)置視頻文件當(dāng)前播放的幀
cvSetCaptureProperty(g_Capture,CV_CAP_PROP_POS_FRAMES,pos); }
int main(int argc,char **argv)
{ cvNamedWindow("Demo03",CV_WINDOW_AUTOSIZE); //根據(jù)參數(shù)打開指定的視頻文件
g_Capture=cvCreateFileCapture(argv[1]); //獲得總的幀數(shù)
int totalFrames= (int)cvGetCaptureProperty(g_Capture,CV_CAP_PROP_FRAME_COUNT);
if(totalFrames!=0) { //創(chuàng)建滾動條,在這里指定了滾動條拖動后的回調(diào)函數(shù)
cvCreateTrackbar("TrackBar","Demo03", &g_Pos,totalFrames,CallBackTrackBarSlide); }
IplImage * frame; //循環(huán)的順序的遍歷所有的幀
while(1)
{ //獲取當(dāng)前幀的下一個幀,并將其加載到內(nèi)存中
frame=cvQueryFrame(g_Capture); if(!frame) { break; }
cvShowImage("Demo03",frame);
char chKeyCode=cvWaitKey(30);
if(chKeyCode==27) { break; } }
cvReleaseCapture(&g_Capture);
cvDestroyWindow("Demo03"); return 0;
}
在測試的時候需要注意,有一些視頻文件時不支持動態(tài)指定幀的操作,所以有可能拖動時會失敗。
然后就可以拖動滾動條來定位幀了
Demo03
上面的 Demo 呢確實是實現(xiàn)了可以通過拖動滾動條來實現(xiàn)對視頻幀的動態(tài)控制,
但是有一個問題就是滾動條并不會跟隨視頻的播放而自帶增加,也就是,隨時時間流逝,
視頻會一直播放,但是滾動條如果不人為地拖動的話是不會發(fā)生改變的,
而我們要是實現(xiàn)的就是當(dāng)視頻播放到哪一個幀了,滾動條就應(yīng)該位于相應(yīng)的位置上,
所以下面的 Demo 就來實現(xiàn)這個功能。
//CV 模塊中包含了圖像處理,圖像結(jié)構(gòu)分析, //運動描述和跟蹤,模式識別和攝像機標(biāo)定 #include "cv.h" //包含了圖像和視頻的輸入/輸出 #include "highgui.h" //用來標(biāo)定滾動條當(dāng)前的位置 int g_Pos=0; //將打開的視頻文件作為一個全局變量使用 CvCapture * g_Capture=NULL; //當(dāng)拖動滾動條后,會回調(diào)這個函數(shù) //同時會將當(dāng)前滾動條的位置以32 位形式傳遞過來 void CallBackTrackBarSlide(int pos) { //這里便是重新設(shè)置視頻文件當(dāng)前播放的幀 cvSetCaptureProperty(g_Capture,CV_CAP_PROP_POS_FRAMES,pos); g_Pos=pos; } int main(int argc,char **argv) { //建立一個名字叫做Demo04 的窗體 cvNamedWindow("Demo04",CV_WINDOW_AUTOSIZE); //根據(jù)參數(shù)打開指定的視頻文件 g_Capture=cvCreateFileCapture(argv[1]); //獲得總的幀數(shù) int totalFrames= (int)cvGetCaptureProperty(g_Capture,CV_CAP_PROP_FRAME_COUNT); if(totalFrames!=0) { //創(chuàng)建滾動條,在這里指定了滾動條拖動后的回調(diào)函數(shù) cvCreateTrackbar("TrackBar","Demo04", &g_Pos,totalFrames,CallBackTrackBarSlide); } IplImage * frame; //循環(huán)的順序的遍歷所有的幀 while(1) { //獲取當(dāng)前幀的下一個幀,并將其加載到內(nèi)存中 frame=cvQueryFrame(g_Capture); if(!frame) { break; } cvShowImage("Demo04",frame); char chKeyCode=cvWaitKey(30); if(chKeyCode==27) { break; } g_Pos++; //當(dāng)播放完200 個幀的時候才觸發(fā)滾動條滾動 if(g_Pos%200==0) { cvSetTrackbarPos("TrackBar","Demo04", g_Pos); } } cvReleaseCapture(&g_Capture); cvDestroyWindow("Demo03"); return 0; }
從而實現(xiàn)了滾動條隨著視頻的播放而自動滾動的效果。
這一篇博文就寫到這里了,從上面可以看出,主要是隨著逐步的深入來介紹一些 OpenCV 的常用的 API ,其主線是首先是加載一張圖片,而后再是加載視頻,再在視頻中加入滾動條,最后是滾動條和視頻聯(lián)動的效果,隨著一步一步的深入,可以更好的熟悉 OpenCV 的幾個常用的 API 。