Ovrvision用のキャプチャーソフトを書いてみている。
USB接続の2つのカメラデバイスとしてWindowsに認識されているので、2つのカメラからの同時キャプチャーをすればよい。
OpenCVでする場合、
CvCapture *capture1,*capture2; capture1 = cvCaptureFromCAM(0); // Right camera capture2 = cvCaptureFromCAM(1); // Left camera frame1 = cvQueryFrame(capture1); frame2 = cvQueryFrame(capture2);
などとすればよい。
しかし、ノートPCで既に内蔵のwebカメラがある場合にカメラが3つとなり、その場合にちょっと困る。
内蔵カメラデバイスをデバイスマネージャーから無効に設定するとよいかと思ったら、動くこともあれば動かないこともあり、どうも安定しない。
OpenCV以外のキャプチャー方法としては、VFW(Video for Windows)を使う方法がある。
#include <vfw.h>
hnd = capCreateCaptureWindow("Camera",WS_CHILD,0+10,0+10,640,480,hWnd,0 );
capDriverConnect( hnd, index );
dwSize = capGetVideoFormatSize(hnd);
capGetVideoFormat(hnd, &psVideoFormat, dwSize);
psVideoFormat.biBitCount = 24;
psVideoFormat.biCompression = BI_RGB;
capSetVideoFormat(hnd, &psVideoFormat, dwSize);
capSetCallbackOnFrame( hnd, FrameCallback );
などとするのだけど、複数のカメラからのキャプチャーは試していない。
OpenCVは内部でVFWを使っているようなので、結果は同じで不安定なままになりそうな気がする。
あと他の方法ということで、DirectXのDirectShowを使ってキャプチャーするという方法があ
る。
実際に2つのカメラからキャプチャーするソフトを書いてみたところ、とりあえずうまくいったようだ。
ちょっと長いが、コードは以下のように書いた。
#include <windows.h>
#include <dshow.h> // DirectShow(DirectX)
#include "qedit.h" // SampleGrabber
#include <stdio.h>
#pragma comment (lib, "strmiids.lib")
#define TIMER_WAIT 10
#define MAX_CAM 2
HWND hWnd = NULL;
IGraphBuilder * pGraph;
IMediaControl * pMC = NULL;
ICaptureGraphBuilder2 * pCapture[MAX_CAM];
IBaseFilter *pF[MAX_CAM];
ISampleGrabber *pGrab[MAX_CAM];
ICreateDevEnum * pDevEnum =NULL;
IBaseFilter *pbf[MAX_CAM];
IMoniker * pMoniker = NULL;
IEnumMoniker * pClassEnum = NULL;
ULONG cFetched;
AM_MEDIA_TYPE amt;
BITMAPINFOHEADER Bmphd;
BYTE *lpBmpData1;
BYTE *lpBmpData2;
long datasize;
int init_flag=0 , cap_start=0;
int wd,ht;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg,WPARAM wParam, LPARAM lParam);
int InitVideoCapture(void)
{
int i,n;
CoInitialize(NULL); // COM initialize
// capture device
CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void ** ) &pDevEnum);
pDevEnum -> CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if (pClassEnum == NULL){
pDevEnum -> Release(); CoUninitialize(); return -1; // no camera
}
// find interface of video capture device object
n=0;
while (1) {
if (pClassEnum -> Next(1, &pMoniker, &cFetched) == S_OK) {
pMoniker -> BindToObject( 0, 0, IID_IBaseFilter, (void**)&pbf[n] );
n++;
} else {
break;
}
}
if (n!=2) {
pDevEnum -> Release(); CoUninitialize(); return -1; // not 2 cameras
}
// filter graph
CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &pGraph);
pGraph -> QueryInterface( IID_IMediaControl, (LPVOID *) &pMC );
for (i=0;i<n;i++) {
pGraph -> AddFilter( pbf[i], L"Video Capture");
pbf[i] -> Release();
}
// add grabber filter
for (i=0;i<n;i++) {
CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (LPVOID *)&pF[i]);
pF[i] -> QueryInterface( IID_ISampleGrabber, (void **)&pGrab[i] );
ZeroMemory(&amt, sizeof(AM_MEDIA_TYPE));
amt.majortype = MEDIATYPE_Video; amt.subtype= MEDIASUBTYPE_RGB24; amt.formattype = FORMAT_VideoInfo;
pGrab[i] -> SetMediaType( &amt );
pGraph -> AddFilter(pF[i], L"SamGra");
}
// capture graph
for (i=0;i<n;i++) {
CoCreateInstance( CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **) &pCapture[i] );
pCapture[i] -> SetFiltergraph( pGraph );
pCapture[i] -> RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pbf[i], NULL, pF[i]);
}
pDevEnum -> Release();
pClassEnum -> Release();
pMoniker -> Release();
// capture Info
pGrab[0] -> GetConnectedMediaType( &amt );
pGrab[1] -> GetConnectedMediaType( &amt );
VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)amt.pbFormat;
BITMAPINFO BitmapInfo;
ZeroMemory( &BitmapInfo, sizeof(BitmapInfo) );
CopyMemory( &BitmapInfo.bmiHeader, &(pVideoHeader->bmiHeader),sizeof(BITMAPINFOHEADER));
Bmphd = BitmapInfo.bmiHeader;
datasize = Bmphd.biSizeImage;
lpBmpData1 = (BYTE *)malloc( datasize );
lpBmpData2 = (BYTE *)malloc( datasize );
wd=pVideoHeader->bmiHeader.biWidth;
ht=pVideoHeader->bmiHeader.biHeight;
// capture start
pGrab[0]->SetBufferSamples(TRUE);
pGrab[1]->SetBufferSamples(TRUE);
pMC->Run();
return 0;
}
void FinishVideoCaputure(void)
{
// capture stop
pMC -> Stop();
pGrab[0] -> SetBufferSamples( 0 );
pGrab[1] -> SetBufferSamples( 0 );
// release
pGraph -> Release();
pCapture[0] -> Release();
pCapture[1] -> Release();
// pMC -> Release();
free( lpBmpData1 );
free( lpBmpData2 );
CoUninitialize(); // COM Release
}
int APIENTRY WinMain(HINSTANCE hInst,HINSTANCE,LPSTR cmd,int show)
{
WNDCLASSEX wndclass;
MSG msg;
int ret;
ret=InitVideoCapture(); // capture init
if (ret == -1) { MessageBox(NULL,"capture cannot start",NULL,MB_OK); return 0; }
// window setting
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInst;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "wnd-001";
wndclass.hIconSm = 0;
RegisterClassEx (&wndclass);
// create window
hWnd = CreateWindow( "wnd-001", "" ,WS_OVERLAPPEDWINDOW, 0,0,wd*2+20,ht+50,NULL,NULL,hInst,NULL);
if (hWnd==NULL) { return FALSE; }
ShowWindow(hWnd,show); UpdateWindow(hWnd);
// timer
SetTimer(hWnd,1,TIMER_WAIT,NULL);
// event loop
while (GetMessage(&msg,NULL,0,0)) {
TranslateMessage(&msg); DispatchMessage(&msg); // do event
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg,WPARAM wParam, LPARAM lParam)
{
static int tic=0;
switch (msg) {
case WM_CREATE:
break;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
hdc=BeginPaint(hWnd,&ps);
if (cap_start==1) {
SetDIBitsToDevice(ps.hdc , 5 ,5, wd,ht, 0,0, 0,ht, lpBmpData2 , (BITMAPINFO *)&(Bmphd), DIB_RGB_COLORS );
SetDIBitsToDevice(ps.hdc , 5+wd,5, wd,ht, 0,0, 0,ht, lpBmpData1 , (BITMAPINFO *)&(Bmphd), DIB_RGB_COLORS );
}
EndPaint(hWnd,&ps);
break;
case WM_TIMER:
if (tic<60) {
tic++;
if (tic==60) { init_flag=1; }
}
if (init_flag==1) {
// capture
HRESULT hr;
hr = pGrab[0]->GetCurrentBuffer( &datasize, (long*)lpBmpData1);
hr = pGrab[1]->GetCurrentBuffer( &datasize, (long*)lpBmpData2);
cap_start=1;
InvalidateRect(hWnd , NULL , FALSE);
}
break;
case WM_KEYDOWN:
if (wParam==0x1B) { SendMessage(hWnd,WM_DESTROY,0,0); } // ESC key to quit
break;
case WM_DESTROY:
FinishVideoCaputure(); // capture finish
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,msg,wParam,lParam);
}
return 0;
}
これを元にOculus Rift DK1用に画面出力するソフトを書けば、なんとかなりそうな気がしてきた。
Ovrvision1をDK2用に換装してしまったけど、またDK1用に戻してみる。
–
追記
OpenCVでDirectShowを使う方法があるそうだ。
Twitterで教えていただいた。
→ buildinsider.net/small/opencv/05 の「2.5.2 サンプルプログラム」にあるようにすればOpenCVでもDirectShow経由のキャプチャができるのでご参考までに。
int camera_id = 0; cv::VideoCapture cap(CV_CAP_DSHOW + camera_id);
などとすればよいらしい。
いままでC言語でOpenCVを利用してきたけど、C++で使うのが主流なのかもしれない。