最近これらを使うことが多いのですが、それぞれ目的が少しずつ違うので、
- カメラからの入力が簡単: OpenCV, ARToolkit
- GUIの利用が簡単: GLUT(ARToolkit), OpenCV
- 3次元物体の描画が簡単: GLUT(ARToolkit)
- 画像処理が簡単: OpenCV
というように得意分野が違うわけです。なので、必然的にこれらを組み合わせて使う必要がでてきたりするので、個人的なメモとして書いときます。環境はWindows XP, VS2008, OpenCV2.1, ARToolkit2.72.1, GLUT3.7。
この場合、GUIとカメラキャプチャはARToolkitということになるので、カメラからの取得画像の変換ができればよい。
ARUint8 *dataPtr; // ARToolkit によるカメラ画像へのポインタ
IplImage *image; // OpenCV の画像
// カメラから画像を取得
dataPtr = (ARUint8 *)arVideoGetImage()
// OpenCV用の画像を作成する。ARToolkit がRGBAの4チャンネルなのでそれにあわせる
image = cvCreateImage( cvSize(win_xsize, win_ysize), IPL_DEPTH_8U, 4 );
memcpy( image->imageData, dataPtr, image->imageSize ); // 画像をコピー
// OpenCVで何か処理する
cvFlip( image, image, 1 ); // 左右反転
ARToolkitはGLUTベースなので、OpenCV用にはRGBAフォーマットで画像バッファを作成してIplImage->imageDataに内容を渡してやればよい。逆もまた然り。
基本的には上記と同じなのですが、カメラキャプチャもOpenCVで行うことになる。
frameImage = cvQueryFrame( capture ); // カメラから画像を取得
cvCvtColor( frameImage, copyImage, CV_BGR2RGB ); // OpenCV のBGR並びをRGBに変換しながらコピー
cvFlip( copyImage, copyImage, 0 ); // OpenGLは左下原点なので上下を入れ替える
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // バッファクリア
glMatrixMode(GL_PROJECTION); // 投影行列を初期化
glLoadIdentity();
glRasterPos2i( -1, -1 ); // 二次元画像の原点を左下に設定
// OpenCV の画像データを描画
glDrawPixels( copyImage->width, copyImage->height, GL_RGB, GL_UNSIGNED_BYTE, copyImage->imageData );
ここでは、カメラで取得した画像をGLUTで作成したウィンドウ上に表示している。OpenCVで得られる画像は、Windows NativeのBGR並びになっているので、これをOpenGLに合わせてRGB(またはRGBA)に変換しないと色がおかしくなる。また、OpenGLは左下原点、得られた画像は左上原点になっているのでこれをあわせる必要がある。ここではOpenCV側で反転画像を作成してから描画しているが、OpenGL側で画像座標系を指定することで解決させることも可能。
GUI作成とカメラキャプチャはOpenCVで行う。OpenCVで作ったウィンドウにどうやってOpenGLの3Dモデルを表示するかがポイント。ちょっと長いですが、動くものを載せときます。
#include
#include
#include
#include
#include
#include// for glutSolidCube()
// OpenGLのためのフォーマット指定
static PIXELFORMATDESCRIPTOR pfd = {
sizeof (PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0, 0, 0,
0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
32,
0,
0,
PFD_MAIN_PLANE,
0,
0,
0,
0
};
int main( int argc, char *argv )
{
CvCapture *capture=NULL;
IplImage *frameImage=NULL, *copyImage=NULL, *outputImage=NULL;
int key;
int count=0;
HWND hwnd;
HDC hdc;
HGLRC hglrc;
int pfdID;
GLfloat light_diffuse = {1.0, 0.0, 0.0, 1.0};
GLfloat light_position[] = {3.0, 3.0, 3.0, 0.0};
capture = cvCreateCameraCapture( -1 );
frameImage = cvQueryFrame( capture ); // カメラ画像の取得
cvNamedWindow( "OpenCV", CV_WINDOW_AUTOSIZE ); // ウインドウ作成
cvResizeWindow( "OpenCV", frameImage->width, frameImage->height );
// 画像バッファ作成
copyImage = cvCreateImage( cvSize(frameImage->width, frameImage->height), 8, 4 ); // RGBA の4バイト
outputImage = cvCreateImage( cvSize(frameImage->width, frameImage->height), 8, 3 ); // BGR の3バイト
hwnd = (HWND)cvGetWindowHandle( "OpenCV" ); // ウインドウハンドルを取得
hdc = GetDC(hwnd); // デバイスコンテキストを取得
pfdID = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pfdID, &pfd); // デバイスコンテキストにピクセルフォーマットを設定
hglrc = wglCreateContext(hdc); // OpenGL用のコンテキストを作成
wglMakeCurrent(hdc, hglrc); // ここからOpwnGLコマンドが有効になる
// 光源設定
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// 陰面処理を有効に
glEnable(GL_DEPTH_TEST);
while( 1 ) {
frameImage = cvQueryFrame( capture );
cvCvtColor(frameImage, copyImage, CV_BGR2RGBA ); // OpenGL用に画素の並びを変換
cvFlip( copyImage, copyImage, 0 ); // OpenGLの原点に合わせて上下反転
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); // バッファクリア
glMatrixMode(GL_PROJECTION); // 投影行列を初期化
glLoadIdentity();
glRasterPos2i( -1, -1 ); // 二次元画像の原点を左下に設定
// OpenCV の画像データを描画
glDrawPixels( copyImage->width, copyImage->height, GL_RGBA, GL_UNSIGNED_BYTE, copyImage->imageData ); //キャプチャした画像を背景として描画
glClear( GL_DEPTH_BUFFER_BIT ); // デプスバッファのみをクリア
glOrtho( -4.0, 4.0, -3.0, 3.0, -5.0, 5.0 ); // とりあえず 横:縦=4:3 で正射影
// 原点上で立方体を回転させる
glRotatef( (count%360), 1.0, 0.5, 0.5 );
glutSolidCube( 1 );
count++;
// OpenGLバッファの内容を画像として取得
glReadPixels( 0, 0, outputImage->width, outputImage->height, GL_RGB, GL_UNSIGNED_BYTE, outputImage->imageData ); // RGBで取得
cvCvtColor( outputImage, outputImage, CV_RGB2BGR ); // OpenCVのBGR並びに変換
cvFlip( outputImage, outputImage, 0 ); // OpenCV に合わせて上下反転
cvShowImage( "OpenCV", outputImage ); // 画像表示
key = cvWaitKey(10);
if( key == 27 ){
break;
}
}
wglMakeCurrent(hdc, 0);
wglDeleteContext(hglrc);
ReleaseDC(hwnd, hdc);
cvReleaseImage( ©Image );
cvDestroyWindow( "OpenCV" );
cvReleaseCapture( &capture );
return 0;
}
ここでは、OpenCVが作ったウィンドウからデバイスコンテキストを取得し、OpenGL用の描画バッファを作成しています。そして、OpenGLの画像バッファにカメラの画像を書き込み、さらにその上に3Dモデルを書き重ねています。表示上はこのままOpenGLのSwapBuffers()を呼んでもいいのですが、OpenCVでいろいろ処理できるようにするため、IplImageに書き戻してから表示させています。
- まとめ
OpenGLでの描画を行うことがわかっているならば手続きの面からも処理速度の面からも、OpenCVをベースに表示するのはあまりよくなさそうです。カメラからの動画の上に3Dを重ねたい、という場合はしょうがないですけど。
単純にカメラの映像が欲しいだけならば、ARToolkitのカメラキャリブレーションなどはオーバースペックでしょう。OpenCVを使うのが楽チンです。
OpenGL メインであれば、GLUTベースでGUIを使い、必要に応じてカメラ画像の取得や画像処理をOpenCVにやらせる、というのが妥当な感じです。