Ovrvision用のキャプチャーソフトを書いてみている

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




コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Time limit is exhausted. Please reload CAPTCHA.

3 × 2 =