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;
}

3

這個程序在使用不同類型的攝像頭時,例如我使用一個普通的網絡攝像頭,另外一個是手機上的攝像頭(這款手機具有網絡攝像頭功能),這樣的話程序就能正常運行;但如果攝像頭是相同類型時,就只能讀取其中一個攝像頭的數據了,第二個窗口則是一片灰色。查閱開發文檔資料得知 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。

4

利用該方案提供的例程,結合上一篇筆記中單窗口顯示多個視頻子圖像的程序,就實現了讀取兩個攝像頭的數據、並進行實時邊緣檢測的功能,主函數代碼如下:

//
// 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);
        else
            printf("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 。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV 是一个开源的计算机视觉库,可以用于实现许多图像和视频处理的任务。而 Jetson 是 NVIDIA 推出的一款高性能嵌入式计算平台,用于加速人工智能应用程序的开发和部署。 使用 OpenCV 实现通过摄像头识别二维码可以通过以下步骤来实现: 1. 首先,需要在 Jetson 上安装 OpenCV 库和相机驱动程序。可以通过 JetPack 以及 OpenCV 的官方文档进行安装和配置。 2. 在程序中导入 OpenCV 库,并创建一个 VideoCapture 对象,用于打开摄像头并捕获视频帧。 3. 使用循环来不断读取摄像头捕获的帧,并对每一帧进行二维码的检测。可以使用 OpenCV 中的 QRCodeDetector 类来实现,该类提供了对二维码的解码和检测功能。 4. 对每个检测到的二维码进行解码,获取二维码内容。可以通过调用 QRCodeDetector 类中的 detectAndDecode 方法来实现。 5. 在屏幕上显示二维码内容或将内容保存到文件中,以便后续使用。 需要注意的是,使用 Jetson 进行图像处理可能需要使用 GPU 加速来提高性能,可以通过 OpenCV 的 GPU 模块或使用 CUDA 来实现。另外,为了提高二维码的检测效果,可以根据实际情况进行参数的调整,如调整二维码的大小范围、检测的灵敏度等。 总结起来,通过在 Jetson 上使用 OpenCV 库和相机驱动程序,可以方便地实现通过摄像头识别二维码的功能。这可以在许多应用场景中使用,如物流追踪、社交媒体分享、支付扫码等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值