takuYSD

 

いきなりだが,なんかですます調の方が書きやすいので変えようと思う.です.

本題に入ります.
市販のプロジェクタはきっちりと設計されたレンズを使用してるので,
レンズの歪みや収差が気になるなんてことはまずありません.

しかし,学生時代の私のようにレンズやらミラーやら色々組み合わせて謎の光学系を作っていると,
レンズ歪みや色収差が無視できないレベルで発生することがしばしばあります.
例えば,市販のプロジェクタの画角を広げるために強めの凹レンズをくっつけたりするとよく起きる問題です.
そんなことやらねーよって?やらないの?

今回は,glutベースのOpenGLアプリケーションに数行追加するだけで
レンズ歪み・色収差を補正できるソフトを作ったので紹介しようと思います.
glutがよくわからんという人は,きっともっとよくわからん記事だと思います.

まずは次の3つのファイルをダウンロードして下さい.

DistortionCorrection.h と DistortionCorrection.cpp が,歪み補正を実行するクラスを定義したファイルです.
Sample.cpp はこのクラスの使用方法を示したサンプルです.

VisualStudioで空のコンソールプロジェクトを作って3つのファイルを追加します.
glutは事前にインストールしておいて下さい.
コンパイルして実行すると,下図のような画面が出てきます.

キーボードで各種パラメータを調整します.

  • r, g, b: RGBそれぞれの歪み係数を減らす
  • Shift+r, g, b: RGBそれぞれの歪み係数を増やす
  • ↑, ↓, ←, →: 歪みの中心を移動
  • d: デバッグ表示をON/OFF

適当に調整したらこんな感じになります.

画面を投影しながら,できるだけ歪みや収差がなくなるようにパラメータを調整してください.
サンプルプログラムの使い方は以上です.

次に,DistortionCorrectionクラスの使い方を説明します.
まず,プロジェクトにDistortionCorrection.h と DistortionCorrection.cpp を突っ込んで,
glutのコールバック関数を定義しているファイルでヘッダをインクルードします.

#include "DistortionCorrection.h"

次に,クラスのインスタンスを作ります.
Sample.cppではめんどくさいのでグローバルにしています.

DistortionCorrection g_DC;

使用する関数は次の5つです.

初期化

g_DC.init();

色々初期化します.
glutの初期化が終わってからやった方がいいと思います.

歪み中心の指定

float cx = 400.0f;
float cy = 300.0f;
g_DC.setCenter(cx, cy);

歪みの中心を指定します.
単位はピクセルで,解像度に応じて設定して下さい.画面の左下が0です.

歪み系数の指定

float kR = 0.0005f;
float kG = 0.0006f;
float kB = 0.0007f;
g_DC.setParams(kR, kG, kB);

RGBそれぞれの歪みパラメータを個別に設定します.
単位は謎です.結構小さくして下さい.

歪み補正実行

g_DC.runDC();
glutSwapBuffers();

歪み補正を実行します.
glutのディスプレイコールバック関数で,スワップバッファの直前で呼び出すと,
それまでに描画した画像を歪ませて描画します.

デバッグモード切り替え

g_DC.switchDebugMode();

デバッグモードをON/OFFします.
デバッグモード時にはRGB各色の格子模様と,各種パラメータが表示されます.
glutのフォント表示が結構重いので注意して下さい.
OFFならパフォーマンスはそれほど落ちないと思われます.

ね,簡単でしょ?
クラスの中身についての解説は要望があったらやるかもしれません.
きっとこのブログ見てる人ならソース読んだらわかるよね?

今は実験環境が無いのでちゃんと補正できてるか確証が無いのですが,多分大丈夫だと思います.
もしこのソフトを使ってうまくいった方がいましたら,ご一報頂けると幸いです.

 

数ヶ月前に作ってTwitterでつぶやいたきりまとめてなかったネタ.

慶應の日吉キャンパスがある日吉駅.
日吉駅前には謎の銀色で球形のオブジェがあり,待ち合わせなどに利用されている.

ぎんたま

このオブジェを見るたびに,「これどう見てもリファレンス球だろ…」と思っていた.
リファレンス球というのは,映画などで実写にCG合成するときに使うものである.
CGを合成したいシーンに銀とか白の球を置いておいて,カメラで撮影する.
そうするとシーンの環境光の情報を手軽に取得できる.
そしてCGをレンダリングするときに環境光の情報を反映させてやれば,
そのシーンに馴染んだCG合成ができるというわけ.

普通はリファレンス球に映った画像を加工して利用するのであるが,
今回は日吉オブジェ(以下銀玉)の材質をそのままテクスチャとして適当なCGに貼りつける.
そうすると銀玉を好きな形に変形させるARができてしまう.

で,やってみたのがこちら↓
ティーポット

使ったのは例によってOpenCVとOpenGL.
OpenCVで画像中の球の部分をcv::getRectSubPixで切り取ってリサイズしておく.
まだ球の部分を指定するのは今は手動で行っているが,ハフ変換を使って自動化すると良いと思う.
OpenGLでその画像をテクスチャとして読み込んで,スフィアマッピングすれば完成.
スフィアマッピングのやり方は床井先生のページを参考に.
http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20050107

リファレンス球にするのは日吉の銀玉だけじゃなく,球形だったら何でも良い.
最近何かと話題の某テレビ局の丸いのをリファレンスにするとこんなこともできる.
たこ
8chだけにタコ?

この処理はリアルタイムに動作するので動画への適用も可能.
そのうち日吉に撮影に行って動画を作ろうと思う.

 

GLUTを使うとウィンドウの生成とか何も考えなくてもいいので便利である.まともにWindowsアプリケーションでOpenGLの描画をやろうとすると,GLUTに比べて超えるべきハードルが多すぎるので初心者にはちょっと厳しい.ただ,Win32APIの知識があれば,色々と面白いことができる.Macしか持ってない人はさようなら.

今回紹介するのは,GLUTで生成したOpenGLのウィンドウの枠を消す方法.プロジェクタとカメラを使ったインタラクティブなホゲホゲとか,マルチウィンドウでOpenGLのアプリケーション動かしたいとか,そういうときに重宝するTIPSである.

GLUTにはフルスクリーン表示するglutFullScreen()という関数が用意されているが,プライマリのウィンドウでしかフルスクリーンにならない.プロジェクタにはOpenGLの画面をフルスクリーンで表示して,PCのディスプレイにはデバッグ用の情報を表示したいというときに,PC側がプライマリになってないと色々と不便なことが多い.そこで,Win32APIを使って,ウィンドウの枠を強制的に消してウィンドウの位置や大きさも自由に変えてしまおうというのが下記のプログラム.

#include <windows.h>
#include <gl/glut.h>

#define WIN_POS_X	0
#define WIN_POS_Y	0
#define WIN_WIDTH	800
#define WIN_HEIGHT	600

void init(void)
{
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
}

void scene(void)
{
	static double angle = 0;

	glPushMatrix();
	glRotated(angle, 0, 1, 0);
	glutSolidTeapot(1.0);
	glPopMatrix();

	angle += 0.1;
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	scene();

	glutSwapBuffers();
}

void reshape(int w, int h)
{
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(40.0, (double)w/h, 0.1, 100.0);

	gluLookAt(0, 2, 5, 0, 0, 0, 0, 1, 0);

}

void idle(void)
{
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
	switch(key){
		case '\033':
			exit(0);
			break;
		default:
			break;
	}
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
	glutInitWindowSize(800, 600);
	glutCreateWindow("OpenGL Sample");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(idle);
	glutKeyboardFunc(keyboard);

	init();

	//GLのデバイスコンテキストハンドル取得
	HDC glDc = wglGetCurrentDC();

	//ウィンドウハンドル取得
    HWND hWnd = WindowFromDC(glDc);

	//ウィンドウの属性と位置変更
	SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);
	SetWindowPos(hWnd, HWND_TOP, WIN_POS_X,WIN_POS_Y,WIN_WIDTH,WIN_HEIGHT,SWP_SHOWWINDOW);

	glutMainLoop();

	return 0;
}

実行すると,ティーポットがくるくる回ってる枠のないウィンドウがでる.Escキーを押したら終了.やってることは簡単で,GLのウィンドウハンドルを取得して,SetWindowLongでウィンドウの属性をPOPUPに,SetWindowPosで位置とサイズを指定してあげるだけ.右上の×ボタンがなくなってしまうので,キーボード入力などで終了する処理を入れておくと良い.

 

 

さて,昨日まではOpenGLでペンタブの入力を取得する方法について書いていたのだが,作りたいアプリの構成を考えるとOpenCV使ったほうが楽そうなので,OpenCVでも同様にbbTabletでペンタブ情報を取得してみた.

とりあえずglutのときと同じようにリンクしてコンパイルしてみると,盛大にエラーが出る.どうやらOpenCVのlibとbbTabletのlibが衝突しているらしい.ので,リンカの設定で「特定の規定のライブラリの無視」にLIBCMTDとmsvcprtdを追加すると,警告は出るものの,コンパイルは通る.

リンカの設定

で,位置と筆圧をゲットして線を引くだけのコードを書いてみた.

#include "bbtablet.h"
#include "opencv2\\opencv.hpp"

#pragma comment(lib, "bbtabletds_vc10.lib")
#pragma comment(lib, "C:\\OpenCV2.2\\lib\\opencv_core220d.lib")
#pragma comment(lib, "C:\\OpenCV2.2\\lib\\opencv_highgui220d.lib")

using namespace cv;

#define WINDOW_NAME		"Tablet Test for OpenCV"
#define PEN_SIZE		20
#define TOUCH_THRESHOLD 0.1

int main(int argc, char *argv[])
{
	//ウィンドウ生成
	namedWindow(WINDOW_NAME, 1);

	//タブレットの初期化
	bbTabletEvent	tabletEvent;
	bbTabletDevice	&td = bbTabletDevice::getInstance( );
	HWND			hWnd = FindWindow(NULL, WINDOW_NAME);
	td.initTablet(hWnd, bbTabletDevice::SEPARATE_POINTER );

	//画像生成
	Mat image(480, 640, CV_8UC3, Scalar(255, 255, 255));

	Point pen, pen_prev;

	//メインループ
	while(1){
		while(td.getNextEvent(tabletEvent)){
			pen_prev = pen;
			pen.x = tabletEvent.getWinX();
			pen.y = tabletEvent.getWinY();
			float p = tabletEvent.pressure;
			if(p > TOUCH_THRESHOLD){
				line(image, pen_prev, pen, Scalar(0,0,0), (int)(PEN_SIZE*p), 8, 0);
			}
		}

		//ウィンドウに表示
		imshow(WINDOW_NAME, image);

		//Escキーで終了
		if(waitKey(1) == 27)break;
	}

	return 0;
}

実行結果はこんな感じ.

息子

結構綺麗に描けるもんだ.曲線補完とかアンチエイリアスとか掛けたくなるね.

 

 

 

前回コンパイルしたlibファイルはVC10用なのでbbtabletds_vc90.lib を勝手にbbtabletds_vc10.libに名前変更.

glutのプロジェクトフォルダにlibファイルとヘッダファイル(bbtablet.h)をコピーする.

で,以下のようにインクルード.

#include <windows.h>
#include <stdio.h>
#include <gl/glut.h>
#include "bbtablet.h"
#pragma comment(lib, "bbtabletds_vc10.lib")

あとは,bbTabletのホームページに書いてある通りに,ウィンドウハンドルを取得して,reshapeコールバック関数あたりで適当に初期化して,idleコールバック関数で適当に呼び出して使う.とても簡単.

HWND getCurrentHWND()
{
	HDC gldc = wglGetCurrentDC();
	HWND win = WindowFromDC(gldc);
	return win;
}

static void reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(40.0, (double)w/h, 0.1, 100.0);

	static bool firstTime=true;
	if (firstTime) {
		void *wnd = getCurrentHWND();
		bbTabletDevice &td = bbTabletDevice::getInstance( );
		td = bbTabletDevice::getInstance();
		td.initTablet( wnd, bbTabletDevice::SEPARATE_POINTER );
		firstTime = false;
	}
}

static void idle(void)
{
	static bbTabletEvent evt;
	bbTabletDevice &td = bbTabletDevice::getInstance();
	while (td.getNextEvent(evt)){
		printf("x:%d, y%d:, p:%f\n", evt.getWinX(), evt.getWinY(), evt.pressure);
	}

	glutPostRedisplay();
}

描画までやろうかと思ったけど,ちょっと方針転換するのでここでおしまい.

以下次回.

 

 

 

一度ペンタブを使ったアプリを作りたかったので作るよ.

アプリの詳細は追々.

まずはVC++2010,OpenGLでペンタブの入力を使うための覚書.

 

C++でペンタブレットの入力を扱うには,WinTabというSDKを使う.

しかし,WinTabは公開終了しているらしい.でも大丈夫.問題ない.

WinTabのdll自体はwacomのタブレットドライバから入手可能.

Wacomの開発者向けページのサンプルプログラムに必要なヘッダファイルがあるのでそこから

WINTAB.h,MSGPACK.h,PKTDEF.h を入手する.

 

Win32アプリだったらこのサンプルをゴニョゴニョして作ればいいんだけれど,glutで簡単に使いたい.

調べたらbbTabletというライブラリを発見.

ヘッダファイルをインクルードしてライブラリファイルリンクするだけでglutで使えるらしい.

bbtablet-2009-01-07.zip Source + precompiled libs というのをダウンロードして解凍.

libファイルがvc9用までしかなかったのでコンパイルしてみる.

[展開したフォルダ]\bbtablet\MSVC\bbtablet_vc9.sln を開いてVS2010用に変換.

ビルドするとwintab.hがないとか言われるので,

[展開したフォルダ]\bbtablet\include

にさっきゲットした WINTAB.h,MSGPACK.h,PKTDEF.h をコピー.

これでbbtablet_vc9というプロジェクトはビルドできる.

他のglのサンプルはまだコンパイルが通らないが,ライブラリファイルさえゲット出来ればいいので放置.

[展開したフォルダ]\bbtablet\lib

の bbtabletds_vc90.lib というファイルがVC10用になったはず.

あとは自前のプロジェクトに突っ込めば動く.詳細は次回.

 

 

 

 

VR/AR/CV系のプログラムのtips,ネタ研究,絵日記をまとめるブログです.

ゆっくりしていってね!

© 2014 Saikoro Laboratory Blog Suffusion theme by Sayontan Sinha