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++で使うのが主流なのかもしれない。