背景
コンピュータービジョンの分野だと、カメラの焦点距離や位置・姿勢情報を推定することをカメラキャリブレーションと呼んでいると思います(正確には幾何学的なキャリブレーション?)。
これらの情報が推定できれば、ARでCGを重畳させたり、2台以上のキャリブレーション済みのカメラを使って、撮影シーンの3次元情報を推定したり色々できるようです。
また、世の中にはOpenCVというライブラリがあって、このライブラリを使えは、ゼロからカメラキャリブレーションのプログラムを実装する必要もなさそうです。
一方で、プロジェクターの幾何学的キャリブレーションとなると、カメラキャリブレーションと比べてネット上での情報が少ないと思いました。
というわけで、プロジェクターの幾何学的キャリブレーションをやってみたので、そのメモを書きます。
プロジェクターの幾何学的キャリブレーションとは?
プロジェクターの焦点距離とか光軸の位置(画像上の位置)を求めることだと思います。
カメラは、シーンから入射してくる光を画像として受け取ります。一方でプロジェクターは入力画像をシーンに出射します。光の進行方向は逆だけど、光の経路はどちらも同じカンジ。ということはプロジェクターもカメラと同じようにキャリブレーションできる、という話だと思います。
前提知識(この記事で説明を省略する内容)
- カメラキャリブレーション
- ステレオカメラのキャリブレーション
- 使用ライブラリ/ソフトのインストール方法
実行環境
- windows 8.1(64ビット)
- python 2.7
- Microsoft Visual Studio Express 2013 for Windows Desktop
- OpenCV 3.1.0
- プロジェクター
- カメラ
作業&処理の流れ
- カメラキャリブレーション用のパターンを印刷して板かなにかに貼る
- ハミングカラーコード画像を作る
- プロジェクターを使って、ハミングカラーコードを板に投影してカメラで撮影する
- 撮影画像をデコードする
- 板のパターンを検出して、検出点の座標(カメラ画像上の座標)をデコード情報に基づいてプロジェクター上の座標に変換して、その対応点セットからステレオキャリブレーションする。
上記の作業&処理の流れは、要は、どうにかしてOpenCVに既に実装されているカメラキャリブレーション関数を利用して楽したい、というカンジ。
キャリブレーション用板の画素座標(プロジェクター画像上の画素座標)さえ分かれば、後はOpenCVにお任せできるので、どうにかしてプロジェクター画像上の座標を求めたい。そのためにハミングカラーコードを使います。
1. カメラキャリブレーション用のパターンを印刷して板かなにかに貼る
OpenCVに実装されているキャリブレーションでは、処理の途中で、市松模様のコーナー点を画像上で検出して、そのコーナー点情報に基づいて焦点距離などを推定します。
まずは、そのためのパターンを印刷して板に貼ります。
今回使ったのは↓のようなものです。OpenCVをインストールすると、白黒の市松模様の画像(pdfかも)が一緒に入ってくると思います。今回は、黒色を少し明るくして灰色にしました(プロジェクター光の反射光を観測したいから)。
2.ハミングカラーコード画像を作る
ハミングカラーコードは文献[1]に書かれている数列のようなものです。
例えば文献[1]中のFigure.1にあるような数列では、6767という数列はかならず1回しか現れません。
つまり、数列を色に変換して、その縞模様をプロジェクターからシーンに投影した場合、その色の組み合わせから、カメラ座標とプロジェクターの座標の対応が分かるというわけです。
文献[1]のFigure. 1のカラーコードを生成するプログラムは以下です。
実行すれば、カラーコード画像が生成されます。
https://github.com/kibekibe/structured_light/blob/master/hamming_color_code/generate_hamming_code.py
3. プロジェクターを使って、ハミングカラーコードを板に投影してカメラで撮影する
1.で作成した板に対して、真っ白の画像、2で作った縞模様(縦と横で計2枚)をそれぞれ投影します。
この3枚を1セットとして、板の向きや位置を変えながら複数セット撮影します。このとき、カメラとプロジェクターは動かさず、固定したままにします。
真っ白パターンを撮るのは、後で、市松模様のコーナー点を検出する処理があるからです。
4. 撮影画像をデコードする
文献[1]では高尚なアルゴリズムで高度なパターンマッチングを実現していますが、
レベルが高そうだったので、それは実装せずに、単純に一致するパターン列を探すだけのプログラムを書いてみました。
縦模様の画像から、画像の横軸方向の対応を探します。同様に横模様の画像から、画像の縦軸方向の対応を探します。これらの対応点探索処理は、スキャンラインごとに独立で行いました。
デコードするプログラムは以下です。
https://github.com/kibekibe/structured_light/blob/master/hamming_color_code/hamming_decode.py
実行時に引数で-in_dirオプションを付けて画像フォルダを指定します。
例えば、以下のように実行するとcap_chessboardというフォルダにある画像に対してデコード処理を行います。
python hamming_decode.py -in_dir cap_chessboard
実行すると、プロジェクターの対応座標値が格納された画像が生成されます。画像フォーマットはexr画像とpng画像の2種類です。pngはデコード結果の確認用に出力しています。
実行結果例は以下です。
左が入力画像で、右がデコード結果の画像です。プロジェクターの対応座標値が輝度値を表しています。
ちなみに上記プログラムでは、デコード結果に対してローパスフィルターを掛けています。
5. 板のパターンを検出して、検出座標をデコード情報に基づいてプロジェクター上の座標に変換して、その対応点セットからステレオキャリブレーションする。
プロジェクター画像上でコーナー点を見つけるために、まずカメラ画像上でコーナー点を探します(コーナー点を検出してくれる関数がOpenCVに実装されていて、それを呼ぶだけです)。そして、その検出したコーナー点の画像座標をデコード情報に基づいてプロジェクター画像座標上のコーナー点位置に変換します。
あとは、検出したコーナー点の対応点セットを入力としてステレオキャリブレーションします。ステレオキャリブレーション自体はOpenCVで実装されているので、然るべき関数を呼び出すだけです。
そのプログラムは以下です。
https://github.com/kibekibe/structured_light/blob/master/projcalib_from_decoded_imgs/main_projcalib.cpp
プログラム、サンプルデータセット
-
プログラム
https://github.com/kibekibe/structured_light/tree/master/hamming_color_code -
サンプルデータ
https://github.com/kibekibe/structured_light/tree/master/sample_data/cap_chessboard
参考文献
[1] Hamming Color Code for Dense and Robust One-shot 3D Scanning. Shuntaro Yamazaki, Akira Nukada, Masaaki Mochimaru. Proceedings of the British Machine Vision Conference (BMVC2011), pages 96.1-96.9. BMVA Press, September 2011.