zoukankan      html  css  js  c++  java
  • 基于 VC+OpenCV+DirectShow 的多个摄像头同步工作[转]

    OpenCV学习笔记(6)基于 VC+OpenCV+DirectShow 的多个摄像头同步工作

    分类: 机器视觉2009-10-08 21:05 20094人阅读 评论(66) 收藏 举报

    imagepropertiescamincludepreprocessorlinker

    因项目需要采集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 。

  • 相关阅读:
    编写好代码的10条戒律
    [Project] 基开放云平台
    [Project] HUSTOJ随笔
    编码规范:大家都应该做的事情
    ural 1167. Bicolored Horses 夜
    1709. PenguinAvia 夜
    hdu 1011 Starship Troopers 夜
    hdu 2571 命运 夜
    hdu 1561 The more, The Better 夜
    hdu 1598 find the most comfortable road 夜
  • 原文地址:https://www.cnblogs.com/freedesert/p/3008350.html
Copyright © 2011-2022 走看看