Visual C++ 2008 Express Edition で動画再生と OpenCV との連携

MPEG-2 ファイルを処理したいが,OpenCV で読み込めない.DirectShow で 出来るらしいから試し,実際に出来た.事前にセットアップすべきは

表題の情報は DirectShowプログラミング [VC++]:Geekなぺーじ にまとまっている.手元の環境で

  • MPEGファイルを再生する(RenderFile)
  • 映像再生が終るまで待つ

の2件が成功した.リンカ入力に「strmiids.lib」を設定すること.

読み込んだ画像は OpenCV で処理したい.
http://www.eml.ele.cst.nihon-u.ac.jp/~momma/wiki/wiki.cgi/Windows/DirectShow/OpenCV%E3%81%A8%E3%81%AE%E7%B5%84%E5%90%88%E3%81%9B.html にサンプルがある.入力がカメラだったから Aspire One で試し,動くのを確認した.リンカ入力には「strmiids.lib cv.lib cxcore.lib highgui.lib」を設定した.dxtrans.h が無いというトラブル*1があるが,

で解決できた.qedit.h をインクルードする直前に

#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__

を追記する.

コード全文

たった5行を追加しただけだが,コード全文を掲載する.

// 書籍「はじめての動画処理プログラミング」土井 滋貴 著 CQ出版
// http://www.cqpub.co.jp/hanbai/books/43/43001.htm
// で公開されている
// list604.cpp  DirectShowを使ったキャプチャ ver1.1
// を利用
//
// DirectShow対応デバイスからキャプチャしたビットマップをOpenCVで横取り 
// 当然Windows専用
// 変更者 Eiichiro Momma 2007/7/4

#include <windows.h>
#include <dshow.h>
#include <stdio.h>
#include <conio.h>

#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
#include <qedit.h>      // SampleGrabber用

#include <cv.h>
#include <highgui.h>

void main()
{
	/*ここからDirectShow用の処理 設定をいじらないならブラックボックス扱いで良い*/
	HRESULT hr;
	CoInitialize(NULL);              // COMの初期化
	// ---- キャプチャフィルタの準備 ----
	// キャプチャデバイスを探す
	ICreateDevEnum * pDevEnum = NULL;
	IEnumMoniker * pClassEnum = NULL;
	IBaseFilter  *pbf = NULL;
	IMoniker * pMoniker = NULL;
	ULONG cFetched;
	// デバイス列挙子を作成
	CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
		IID_ICreateDevEnum, (void ** ) &pDevEnum);
	// ビデオキャプチャデバイス列挙子を作成
	pDevEnum -> CreateClassEnumerator( CLSID_VideoInputDeviceCategory, 
		&pClassEnum, 0);
	if ( pClassEnum == NULL ){
		printf("ビデオキャプチャデバイスは存在しません\n");
		pDevEnum -> Release();
		CoUninitialize();
		return ;
	}
	// 最初に見つかったビデオキャプチャデバイスのオブジェクトの
	// インタフェースを得る
	pClassEnum -> Next(1, &pMoniker, &cFetched);
	pMoniker -> BindToObject( 0, 0, IID_IBaseFilter, (void**)&pbf );
	pMoniker -> Release();
	pDevEnum -> Release();
	pClassEnum -> Release();
	// ---- フィルタグラフの準備 ----
	IGraphBuilder * pGraph = NULL;
	IMediaControl * pMC = NULL;
	// フィルタグラフを作り、インターフェースを得る
	CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
		IID_IGraphBuilder, (void **) &pGraph);
	pGraph -> QueryInterface( IID_IMediaControl, (LPVOID *) &pMC );
	// キャプチャフィルタをフィルタグラフに追加
	pGraph -> AddFilter( pbf, L"Video Capture");
	// ---- グラバフィルタの準備 ----
	ISampleGrabber  *pGrab = NULL;        // これらは後で解放すること。
	IBaseFilter     *pF  = NULL;
	AM_MEDIA_TYPE   amt;
	// グラバフィルタを作る
	CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, 
		IID_IBaseFilter, (LPVOID *)&pF);
	pF -> QueryInterface( IID_ISampleGrabber, (void **)&pGrab );
	// グラバフィルタの挿入場所の特定のための設定
	ZeroMemory(&amt, sizeof(AM_MEDIA_TYPE));
	amt.majortype  = MEDIATYPE_Video;
	amt.subtype    = MEDIASUBTYPE_RGB24;
	amt.formattype = FORMAT_VideoInfo; 
	pGrab -> SetMediaType( &amt );
	// グラバフィルタをフィルタグラフに追加
	pGraph -> AddFilter(pF, L"SamGra");
	// ---- キャプチャグラフの準備 ----
	ICaptureGraphBuilder2 * pCapture = NULL;
	// キャプチャグラフを作る   
	CoCreateInstance( CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
		IID_ICaptureGraphBuilder2, (void **) &pCapture );
	// フィルタグラフをキャプチャグラフに組み込む
	pCapture -> SetFiltergraph( pGraph );
	// キャプチャグラフの設定、グラバをレンダリング出力に設定
	pCapture -> RenderStream ( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
		pbf, NULL, pF);
	// ビットマップ情報の取得  
	pGrab -> GetConnectedMediaType( &amt ); 
	// ビデオ ヘッダーへのポインタを獲得する。
	printf( "amt.lSampleSize = %d (byte)\n", amt.lSampleSize );
	VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)amt.pbFormat;
	// ビデオ ヘッダーには、ビットマップ情報が含まれる。
	// ビットマップ情報を BITMAPINFO 構造体にコピーする。
	BITMAPINFO BitmapInfo;
	ZeroMemory( &BitmapInfo, sizeof(BitmapInfo) );
	CopyMemory( &BitmapInfo.bmiHeader, &(pVideoHeader->bmiHeader), sizeof(BITMAPINFOHEADER));
	printf( "width = %d , height %d , color %d \n",
		BitmapInfo.bmiHeader.biWidth, 
		BitmapInfo.bmiHeader.biHeight, 
		BitmapInfo.bmiHeader.biBitCount );
	printf( "キャプチャを開始します、どれかキーを押して下さい\n" ); 
	getch();
	/*ここまでDirectShow用の処理 設定をいじらないならブラックボックス扱いで良い*/

	/*ここからOpenCVの処理*/
	IplImage *frame = 0;
	IplImage *dstImage = 0;
	IplImage *baseImage = 0;
	//カラー24bitという前提
	frame = cvCreateImage(cvSize(BitmapInfo.bmiHeader.biWidth, BitmapInfo.bmiHeader.biHeight),IPL_DEPTH_8U,3);
	dstImage = cvCreateImage(cvSize(BitmapInfo.bmiHeader.biWidth, BitmapInfo.bmiHeader.biHeight),IPL_DEPTH_8U,3);
	baseImage = cvCreateImage(cvSize(BitmapInfo.bmiHeader.biWidth, BitmapInfo.bmiHeader.biHeight),IPL_DEPTH_8U,3);
	cvNamedWindow("OpenCV",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("Dst Image",CV_WINDOW_AUTOSIZE);
	/*ここまでOpenCVの処理*/

	// ---- キャプチャ開始 ----
	pMC -> Run();            // レンダリング開始
	pGrab -> SetBufferSamples(TRUE);  // グラブ開始
	int nn = 0;              // グラブ回数のカウント、意味はない
	int input=0;
	int fCount=1;
	char fname[2048];
	while(1){
		hr = pGrab -> GetCurrentBuffer((long *)&(BitmapInfo.bmiHeader.biSizeImage),  // グラブ
			(long *)(frame->imageData) ); //<-ここで横取り
		cvFlip(frame,frame,0); //上下反転しているのでcvFlipで戻す
		if(nn == 1){ //初期背景画像
			cvCopyImage(frame,baseImage);
		}
		cvSub(frame,baseImage,dstImage); //差分画像の作成
		cvShowImage("Dst Image",dstImage); //差分の表示
		cvShowImage("OpenCV",frame); //現在画像の表示
		nn++;
		printf("グラブ hr = %x, n = %d\n", hr, nn );
		input = cvWaitKey(100); //早過ぎると処理が間に合わない?
		switch(input){
		case 'q':
			goto EXIT_WHILE;
			break;
		case 'b':
			//背景の更新
			cvCopyImage(frame,baseImage);
			break;
		case 'c':
			//キャプチャ
			sprintf(fname,"frame%00d.jpg",fCount);
			cvSaveImage(fname,frame);
			sprintf(fname,"sub%00d.jpg",fCount);
			cvSaveImage(fname,dstImage);
			sprintf(fname,"base%00d.jpg",fCount);
			cvSaveImage(fname,baseImage);
			fCount++;
			break;
		}
	}
EXIT_WHILE:
	/* ここからDirectShowの終了処理 */
	// インターフェースのリリース
	pbf -> Release();              // キャプチャフィルタ用
	pMC -> Release();              // フィルタグラフ用
	pGraph -> Release();
	pCapture -> Release();            // キャプチャグラフ用
	pF -> Release();              // サンプルグラバ用            
	pGrab -> Release();
	// COMのリリース
	CoUninitialize();
	/*ここまでDirectShowの終了処理*/

	//OpenCVの後片付け
	cvDestroyWindow("OpenCV");
	cvDestroyWindow("Dst Image");
	cvReleaseImage(&frame);
	cvReleaseImage(&baseImage);
	cvReleaseImage(&dstImage);
}

*1:1>c:\program files\microsoft sdks\windows\v6.0a\include\qedit.h(498) : fatal error C1083: include ファイルを開けません。'dxtrans.h': No such file or directory