OpenCV學習筆記(6)基於 VC+OpenCV+DirectShow 的多個攝像頭同步工作
http://blog.csdn.net/chenyusiyuan/article/details/4643313
因項目需要采集2個攝像頭的數據進行雙目檢測,一開始采用以下代碼來測試:
#include "stdafx.h"#include <cv.h> #include <cxcore.h> #include <highgui.h>int main(int argc, _TCHAR* argv[]) {CvCapture* capture1 = cvCreateCameraCapture( 0 );CvCapture* capture2 = cvCreateCameraCapture( 1 );double w = 320, h = 240;cvSetCaptureProperty ( capture1, CV_CAP_PROP_FRAME_WIDTH, w ); cvSetCaptureProperty ( capture1, CV_CAP_PROP_FRAME_HEIGHT, h );cvSetCaptureProperty ( capture2, CV_CAP_PROP_FRAME_WIDTH, w ); cvSetCaptureProperty ( capture2, CV_CAP_PROP_FRAME_HEIGHT, h );cvNamedWindow( "Camera_1", CV_WINDOW_AUTOSIZE );cvNamedWindow( "Camera_2", CV_WINDOW_AUTOSIZE );IplImage* frame1;IplImage* frame2;int n = 2;while(1){frame1 = cvQueryFrame( capture1 );if( !frame1 ) break;cvShowImage( "Camera_1", frame1 );frame2 = cvQueryFrame( capture2 );if( !frame2 ) break;cvShowImage( "Camera_2", frame2 );int key = cvWaitKey(30);if( key == 27 ) break;}cvReleaseCapture( &capture1 );cvReleaseCapture( &capture2 );cvDestroyWindow( "Camera_1" );cvDestroyWindow( "Camera_2" );return 0; }
這個程序在使用不同類型的攝像頭時,例如我使用一個普通的網絡攝像頭,另外一個是手機上的攝像頭(這款手機具有網絡攝像頭功能),這樣的話程序就能正常運行;但如果攝像頭是相同類型時,就只能讀取其中一個攝像頭的數據了,第二個窗口則是一片灰色。查閱開發文檔資料得知 cvCreateCameraCapture(int index) 函數可以選擇攝像頭,但實際測試發現 cvCreateCameraCapture 只接受 –1 和 0 兩種參數,其他值,如1,2,101,102,201,202...全都無法正確的切換到第二個接入的攝像頭。如果兩個 capture 都使用 cvCreateCameraCapture(-1),是可以切換到第二個攝像頭,但當第二次執行 cvCreateCameraCapture() 函數時,會強行彈出選擇攝像頭的對話框要你手動選擇,而且以後再添加攝像頭的話,還得修改代碼重新build,實際項目中肯定不能這樣處理。在OpenCV中文論壇上找到的解釋是,如果攝像頭的名稱是「USB視頻設備 #*」,則 OpenCV 只能讀取其中一個的數據。
查閱opencv的cvcam官方文檔,找到一些資料:
/* Begin work with cvcam, you can select single or multiple cameras in 2 ways. The first is using a camera selection dialog with cvcamSelectCamera. See an example below: */ //Prototype /* Pops up a camera(s) selection dialog Return value - number of cameras selected (0,1 or 2); Argument: an array of selected cameras numbers NULL if none selected. Should be released with free() when not needed. if NULL passed, not used. */ CVCAM_API int cvcamSelectCamera(int** out); Function ThatSelectsCamera() { int* out; int nselected = cvcamSelectCamera(&out); if(nselected>0) printf("the 1-st selected camera is camera number %d", out[0]); if(nselected == 2) printf("the 2-nd selected camera is camera number %d", out[1]); free(out); return; } /* Note: if you don』t need selected cameras numbers, simply call cvcamSelectCamera(NULL) Note2: Linux version of cvcam currently has no implementation of cvcamSelectCamera. *///The second, non-dialog way is to use CVCAM_PROP_ENABLE property like this: int desiredcamera = 0;//for example cvcamSetProperty(desiredcamera, CVCAM_PROP_ENABLE,CVCAMTRUE);
根據上述說明,我找到了下面這段對應的代碼,不過應該是用 VC6+OpenCV1.0 寫的,在我的機子上(VS2008+OpenCV2.0)運行不了,不能驗證是否有效,不過還是貼出來供大家討論:
#include <cvcam.h> #include <cv.h> #include <highgui.h> #include "stdio.h" #include <windows.h>void StereoCallback(IplImage *frame1,IplImage *frame2); void onMouse(int Event,int x,int y,int flags,void *param);IplImage *image1,*image2;char *strleft[4]={"left1.bmp","left2.bmp","left3.bmp","left4.bmp"}; char *strright[4]={"right1.bmp","right2.bmp","right3.bmp","right4.bmp"};void main() {HWND CaptureWindow1=0;HWND CaptureWindow2=0;//int ncams=cvcamGetCamerasCount(); //獲取攝像頭的個數 //用對話框的形式來選取攝像頭 int *CameraNumber;int nSelected = cvcamSelectCamera(&CameraNumber);/* //灰色圖像image1=cvCreateImage(cvSize(320,240),IPL_DEPTH_8U,1);image2=cvCreateImage(cvSize(320,240),IPL_DEPTH_8U,1); *///彩色圖像 image1=cvCreateImage(cvSize(320,240),IPL_DEPTH_8U,3);image2=cvCreateImage(cvSize(320,240),IPL_DEPTH_8U,3);//初始化兩個攝像頭 cvNamedWindow("cvcam1 Window",1);CaptureWindow1=(HWND)cvGetWindowHandle("cvcam1 Window");cvcamSetProperty(CameraNumber[0], CVCAM_PROP_ENABLE, CVCAMTRUE);cvcamSetProperty(CameraNumber[0], CVCAM_PROP_RENDER, CVCAMTRUE);cvcamSetProperty(CameraNumber[0], CVCAM_PROP_WINDOW, &CaptureWindow1);cvSetMouseCallback("cvcam1 Window",onMouse,0);cvNamedWindow("cvcam2 Window",1);CaptureWindow2=(HWND)cvGetWindowHandle("cvcam2 Window");cvcamSetProperty(CameraNumber[1], CVCAM_PROP_ENABLE, CVCAMTRUE);cvcamSetProperty(CameraNumber[1], CVCAM_PROP_RENDER, CVCAMTRUE);cvcamSetProperty(CameraNumber[1], CVCAM_PROP_WINDOW, &CaptureWindow2);//讓兩個攝像頭同步 cvcamSetProperty(CameraNumber[0], CVCAM_STEREO_CALLBACK,(void *)&StereoCallback); //啟動程序 cvcamInit();cvcamStart();cvWaitKey(0);cvcamStop();cvcamExit();free(CameraNumber);cvDestroyWindow("cvcam1 Window");cvDestroyWindow("cvcam2 Window"); }void StereoCallback(IplImage* frame1,IplImage *frame2) { /* //把圖像轉換成灰度圖並保存到image中cvCvtColor(frame1,image1,CV_RGB2GRAY);cvCvtColor(frame2,image2,CV_RGB2GRAY);*///拷貝圖像到全局變量image中 該函數這樣用存在問題 cvCopy(frame1,image1);cvCopy(frame2,image2); // image1=cvCloneImage(frame1); // image2=cvCloneImage(frame2);//對截取的圖像翻轉 cvFlip(image1,image1,0);cvFlip(image2,image2,0); }void onMouse(int Event,int x,int y,int flags,void *param) { static int num=0;if(Event==CV_EVENT_LBUTTONDOWN){if(num==4)num=0;//只是固定定義了保存4張圖片,為了不讓程序非法而設置的復原 cvcamPause();//圖像保存 cvSaveImage(strleft[num],image1); cvSaveImage(strright[num],image2);// cvSaveImage("left.bmp",image1);// cvSaveImage("right.bmp",image2); }if(Event==CV_EVENT_RBUTTONDOWN){cvcamResume();num++;} }
在論壇上找了很久,最終找到了解決辦法,即利用於仕琪老師提供的DirectShow視頻采集方案(http://www.opencv.org.cn/index.php/%E4%BD%BF%E7%94%A8DirectShow%E9%87%87%E9%9B%86%E5%9B%BE%E5%83%8F)。該方案介紹的CCameraDS類調用采集函數可直接返回IplImage,使用更方便,且集成了DirectShow,勿需安裝龐大的DirectX/Platform SDK。
利用該方案提供的例程,結合上一篇筆記中單窗口顯示多個視頻子圖像的程序,就實現了讀取兩個攝像頭的數據、並進行實時邊緣檢測的功能,主函數代碼如下:
// // Multiple Cameras Capture using DirectShow // Author: Yuhua Zou // Thanks to: // Shiqi Yu (shiqi.yu@gmail.com) // HardyAI@OpenCV China // flymanbox@OpenCV China (for his contribution to function CameraName, and frame width/height setting) // Last modification: October 8, 2009 //// // 使用說明: // 在 VC6 開發環境下的使用說明: // 1. 將CameraDS.h CameraDS.cpp以及目錄DirectShow復制到你的項目中 // 2. 菜單 Project->Settings->Settings for:(All configurations)->C/C++->Category(Preprocessor)->Additional include directories // 設置為 DirectShow/Include // 3. 菜單 Project->Settings->Settings for:(All configurations)->Link->Category(Input)->Additional library directories // 設置為 DirectShow/Lib // 在 VS2005/2008 開發環境下的使用說明: // 1. 將CameraDS.h CameraDS.cpp復制到你的項目中 // 2. 將DirectShow復制到你的opencv根目錄下,菜單 工具->選項->項目和解決方案->vc++目錄,把..(你的opencv安裝目錄)/DirectShow/Include添加到 // 「引用文件」中$(VCInstallDir)PlatformSDK/include和$(FrameworkSDKDir)include下面任意位置 // 3. 菜單 工具->選項->項目和解決方案->vc++目錄,把..(你的opencv安裝目錄)/DirectShow/Lib添加到「庫文件」下面。也可參考使用說明3。 // #include "stdafx.h" #include "camerads.h" #include <cv.h> #include <cxcore.h> #include <highgui.h> #include <stdio.h> #include <stdarg.h> #include <time.h>// 單窗口顯示多幅圖像的函數 void cvShowMultiImages(char* title, int nArgs, ...) {// 略,詳見學習筆記(5) }int main( int argc, char** argv ) {int cam_count;//僅僅獲取攝像頭數目 cam_count = CCameraDS::CameraCount();printf("There are %d cameras./n", cam_count);//獲取所有攝像頭的名稱 for(int i=0; i < cam_count; i++){char camera_name[1024]; int retval = CCameraDS::CameraName(i, camera_name, sizeof(camera_name) );if(retval >0)printf("Camera #%d's Name is '%s'./n", i, camera_name);elseprintf("Can not get Camera #%d's name./n", i);}if(cam_count==0)return -1;// 創建2個攝像頭類 CCameraDS camera1;CCameraDS camera2; //打開第一個攝像頭//if(! camera.OpenCamera(0, true)) //彈出屬性選擇窗口 if(! camera1.OpenCamera(0, false, 320,240)) //不彈出屬性選擇窗口,用代碼制定圖像寬和高 {fprintf(stderr, "Can not open camera./n");return -1;}//打開第二個攝像頭 camera2.OpenCamera(1, false, 320,240);cvNamedWindow("Multiple Cameras");// 初始化在子圖像中顯示字符的字體格式 CvFont tFont;cvInitFont(&tFont, CV_FONT_HERSHEY_COMPLEX, 0.5f,0.7f,0,1,8);char cam1str[] = "Camera #1";char cam2str[] = "Camera #2";// 為讀取系統時間信息分配內存 char timestr[25];memset(timestr, 0, 25 * sizeof(char)); while(1){//獲取一幀 IplImage *pFrame1 = camera1.QueryFrame();IplImage *pFrame2 = camera2.QueryFrame();// 獲取當前幀的灰度圖 IplImage* frame_gray_1 = cvCreateImage(cvGetSize(pFrame1),pFrame1->depth,1);IplImage* frame_gray_2 = cvCreateImage(cvGetSize(pFrame2),pFrame2->depth,1);cvCvtColor(pFrame1,frame_gray_1,CV_RGB2GRAY);cvCvtColor(pFrame2,frame_gray_2,CV_RGB2GRAY);// 對灰度圖像進行Canny邊緣檢測// 然後將圖像通道數改為三通道 IplImage* frame_canny_1 = cvCreateImage(cvGetSize(pFrame1),pFrame1->depth,1);IplImage* frame_canny_2 = cvCreateImage(cvGetSize(pFrame2),pFrame2->depth,1);IplImage* frame1 = cvCreateImage(cvGetSize(pFrame1),pFrame1->depth,pFrame1->nChannels);IplImage* frame2 = cvCreateImage(cvGetSize(pFrame2),pFrame2->depth,pFrame2->nChannels);cvCanny(frame_gray_1,frame_canny_1,20,75,3);cvCanny(frame_gray_2,frame_canny_2,20,75,3);cvCvtColor(frame_canny_1,frame1,CV_GRAY2BGR);cvCvtColor(frame_canny_2,frame2,CV_GRAY2BGR);// 獲取系統時間信息 time_t rawtime; struct tm* timeinfo; rawtime = time( NULL ); timeinfo = localtime( &rawtime ); char* p = asctime( timeinfo );// 字符串 p 的第25個字符是換行符 '/n'// 但在子圖像中將亂碼顯示// 故僅讀取 p 的前 24 個字符 for (int i = 0; i < 24; i++){timestr[i] = *p;p++;}p = NULL;// 在每個子圖像上顯示攝像頭序號以及系統時間信息 cvPutText( pFrame1, cam1str, cvPoint(95,15), &tFont, CV_RGB(255,0,0) );cvPutText( pFrame2, cam2str, cvPoint(95,15), &tFont, CV_RGB(255,0,0) );cvPutText( frame1, cam1str, cvPoint(95,15), &tFont, CV_RGB(255,0,0) );cvPutText( frame2, cam2str, cvPoint(95,15), &tFont, CV_RGB(255,0,0) );cvPutText( pFrame1, timestr, cvPoint(5,225), &tFont, CV_RGB(255,0,0) );cvPutText( pFrame2, timestr, cvPoint(5,225), &tFont, CV_RGB(255,0,0) );cvPutText( frame1, timestr, cvPoint(5,225), &tFont, CV_RGB(255,0,0) );cvPutText( frame2, timestr, cvPoint(5,225), &tFont, CV_RGB(255,0,0) );// 顯示實時的攝像頭視頻 cvShowMultiImages( "Multiple Cameras", 4, pFrame1, pFrame2, frame1, frame2 );//cvWaitKey(33); int key = cvWaitKey(33);if( key == 27 ) break;cvReleaseImage(&frame1);cvReleaseImage(&frame2);cvReleaseImage(&frame_gray_1);cvReleaseImage(&frame_gray_2);cvReleaseImage(&frame_canny_1); cvReleaseImage(&frame_canny_2); }camera1.CloseCamera(); //可不調用此函數,CCameraDS析構時會自動關閉攝像頭 camera2.CloseCamera();cvDestroyWindow("Multiple Cameras");return 0; }
在 Project -> Properties -> Configuration Properties -> Linker -> Input 的 Additional Dependencies 中,需要添加以下庫文件:
odbc32.lib
odbccp32.lib
cv200.lib
cxcore200.lib
highgui200.lib
在編譯以上程序時,可能會出現以下幾種錯誤(參見 http://topic.csdn.net/u/20081022/12/30fb745f-332b-42f7-bbee-02a760c48132.html):
1> ../../../winnt.h(222) : error C4430: missing type specifier - int assumed. Note: C++ does not support
2> ../../../winnt.h(222) : error C2146: syntax error : missing ';' before identifier 'PVOID64'
3> ../../../winnt.h(5940) : error C2146: syntax error : missing ';' before identifier 'Buffer'
對於第1類錯誤,可以用wd4430來解決,具體的在Project -> Properties -> Configuration Properties -> Linker -> Command Line的 Additional Options 中添加 『/wd4430』 即可。
對於第2類錯誤,一般可通過調整 DirectShow/Include 在 Tools -> Options -> Projects and Solutions -> VC++ Directories -> Show Directories for –> Include Files 中的位置(把它下移到最下面),然後把 Project -> Properties -> Configuration Properties –> C/C++ 中的 Additional Include Directories 裡面的內容(../../../../include)刪掉,重新編譯,PVOID64的錯誤就會消失,原因如下:
POINTER_64 是一個宏,在64位編譯下起作用,它包含在SDK目錄下的BASETSD.H中(Microsoft Visual Studio 8/VC/PlatformSDK/Include/basetsd.h(23):#define POINTER_64 __ptr64),但DXSDK自己也帶了一個basetsd.h,裡面沒有定義POINTER_64,從而導致出錯,只需要改變 include files 的優先級即可。
當然,也可以改寫 winnt.h 中的代碼,在下面這兩行:
typedef void *PVOID;
typedef void *POINTER_64 PVOID64;
之前增加一行:
#define POINTER_64 __ptr64
不過最好不要輕易改寫 winnt.h 。
OpenCV學習筆記(6)基於 VC+OpenCV+DirectShow 的多個攝像頭同步工作相关推荐
- android 直接v4l2驱动,基於V4L2驅動程序的USB攝像頭Android(JNI)的編寫(三)
在上兩篇文章基於V4L2驅動程序的USB攝像頭Android(JNI)的編寫(一)和基於V4L2驅動程序的USB攝像頭Android(JNI)的編寫(二)中,我詳細介紹了如何配置V4L2的環境.設置捕 ...
- java中集合什么时候有索引,JavaSE中Collection集合框架學習筆記(1)——具有索引的List...
前言:因為最近要重新找工作,Collection(集合)是面試中出現頻率非常高的基礎考察點,所以好好惡補了一番. 復習過程中深感之前的學習不系統,而且不能再像剛畢業那樣死背面試題,例如:String是 ...
- c#學習筆記--Visual Studio 使用
一.新建項目 ① 在visual studio 的文件工具欄中,能夠創建新的項目,打開已有項目,打開文件夾和最近的項目 2010express版本可新建的項目包括: Windows Forms Ap ...
- python操作txt找到最便宜的素菜_Python線性模型學習筆記
歡迎關注Python愛好者社區,我們是專註於商業智能BI,大數據,數據分析領域的垂直社區,學習,問答.求職一站式搞定! 天善學院618活動正在進行中!天善學院618大禮包,大數據.數據分析與數據挖掘. ...
- hf标签什么意思 html,HF HTMLCSS第2版學習筆記(陸續更新)
第一章 認識HTML HTML的全稱是Hyper Text Makeup Language,簡稱HTML,譯名超文本標記語言 Web服務器(把文件放到Web服務器上,瀏覽器就可以通過互聯網得到你的We ...
- GeekBand c++學習筆記之四(conversion fuction vs non-explicit-one-arguement ctor)
大家好,因為這一周所講述的課程點比較分散,所以我就記錄與分享一下我比較深刻與個人覺得比較有趣的部份,這個部分就conversion fuction和non-explicit-one-arguement ...
- easyuefi添加linux分区,linux學習筆記(一)——使用easyBCD或easyUEFI引導從硬盤安裝Ubuntu系統...
Table of Contents windows系統安裝ubuntu會出現引導問題,windows系統不希望有其他系統和windows系統共存.所以我們得自己作一個引導.接下來主要介紹兩種引導,ea ...
- FileMaker中的腳本觸發器學習筆記
脚本触发器 **脚本触发器是始终绑定到用户布局接口.对于数据表或者字段.只有在而已接口才能触发. **如果某一个布局或者对象上包含触发器,则其右下角会有触发器图标 **当触发一个事件时,有且仅有一个触 ...
- 《深入浅出Mysql》學習筆記--日志
四种日志 错误日志 二进制日志 慢查询日志 查询日志 1. 错误日志(.err) 严重错误信息记录 more x.err 2. 二进制日志(binlog) 记录DDL,DML(select除外) 用于 ...
最新文章
- 上传一份个人学习struts2的历程笔记
- 老公,等儿子长大,我去天堂找你(推荐)
- iptables如何添加容许某个端口的访问
- oracle的工具cmd,数据库命令行工具DBCLI
- ubuntu python3 mysql_ubuntu14.04 python3.*连接mysql
- linux后台执行shell脚本
- python怎么看待_如何看待将Python作为少儿编程的基础语言?
- B站(IT速成之JAVA速成班 300分钟算法面试)跟做笔记
- java视频上传,播放预览,解决苹果手机视频无法预览的问题
- 整理12种电脑有趣屏保
- 虚拟机VirtualBox中Ubuntu和windows共享文件夹访问权限问题
- 第8章 HDFS HA高可用
- html颜色(背景,字体等)
- hdu1175连连看
- 计算机三维成像在哪些领域有运用,【图】三维动画类别及运用领域,三维动画分类介绍...
- ubuntu下查看电脑内存硬盘CPU显卡驱动等配置命令
- Git 使用的一些命令以及Git commit 注释格式
- webpack command not found 的意外的坑 - 原因是从node开始
- # nest笔记四:文件的上传与下载
- 计算机绘图论文范文大全集,计算机绘图实习报告范文
热门文章
- c语言中如何将字体弄大,CFree怎样调大字体
- 七年时间,微软智能云在中国走过的“大江大河”
- 趣图:程序员的鄙视链/图
- golang:%v,%+v,%#v的区别
- 微信小程序:微信零钱模拟器微信小程序源码下载查收充电器自动充钱
- Axure高级功能(变量、动态面板[轮播图]、中继器)
- 一款产品经理很好用的产品原型Axure配合软件--墨刀,将产品原型Axure,添加到墨刀,可以/预览/设计/编辑/共享/协同设计
- 机器学习(周志华) 第一章 引言
- Android实现资源动态加载的两种方式
- html图片动态案例,10个强大的纯CSS3动画案例分享