在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++目錄”,在“包含目錄”裏添加
發現#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;
}