3点マルチタッチ

動画その6でのマルチタッチはカーソル2つで行ってたけど、使ってると少し不便なので、カーソル3つでのマルチタッチに改良してみた。

カーソル2つのマルチタッチも面倒くさかったけど、3つだとさらに計算が面倒くさかった。3次元だからなおさらだ。
しかし、数学的には大したことはやっていない。基本的にはベクトルの内積外積や行列の計算がほとんど。出来てしまえばなるほどで終わるが、出来上がるまでがしんどい。

ARDeskTop上で動画再生(その2)

OpenCVのcvCaptureFromFile関数では動画の音声トラックは再生されないので、試しにDirectShowでも同時に再生してみた。まあ、あたりまえだが、そのままでは音と映像がズレてしまう。
で、DirectShowのGetPositions関数で現在の再生位置を取得して、それに合わせて動画を再生してみた。
同期成功!

ちなみに、FLV Splitterをインストールするとfpsが30くらいまでのならflvファイルがDirectShowでも再生できるようになる。
http://www.gigafree.net/media/codec/flvsplitter.html

あとは、このままではDirectShowの別ウインドウでも動画が再生されてしまうので、音声のみを再生できるようにすれば終了。

int CCVVideoManager::Animate(void)
{
	if(!capture) return(1);
	if(!CAnimeManager::Animate()) return(1);

	int n;

	if(fps > 40.0f) {
		if(elapsedTimeMS * fps < counter) return(1);
		n = elapsedTimeMS * fps - counter + 1;
	} else {
		//DirectShowから現在の再生位置を取得して何フレーム進めるか決める
		LONGLONG pCurrent, pStop;
		pMediaSeeking->GetPositions(&pCurrent, &pStop);
		float current = pCurrent * 0.0000001;

		if(current * fps < counter) return(1);
		n = current * fps - counter + 1;
	}

	IplImage* image;

	for(int i = 0; i < n; i++) {
		image = cvQueryFrame(capture);
		if(!image) {
			Stop();
			return(0);
		}
	}

	counter += n;

	if(!frame) frame = cvCreateImage(cvSize(image->width / 3, image->height / 3), IPL_DEPTH_8U, 3);
	cvResize(image, frame, CV_INTER_CUBIC);
	((CCVPicture*) component)->SetTexture(image);

	return(0);
}


bool CCVVideoManager::OpenVideoFile(wchar_t* pFileName, double size)
{
	char cFileName[256];

	setlocale(LC_ALL, "ja");
	wcstombs(cFileName, pFileName, 256);

	//OpenCV
	capture = cvCaptureFromFile(cFileName);
	if(!capture) {
		return(false);
	}

	fps = (float) cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);

	//DirectShow
	if(fps <= 40.0f) {
		IMediaPosition*		pMediaPosition;

		//DirectShowの初期化は本当はここでやったらダメだろうな。
		CoInitialize(NULL);
		CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (LPVOID*) &pGraphBuilder);
		pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*) &pMediaControl);
		pGraphBuilder->QueryInterface(IID_IMediaPosition, (LPVOID*) &pMediaPosition);
		pGraphBuilder->QueryInterface(IID_IMediaSeeking, (LPVOID*) &pMediaSeeking);

		pGraphBuilder->RenderFile((BSTR) pFileName, NULL);

		//再生時間取得
		REFTIME length;
		pMediaPosition->get_Duration(&length);
		animetionTimeMS	= (float) length;
		pMediaPosition->Release();

		pMediaControl->Run();
	} else { //DirectShowでは再生不可能
		animetionTimeMS = 1000.0f; //適当
	}

	Play();
	Animate();


	return(true);
}

参考サイト:
Geekなぺーじ : DirectShowプログラミング [VC++]
http://www.geekpage.jp/programming/directshow/

ARDeskTop上で動画再生

OpenCVの動画再生関数cvCaptureFromFileを使用してARDeskTop上で動画を再生してみた。とは言え、dandelion氏の協力なくしてはすんなりコーディングはできなかったであろう(本当にありがとうございます)。
CAnimeManagerクラスから動画再生用にCCVVideoManagerクラスを派生させて処理する。同期を取るために何フレームかは飛ばしたり、何もしなかったりする。
OpenCVのみなのでまだ音は出ない。


int CCVVideoManager::Animate(void)
{
	if(!capture) return(1);
	if(!CAnimeManager::Animate()) return(1);
	if(elapsedTimeMS * fps < counter) return(1);

	IplImage* image;
	int n = elapsedTimeMS * fps - counter + 1;

	for(int i = 0; i < n; i++) {
		image = cvQueryFrame(capture);
		if(!image) {
			Stop();
			return(0);
		}
	}

	counter += n;

	if(!frame) frame = cvCreateImage(cvSize(image->width / 3, image->height / 3), IPL_DEPTH_8U, 3);
	cvResize(image, frame, CV_INTER_CUBIC);
	((CCVPicture*) component)->SetTexture(image);

	return(0);
}


bool CCVVideoManager::OpenVideoFile(wchar_t* pFileName, double size)
{
	char cFileName[256];

	setlocale(LC_ALL, "ja");
	wcstombs(cFileName, pFileName, 256);

	capture = cvCaptureFromFile(cFileName);
	if(!capture) {
		return(false);
	}

	fps = (float) cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);

	animetionTimeMS	= 1000.0f;
	Play();
	Animate();

	return(true);
}

ただOpenVideoFile関数内でPlayとAnimate関数入れておかないとうまく実行できない。

OpenCVで色認識

OpenCVのサンプルをいじって簡単な色認識を作ってみた。
大まかな処理の流れ。


1.Webカメラの映像を画像として取り込む。
2.取り込んだ画像をRGBからHSVに変換する。
3.HSV色空間内の特定の範囲だけマスキングして表示する。


HSVの方が特定の色を抜き取りやすいらしい。
http://render.s73.xrea.com/pipe_render/2008/06/artoolkit-hsv.html

こんな感じに一定の範囲の色を認識させる。

実行結果はこんな感じ。

ソースコード

#include 
#include 


void GetMaskHSV(IplImage* src, IplImage* mask,int erosions, int dilations)
{
	int x = 0, y = 0;
	uchar H, S, V;
	uchar minH, minS, minV, maxH, maxS, maxV;

	CvPixelPosition8u pos_src, pos_dst;
	uchar* p_src;
	uchar* p_dst;
	IplImage* tmp;


	tmp = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);

	//HSVに変換
	cvCvtColor(src, tmp, CV_RGB2HSV);

	CV_INIT_PIXEL_POS(pos_src, (unsigned char*) tmp->imageData,
					   tmp->widthStep,cvGetSize(tmp), x, y, tmp->origin);

	CV_INIT_PIXEL_POS(pos_dst, (unsigned char*) mask->imageData,
					   mask->widthStep, cvGetSize(mask), x, y, mask->origin);

	minH = 100;	maxH = 115;
	minS = 80;	maxS = 255;
	minV = 120;	maxV = 255;
	for(y = 0; y < tmp->height; y++) {
		for(x = 0; x < tmp->width; x++) {
			p_src = CV_MOVE_TO(pos_src, x, y, 3);
			p_dst = CV_MOVE_TO(pos_dst, x, y, 3);

			H = p_src[0];	//0から180
			S = p_src[1];
			V = p_src[2];

			if( minH <= H && H <= maxH &&
				minS <= S && S <= maxS &&
				minV <= V && V <= maxV
			) {
				p_dst[0] = 255;
				p_dst[1] = 255;
				p_dst[2] = 255;
			} else {
				p_dst[0] = 0;
				p_dst[1] = 0;
				p_dst[2] = 0;
			}
		}
	}

	if(erosions > 0)  cvErode(mask, mask, 0, erosions);
	if(dilations > 0) cvDilate(mask, mask, 0, dilations);

	cvReleaseImage(&tmp);
}


int main(int argc, char **argv)
{
	int c;
	CvCapture* capture = 0;
	IplImage* frame = 0;
	IplImage* mask = 0;
	IplImage* dst = 0;

	if(argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit(argv[1][0])))
		capture = cvCreateCameraCapture(argc == 2 ? argv[1][0] - '0' : 0);

	frame = cvQueryFrame(capture);
	mask = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 3);
	dst = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 3);

	cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("dst", CV_WINDOW_AUTOSIZE);

	while(1) {
		frame = cvQueryFrame(capture);
		GetMaskHSV(frame, mask, 1, 1);
		cvAnd(frame, mask, dst);

		cvShowImage("src", frame);
		cvShowImage("dst", dst);
		c = cvWaitKey(10);
		if(c == 'q') break;
	}

	cvDestroyWindow("src");
	cvDestroyWindow("dst");

	cvReleaseImage(&frame);
	cvReleaseImage(&dst);
	cvReleaseImage(&mask);
	cvReleaseCapture(&capture);

	return(0);
}

参考にしたサンプルはこちら。
http://opencv.jp/sample/accumulation_of_background.html
http://chihara.naist.jp/opencv/?%BF%A7%BE%F0%CA%F3%A4%CB%A4%E8%A4%EB%CE%CE%B0%E8%C3%EA%BD%D0

テクスチャをOpenCVで読み込み

jpegファイルの読み込みをIndependent JPEG GroupのライブラリからOpenCVに変更した。これでjpeg以外のフォーマットも簡単に読み込める。最初っからこうしとけばよかったなあ。
いずれかは動画の再生もサポートしたい。できればflv。

あと、色認識にもトライしてみたが、ちょっといろいろと厳しいかも。
入力画面の半分にサイズを落として、色を探せたらサクサク動くとは思うけど。
誰か代わりにやってくれないかな。

参考サイト:http://wiki.livedoor.jp/mikk_ni3_92/

半透明オブジェクト(その2)

前に日記の続き。
オブジェクトを裏っかえすとヘンになってしまうバグは、視線ベクトル(オブジェクトのポジション)とオブジェクトの行列のz軸との内積で裏か表を判定して、透明のポリゴンを描く位置をずらして直した。

あと描く順番によって、透明ななのか透明じゃないのかよくわからなくなってしまうバグも、距離の遠い順に並び変えて、遠い順に描いていくことで修正した。


だましと言うかトリッキーな手法だけど、透明なオブジェクトを描く一番簡単な方法かも。

半透明オブジェクト

どうも、ARDeskTop開発者のわうわうふーわです。
ゆる〜く現状をブログにしていきます。

半透明のオブジェクトに描写は、そのときだけデプス処理をオフにしてやってるんですが、それだと後に描いたオブジェクトが奥にあろうが、手前に見えてしまう欠点があります。

で、どうにかならないかと思い、フレームの裏に透明なポリゴンを描くと言う手を思いつきました。

ただし、裏っかわにひっくり返すと変な感じになるし、透明ななのか透明じゃないのかよくわからない状態になります。

でも、前後の関係はよくわかるようになりました。
法線ベクトルと視線ベクトルの内積を計算して、どっち向いてるか計算もしてみようと思っています。