2008年6月18日 星期三

GUI介面的製作-"cvcam.h"視訊函式庫(1)

這邊介紹OpenCV另一個跟視訊有關的函式庫"cvcam.h",它也包括AVI檔的存取跟視訊播放,不同的是,它具有控制視訊相關功能的副程式

cvcamInit()
cvcamStart()
cvcamPause()
cvcamResume()
cvcamStop()
cvcamExit()

及兩個設定視訊相關的GUI介面.而且它是支援HWND,也就是他可以支援其他視窗介面程式設計,直接整合在它們的Form裡,如Visual C++或C++ Builder視窗介面程式設計,只要給它視窗介面的ID,就可以整合操作,且一次可以開啟多個視訊.

cvcam.h的視訊使用
#include <cvcam.h>
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>


void CaptureCallback(IplImage *frame1);

int main()
{
    HWND CaptureWindow=0;
    int width=640;
    int height=480;
    int CamerasCount= cvcamGetCamerasCount();
    printf("視訊裝置總數量: %d\n",CamerasCount);

    CameraDescription CameraDescription1;
    cvcamSetProperty(0,CVCAM_PROP_ENABLE,CVCAMTRUE);
    cvcamSetProperty(0,CVCAM_PROP_RENDER,CVCAMTRUE);
    cvcamSetProperty(0,CVCAM_PROP_WINDOW,&CaptureWindow);
    cvcamSetProperty(0,CVCAM_PROP_CALLBACK,(void *)&CaptureCallback);
    cvcamSetProperty(0,CVCAM_RNDWIDTH,&width);
    cvcamSetProperty(0,CVCAM_RNDHEIGHT,&height);

    cvcamGetProperty(0,CVCAM_VIDEOFORMAT,NULL);
    cvcamGetProperty(0,CVCAM_CAMERAPROPS,NULL);
    cvcamGetProperty(0,CVCAM_DESCRIPTION,&CameraDescription1);

    cvcamInit();
    cvcamStart();

    printf("===============================\n");
    printf("選擇編號0的裝置\n");
    printf("裝置名稱:\n%d\n",CameraDescription1.device);
    printf("裝置描述:\n%s\n",CameraDescription1.DeviceDescription);
    printf("裝置通道數:\n%d\n",CameraDescription1.channel);
    printf("裝置通道描述:\n%s\n",CameraDescription1.ChannelDescription);
    printf("最大寬上限:\n%d\n",CameraDescription1.maxwidth);
    printf("最大高上限:\n%d\n",CameraDescription1.maxheight);
    printf("最小寬上限:\n%d\n",CameraDescription1.minwidth);
    printf("最小高上限:\n%d\n",CameraDescription1.minheight);

    cvWaitKey(0);
    cvcamStop();
    cvcamExit();

}
void CaptureCallback(IplImage *frame1)
{
    int i,j;
    for(i=0;i<frame1->height;i+=20)
    {
        for(j=0;j<frame1->widthStep;j+=frame1->nChannels)
        {
            frame1->imageData[i*j] =0;
            frame1->imageData[i*j+1]=0;
            frame1->imageData[i*j+2]=255;
        }
    }
}

執行結果:
(1)Frame格式修改

(2)視訊裝置設定

(3)播放


上面的程式碼,屬於cvcam重新開啟的win32視窗,而非OpenCV自己的視窗程式,所以不支援cvSetMouseCallback()及cvCreateTrackbar()的呼叫,因此,不能製作滑鼠及Trackbar事件,不過它可以用鍵盤阻斷,以及他程式內部本身的呼叫,CaptureCallback()就是他的呼叫方法,,它可以藉由HWND去嵌入視窗程式(Visual C++,C++ Builder)做更強大的應用.上面的程式碼給了一個HWND為NULL的視窗代號,讓她重新開啟了一個非Opencv的視窗,接著偵測視訊裝置數量cvcamGetCamerasCount(),注意,這是程式碼必要的,如果不做偵測就找不到視訊裝置,接著是視訊描述資料結構,cvcamSetProperty()是視訊屬性設立,第一個功能CVCAM_PROP_ENABLE是將視訊設成Enable,CVCAM_PROP_RENDER則是交付視訊,CVCAM_PROP_WINDOW則是選定視訊開啟視窗,CVCAM_PROP_CALLBACK就是視訊的呼叫,可以自行定義副程式來修改視訊顯示畫面的內容,CVCAM_RNDWIDTH跟CVCAM_RNDHEIGHT可以自由修改畫面的寬跟高.接著是用cvcamGetProperty()取得資訊,CVCAM_VIDEOFORMAT及CVCAM_CAMERAPROPS為開啟GUI介面視訊功能設定,CVCAM_DESCRIPTION是取得視訊詳細描述.這邊顯示的執行結果是變成紅色直線條,而CameraDescription資料結構獲取的訊息有些也不一定正確.

下面這一段程式碼就是要做到視訊的選擇,以及兩台視訊的功能設定,使用的是cvcamSelectCamera()的函式,"cvcam.h"具有一個視窗介面一次可以自己選擇兩台想要用的視訊功能,以及二擇一的視訊裝置,但是對於不同廠商的視訊裝置會造成無法同步播放的問題,可以用視訊模擬器的軟體解決,以下為多台視訊操控程式碼

cvcam.h的視訊二擇一,或是兩台操作
#include <cvcam.h>
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>


void CaptureCallback(IplImage *frame1);
void StereoCallback(IplImage frame1,IplImage *frame2);

int main()
{
    HWND CaptureWindow1=0;
    HWND CaptureWindow2=0;

    int *CameraNumber;
    int nSelected = cvcamSelectCamera(&CameraNumber);
    if(nSelected==1)
    {
        cvcamSetProperty(CameraNumber[0], CVCAM_PROP_ENABLE, CVCAMTRUE);
        cvcamSetProperty(CameraNumber[0], CVCAM_PROP_RENDER, CVCAMTRUE);
        cvcamSetProperty(CameraNumber[0], CVCAM_STEREO_CALLBACK,(void *)&CaptureCallback);
        cvcamSetProperty(CameraNumber[0], CVCAM_PROP_WINDOW, &CaptureWindow1);

    }
    else if(nSelected==2)
    {
        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_STEREO_CALLBACK,(void *)&StereoCallback);
        cvcamSetProperty(CameraNumber[0], CVCAM_PROP_WINDOW, &CaptureWindow1);

        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_STEREO_CALLBACK,(void *)&StereoCallback);
        cvcamSetProperty(CameraNumber[1], CVCAM_PROP_WINDOW, &CaptureWindow2);

    }

    cvcamInit();
    cvcamStart();

    cvWaitKey(0);

    cvcamStop();
    cvcamExit();
    free(CameraNumber);
    cvDestroyWindow("cvcam1 Window");
    cvDestroyWindow("cvcam2 Window");
}
void CaptureCallback(IplImage *frame1)
{}
void StereoCallback(IplImage frame1,IplImage *frame2)
{}

執行結果:
(1)選擇想要用的裝置


(2)選擇e2eSoft VCam出現的掛載畫面


(2)裝置播放


cvcamSelectCamera()這個函式會自動偵測視訊裝置的數量,並且開啟一個GUI介面,讓使用者可以選定想使用的裝置,因而取代了cvcamGetCamerasCount()的功能,而他最多可以選定兩台裝置同步播放,當cvcamSelectCamera()的回傳值nSelected==1時,表示只選定一台裝置,被選定的裝置代號被放在CameraNumber[0]裡面,而當nSelected==2時,表示選定了兩台裝置,裝置代號分別被放在CameraNumber[0]及CameraNumber[1]裡,而要做到同步播放,那就一定要用cvcamSetProperty()裡面CVCAM_STEREO_CALLBACK的參數啦,這個參數只能處理兩台視訊同步播放,並且可以在StereoCallback()這個函式處理兩台視訊播放的畫面內容,也就是寫演算法進去一次處理兩台畫面,也是因為這個函式的問題,造成了不同廠商的視訊裝置會互衝,會出現的問題是"找不到中介篩選器的組合,所以無法連接。",這個問題就只能用視訊模擬器來解決啦,把其中一台會衝到的裝置掛載到視訊模擬器上面,視訊模擬器的下載網址

http://www.e2esoft.cn/vcam/index.asp

VCam


要注意的是,被掛載的裝置會造成OpenCV的任何一個視訊函式都無法播放,如果是做單一的視訊播放,而該裝置又被掛載,則無法使用該裝置,而只能使用視訊模擬器所提供的虛擬裝置名稱.

cvcamGetCamerasCount()
偵測所有視訊裝置數量,是一定要做的第一步.
int cvcamGetCamerasCount()

cvcamSelectCamera()
開啟選擇視訊裝置的GUI界面,內部具有偵測視訊裝置數量cvcamGetCamerasCount()的功能,cvcamSelectCamera()的回傳值為被選定的視訊數,這邊只能選定兩個裝置,因此回傳值不是0就是1,2,而它的第一個引數為被選定的視訊代號陣列,陣列的最大長度為2,分別放入的是第一視訊裝置代號跟第二視訊裝置代號.
cvcamSelectCamera(被選定的int *型別視訊陣列)

cvcamSetProperty()
設立視訊裝置屬性,這邊他的參數方式是用字串的形式去設計,如下定義

static const char CVCAM_PROP_ENABLE[] = "enable";    Enable視訊
static const char CVCAM_PROP_RENDER[] = "render";    啟用視訊
static const char CVCAM_PROP_WINDOW[] = "window";    選定畫面在哪個視窗開啟
static const char CVCAM_PROP_CALLBACK[] = "callback";  設定Capture方法呼叫
static const char CVCAM_STEREO_CALLBACK[] = "stereo_callback";  設定兩個裝置同步播放及StereoCallback函式
static const char CVCAM_STEREO3_CALLBACK[] = "stereo3_callback";  設定三個裝置同步播放及Stereo3Callback
static const char CVCAM_STEREO4_CALLBACK[] = "stereo4_callback";  設定四個裝置同步播放及Stereo4Callback
static const char CVCAM_RNDWIDTH[] = "rendering_width";  修改畫面寬度
static const char CVCAM_RNDHEIGHT[] = "rendering_height";  修改畫面高度

因此,它的參數是字串,而非之前的用代號表示,CVCAM_PROP_WINDOW需要給它HWND型別,CVCAM_PROP_CALLBACK則需要給它自行定義的副程式名稱,CVCAMTRUE則是用代號表示如下

#define CVCAMTRUE (void*)1
#define CVCAMFALSE (void*)0

cvcamSetProperty(裝置代號,參數或字串,屬性設定參數);

cvcamGetProperty()
取得視訊裝置屬性,用法跟上面類似,可取得的參數為

static const char CVCAM_VIDEOFORMAT[] = "video_pp";    GUI介面的視訊格式設定
static const char CVCAM_CAMERAPROPS[] = "camera_pp";    GUI介面的視訊裝置設定
static const char CVCAM_DESCRIPTION[] = "description";    詳細資訊描述

CVCAM_DESCRIPTION要用CameraDescription資料結構接.
cvcamSetProperty(裝置代號,參數或字串,屬性設定參數);

cvcamInit();
初始化視訊

cvcamStart();
啟動視訊

cvcamStop();
停止視訊

cvcamExit();
關閉視訊

CaptureCallback()
修改播放Frame的呼叫,接收的格式是IplImage資料結構.
CaptureCallback(IplImage *frame1);

StereoCallback()
修改兩個視訊裝置Frame播放的呼叫,接收的格式是IplImage資料結構.
CaptureCallback(IplImage *frame1,IplImage *frame2);

--
附註:Opencv1.0 only,如果opencv 1.1想要使用的話,可以到sourceforge的opencv網站上下載

23 意見:

匿名 提到...

您好,請問cvcamGetProperty(camnum, CVCAM_PROP_SETFORMAT, &divforamt);
是不是沒有作用....

我試過好像無法傳回或取回 經屬性/格式對話盒設定後的資料.....

請問有調用屬性/格式對話中的參數該如何使用? cvcamGetProperty(,CVCAM_PROP_SETFORMAT, );
cvcamSetProperty(,CVCAM_PROP_SETFORMAT, );
都無法work....

yester 提到...

您好,我很晚才看到這篇回應

CVCAM_PROP_SETFORMAT只支援linux喔..

你可以在

C:\Program Files\OpenCV\docs\cvcam.rtf

這篇文件內看到,有一張"cvcam Properties Interface"的表格

以下

Property name:CVCAM_PROP_ENABLE
Can be:Set/Get
Platform:Windows/Linux

Property name:CVCAM_PROP_RENDER
Can be:Set/Get
Platform:Windows/Linux

Property name:CVCAM_PROP_WINDOW
Can be:Set/Get
Platform:Windows/Linux

Property name:CVCAM_PROP_CALLBACK
Can be:Set
Platform:Windows/Linux

Property name:CVCAM_DESCRIPTION
Can be:Get
Platform:Windows/Linux

Property name:CVCAM_VIDEOFORMAT
Can be:Get
Platform:Windows/Linux

Property name:CVCAM_CAMERAPROPS
Can be:Get
Platform:Windows/Linux

Property name:CVCAM_RNDWIDTH
Can be:Set/Get
Platform:Windows/Linux

Property name:CVCAM_RNDHEIGHT
Can be:Set/Get
Platform:Windows/Linux

Property name:CVCAM_STEREO_CALLBACK
Can be:Set
Platform:Windows

Property name:CVCAM_PROP_RAW
Can be:Get
Platform:Linux

Property name:CVCAM_PROP_SETFORMAT
Can be:Set/Get
Platform:Linux

匿名 提到...

您好~~
那請問有辦法用一個GUI讓使用者自己選擇
我要哪一個視訊裝置嗎?
感謝~

yester 提到...

使用cvcamSelectCamera()這個函式

目前本人只有一台裝置可以測試

晚點補上到這篇文章

匿名 提到...

您好:

請問OpenCV中如何切換攝影機使用Preview Mode/Overlay Mode呢,我的擷取卡預設是Preview mode

VFW中有capOverlay可以做到,那麼OpenCV有支援這個功能嗎?

謝謝

yester 提到...

在cvcam.rtf
的文件沒提到Preview Mode及Overlay Mode,我想是沒有吧

匿名 提到...

你好可以請問一下就是我要用Picolo擷取卡來擷取我的影像...不知道利用OpenCV可以擷取嗎?

匿名 提到...

请问stereocallback返回图像为什么无法二值化呢?
void stereocallback(IplImage* image)

cvCvtColor(image, pFrImg, CV_BGR2GRAY);

cvConvert(image, pFrameMat);

//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);

//二值化前景图
cvThreshold(pFrMat, image1, 60, 255.0, CV_THRESH_BINARY);

yester 提到...

Picolo擷取卡 我在Yahoo OpenCV Group有看到可以用Euresys Library,我倒是沒用過,不過如果要將Euresys Library與OpenCV結合使用的話,只要用Euresys Library抓的到Picolo擷取卡Frame的Raw Data直接放到IplImage資料結構的Image->imagedata並且自行初始化IplImage資料結構即可

yester 提到...

StereoCallback()是直接擷取兩個CCD的Frame,如果有兩台裝置的話,應該是可以做二值化,只有一台的話用CaptureCallback()才對.. 我現在要多生出一台裝置試試StereoCallback()有點困難XD

匿名 提到...

不好意思你說在Yahoo OpenCV Group有看到有關Picolo與OPENCV相關資料請問可以給我網址嗎?

匿名 提到...

只要用Euresys Library抓的到Picolo擷取卡Frame的Raw Data直接放到IplImage資料結構的Image->imagedata並且自行初始化IplImage資料結構即可====>不好意思請問有範例或是例子可以參考嗎?...因為我還是OPENCV的新手

yester 提到...

一般的裝置擷取到frame的資料都是一個個的RawData,完全沒有長跟寬的資訊,RawData不是RGBRGB...的排列就是BGRBGR...的排列,因此,你只要知到裝置frame的擷取方法直接餵給Image->imagedata就好了~很抱歉我只能跟你講原理,如果要用directshow當例子那東西更是複雜多了XD

如果你要我這邊跟Picolo與OPENCV的相關資料,可以寫信給我,我可以轉貼給你,因為Yahoo Group是屬於信件交流的論壇方式

匿名 提到...

假如一张采集卡有4个PORTS要怎同时启动2个PORTS的CCD

yester 提到...

兩個視訊的驅動程式有安裝到
應該就可以同時使用了吧
我這邊沒有這類裝置 沒辦法測試看看

匿名 提到...

因为是同一张卡所以算是有灌到吧

cipoke@yahoo.com.tw 提到...

昨日大大:

請問一下,我原本是OpenCV 1.0
照著你的CODE實現,可以抓到CAMERA影像
也可以貼到MFC的Form上.

但是更新成OpenCV 1.1後,一直找不到
"cvcam.h",你的另一篇文章指出此版本
已經移除cvcam的功能了@@"
那.....在OpenCV 1.1怎麼抓webcam影像?

謝謝~

阿噗 敬上

yester 提到...

雖然會有點麻煩..
你可以去OpenCV Source Code網站
抓OpenCV_cvs自己掛載cvcam

如下連結
http://sourceforge.net/project/showfiles.php?group_id=22870&package_id=80741&release_id=655389

匿名 提到...

昨日大大:
是不是可以麻煩您指導一下cvcam在vs2008下的安裝及設定方式,我試了好久都無法編譯成功
謝謝

匿名 提到...

您好:
我試了貴網站的資料。
原先我試了CvCapture *capture1來獲得裝置。-->是okㄉ(一個攝影機狀態)。
然後,試試能否擷取到2個裝置->是不行ㄉ。
(我想我的裝置應該沒衝突到~一個是內建webcan,一個是usb)。
-------------------------
於是我試了另外一種,即
HWND CaptureWindow1=0;
HWND CaptureWindow2=0;

int *CameraNumber;
int nSelected = cvcamSelectCamera(&CameraNumber);這一種,也是只能出現一個。
另外還有一個問題:如果我用第2個方法,我要擷取一張畫面,不知道是用什麼函數來獲得?(像第一種能利用
cvQueryFrame(capture);來獲得。)
第二個我想是利用void CaptureCallback(IplImage *frame1)來獲得。
但是我用了都沒反應?

煩請版主給個提示!!
thx

匿名 提到...

您好,我想請問一下opencv執行的視訊視窗有辦法把左右顛倒過來嘛(就像是在照鏡子的樣子)?

Moses 提到...

Don't include <stdext/winapi/winbase.h>
這是怎麼回事?

匿名 提到...

你好:
如果要用open cv做影像對位,
這程式試用嗎,
或者也類似程式可供我參考,
opev cv版本要用最新版的嗎,
或者要使用哪個版本,

請問影像擷取卡要怎麼知道位置呢,
要寫在程式內抓到影像卡,
謝謝了

Copyright 2008-2009,yester