USB免驱摄像头采集图像【VS2012+opencv+directShow(CcameraDS)实现】

在Opencv中文网站上有关于directShow和opencv结合采集图像的教程,地址:http://wiki.opencv.org.cn/index.php/%e4%bd%bf%e7%94%a8DirectShow%e9%87%87%e9%9b%86%e5%9b%be%e5%83%8f
但是该配置比较老,本文讲述如何基于该教程在 VS2012和opencv2.4.9上进行配置和修改,完成USB摄像头的驱动。

博主的USB免驱摄像头如下:
这里写图片描述

文末有完整代码的下载地址

1.环境配置

在Opencv中文网站上有关于directShow和opencv结合采集图像的教程,地址:http://wiki.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”并不靠谱,DirectShow 似乎已经开始被微软给淘汰了,最后存在是在多年前的 DirectX 9.0b 包里。

注意这里并不需要下载DirectX 9.0包,下面介绍在VS2012和opencv2.4.9下的配置过程。

1.1 配置VS2012和opencv环境

按照网上流行的配置即可,如http://www.sigvc.org/bbs/thread-529-1-1.html 。配置好之后尝试运行一个打开图片的小程序检测opencv环境是否配置成功。

1.2 配置DirectX环境

新建工程,配置好Opencv环境,随后将从Opencv中文网上下载的 CameraDS.h 和 CameraDS.cpp 文件分别添加到项目的头文件和源文件中。

VS2012旗舰版是自带了 SDK 的,在 C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include。
打开建立的VS2012项目的属性页,找到“VC++目录”,在“包含目录”里添加 (FrameworkSDKDir)Include (FrameworkSDKDir)Lib。

发现#include “qedit.h”报错,原因是现在的版本已经没有qedit.h这个头文件了,从网址:http://download.csdn.net/download/bcj296050240/9651955 中下载该文件,添加到项目的头文件中。

2. 运行

环境配置好之后,可以用 Opencv中文网 上下载的main.cpp运行,运行过程可能遇到const char* 无法转换的问题,将此处的代码去掉即可。

下面是本人编写的main函数,提供了USB相机的打开、监视、图像捕获功能。

2.1 查看系统的所有摄像头状态(initAllCameras函数)

参数是 CCameraDS 类的对象。该函数获取相机的数目并且显示相机名称。从输出中我们可以找到USB相机的编号,一般情况下编号为1。

//获取当前可用的摄像头并打开USB摄像头
int initAllCameras(CCameraDS &m_CamDS){
    //仅仅获取摄像头数目
    int m_iCamCount = CCameraDS::CameraCount();
    printf("There are %d cameras.\n", m_iCamCount);

    if(m_iCamCount == 0)
    {
        return -1;
    }

    //获取所有摄像头的名称
    for(int i = 0; i < m_iCamCount; i++)
    {
        char szCamName[1024];
        int retval = m_CamDS.CameraName(i, szCamName, sizeof(szCamName));
        if(retval >0)
        {
            printf("Camera #%d's Name is '%s'.\n", i, szCamName);
        }
        else
        {
            printf("Can not get Camera #%d's name.\n", i);
        }
    }
    return m_iCamCount;
}

运行结果如下所示:

There are 3 cameras.
Camera #0's Name is 'Lenovo EasyCamera'.
Camera #1's Name is '3D Camera'.
Camera #2's Name is 'Basler GenICam Source'.

从运行结果中可以看出,使用的3D相机的编号为1。

2.2 打开USB相机(openUsbCam函数)

函数有四个参数,第一个参数为CcameraDS类的对象,camNum设置为1,表明现在要打开的USB相机,而不是电脑自带相机。camWidth和camHeight根据自己所使用相机的情况进行设置,设置为图像的宽度和高度。

代码如下:

// 打开 USB 相机  !! 在调用 camDisplay 和 camCapPic 之前必须调用该函数
// camNum = 1;       // 摄像头编号为1,表示当前要使用的是 USB 摄像头
// camWidth = 2560;  // 图片宽度
// camHeight = 720;  // 图片高度
int openUsbCam(CCameraDS &m_CamDS, const int camNum=1, const int camWidth=2560, const int camHeight = 720){
    // 获取当前可用的相机个数
    // 在所有的相机中,一般编号为 0 的为电脑自带摄像头,编号为 1 的为要使用的 USB 摄像头
    int m_iCamCount = initAllCameras(m_CamDS);
    if(m_iCamCount == -1){
        cout<<"没有可用的摄像头! 退出.."<<endl;
        return -1;
    }

    Mat pFrame;
    // 打开相机并拍摄一幅图片
    if((!m_CamDS.OpenCamera(camNum, false, camWidth, camHeight)) || (m_CamDS.QueryFrame() == NULL)){
        cout<<"# "<<camNum<<" 号相机无法打开,程序退出..";
        return -2;
    }
    return 1;
}

2.3 实时显示(camDisplay函数)

函数有两个参数,第一个参数是CcameraDS类的对象,第二个参数是时间,默认为每隔20ms拍摄一幅图片。
在显示的过程中,按任意键可以退出。可以根据自己的要求进行修改,比如修改成按任意键保存当前图片。

// 显示相机视场的内容  每隔20毫秒显示一帧
int camDisplay(CCameraDS &m_CamDS, const int t=20){
    Mat pFrame;
    while(1)
    {
        //获取一帧
        pFrame = Mat(m_CamDS.QueryFrame());
        //显示
        imshow("Camera", pFrame);
        //延迟,如果有键按下,退出
        if(cv::waitKey(t) != -1)
            break;
    }
    //m_CamDS.CloseCamera(); //可不调用此函数,CCameraDS析构时会自动关闭摄像头
    cv::destroyAllWindows();
    return 0;
}

2.4 保存图片(camCapPic函数)

实现获取一帧图片显示并保存,保存路径有filename设置。

// 抓取图片 并保存到 filename 路径中
int camCapPic(CCameraDS &m_CamDS, string filename="USBcam.jpg"){
    Mat pFrame;
    //获取一帧
    pFrame = Mat(m_CamDS.QueryFrame());

    //显示
    imshow("抓取图片", pFrame);
    waitKey(1000);
    destroyAllWindows();

    //抓取图片保存
    imwrite(filename, pFrame);

    //m_CamDS.CloseCamera(); //可不调用此函数,CCameraDS析构时会自动关闭摄像头

    return 0;
}

2.5 主函数

调用顺序为,首先实例化对象,然后打开摄像头(调用openUsbCam函数)。

打开摄像头后,可以根据自己的需要调用不同的函数,如果要实时显示,则调用camDisplay函数,如果要抓取图像保存,则调用camCapPic函数。

注意在调用这些函数时,使用的是同一个CCameraDS的对象。

注意在函数中不要轻易调用CloseCamera()函数,该函数会释放打开的USB摄像头。如果释放后要重新捕获图片,需要再次调用openUsbCam函数。

int main(){
    CCameraDS m_CamDS; 

    // 打开USB摄像头
    openUsbCam(m_CamDS);

    // 1.0 显示相机内容
    cout<<endl<<"显示相机内容..."<<endl;
    camDisplay(m_CamDS);
    cout<<"显示相机内容 退出..."<<endl<<endl;

    // 2.0 抓取图片保存
    string filename = "USBcam.jpg";
    cout<<"抓取图片保存..."<<endl;
    camCapPic(m_CamDS, filename);
    cout<<"抓取图片保存 退出... 文件路径 "<<filename<<endl;

    cout<<endl<<"结束..."<<endl;
    getchar();
    return 0;
}

完整代码下载地址:

http://download.csdn.net/detail/bcj296050240/9652088

  • 3
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
//指定视频采集设备的友好名字,为它创建一个Filter IBaseFilter * CTestPreviewDlg::CreateVideoDevice(const char * inFriendlyName) { return CreateHardwareFilter(CLSID_VideoInputDeviceCategory,inFriendlyName); } //根据设备的友好名字,创建一个代表该设备的Filter IBaseFilter * CTestPreviewDlg::CreateHardwareFilter(GUID inCategory,const char * inFriendlyName) { //创建一个系统枚举组件对象 ICreateDevEnum * enumHardware = NULL; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_ALL, IID_ICreateDevEnum,(void**)&enumHardware); if(FAILED(hr)) { return NULL; } IBaseFilter * hardwareFilter = NULL; IEnumMoniker * enumMoniker = NULL; //为指定的目录创建枚举器 hr = enumHardware->CreateClassEnumerator(inCategory,&enumMoniker,0); if(enumMoniker) { enumMoniker->Reset(); ULONG fetched = 0; IMoniker * moniker = NULL; char friendlyName[256]; //枚举得到该目录下所有的设备,逐个进行名字匹配 while(!hardwareFilter && SUCCEEDED(enumMoniker->Next(1,&moniker, &fetched)) && fetched) { if(moniker) { IPropertyBag * propertyBag = NULL; VARIANT name; friendlyName[0] = 0; hr = moniker->BindToStorage(0,0,IID_IPropertyBag,(void**)&propertyBag); //读取设备的友好名字 if(SUCCEEDED(hr)) { name.vt = VT_BSTR; hr = propertyBag->Read(L"Friendlyname",&name,NULL); } if(SUCCEEDED(hr)) { WideCharToMultiByte(CP_ACP,0,name.bstrVal,-1, friendlyName,256,NULL,NULL); //如果当前设备的友好名字与用户指定的设备名字相同, //则将当前设备标识绑定为Filter形式 if(strcmp(friendlyName,inFriendlyName) == 0) { moniker->BindToObject(0,0,IID_IBaseFilter, (void**)&hardwareFilter); } } //释放使用过的接口 if(propertyBag) { propertyBag->Release(); propertyBag = NULL; } moniker->Release(); } } enumMoniker->Release(); } enumHardware->Release(); return hardwareFilter; }
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值