タッチテーブルのしくみ
なぜ接地している面だけ明るく見えるのでしょうか? こちらのサイトに、タッチテーブルの作り方が詳しく書かれていました。
タッチテーブルの実装には、大きく分けて2つの方法があるようです。一番ポピュラーだと思われるのは「Rear Diffused Illumination(後方拡散光源式)」と呼ばれる方法で、カメラとIRライトを同じ側に設置するパターンです。今回筆者が作ったのもこれで、一番簡単に設置できます。
Rear Diffused Illumination Multitouch Technique | Seth Sandlerより引用
アクリルに貼られている投影面兼拡散面(Diffuser)のトレーシングペーパーに赤外線(Infra Red)が当たり、光が拡散されます。拡散面から離れるほど光が拡散されるので像がぼけ、アクリル面(Acrylic Surface)に触れると赤外線がそのまま反射されるためにカメラからは白く見えます。この方法は設置が簡単な反面ライトの反射が認識の邪魔になる場合があります。
もうひとつが「Frustrated Total Internal Reflection(FTIR)」を使った手法です。FTIRは減衰全反射と訳される自然現象です。身近なところでは写真のように水を入れたグラスをもつと確認することができます、接しているところだけ指がはっきり見えるのがわかると思います。
この現象を応用したセンシング技術は1970年代から指紋センサなどで利用されてきたようですが、マルチタッチの検出に利用されたのはニューヨーク大学のジェファーソン・Y・ハン氏が2005年に論文「Low-Cost Multi-Touch Sensing through Frustrated Total Internal Reflection」を発表してからのようです。
Frustrated Total Internal Reflection | Seth Sandlerより引用
アクリル板に側面から赤外線を照射して全反射(Total Internal Reflection)を起こし、光をアクリル板に閉じ込めます。その状態でアクリル板に指が触れると赤外線は「フラストレーション(減衰)」を起こし、内部反射を逃がし下に反射するので、赤外線カメラでそれを観察できるそうです。このしくみを作るにはIRライトアレイを組まなければならないため少し面倒ですが、認識の安定性は高いものになると思います。
この方式を使ったコンテンツで印象に残っているのは、2011年にイギリスのVFXプロダクションThe Millが発表した「MillTouch」です。両側にIRライトを設置した透明のスクリーンに映像が投影され、タッチでコンテンツを操作することができます。
Mill Touch 'Behind the Scenes' from The Mill on Vimeo.
ソフトウェアの実装
タッチテーブルで利用する画像処理は、インタラクティブコンテンツで必要とされる項目の学習にちょうど良いと思います。指検出とトラッキングを行い、データを送出するサンプルコードを用意しました。
●今回のサンプルコード
https://github.com/toshinoritakata/FingerTrackerSample
いくつか足りないコードがあるのでそのままではビルドできませんが、処理の参考にはなると思います。
認識側の実装にはopenFrameworksを利用しています。openFrameworksはクリエイティブコーディングのためのC++ライブラリです。UnityのようなGUIは一切ありませんが、インタラクティブコンテンツで必要と思われるあらゆる種類のアドオンが用意されておりとても使いやすい環境です。
主に利用しているのはOpenCVのライブラリです。openFrameworksではOpenCVのラッパーとして「ofxOpenCV」が標準で利用可能ですが、多くの人がそれとは別の「ofxCv」というアドオンを利用していますので導入した方が良いでしょう。
●GitHub - kylemcdonald/ofxCv: Alternative approach to interfacing with OpenCv from openFrameworks.
https://github.com/kylemcdonald/ofxCv
それでは、openFrameworksで画像処理を行う流れを見ていきましょう。
1:カメラ画像取得
これはそのままで、カメラから画像を取得する処理です。openFrameworksの場合Webカメラからの入力にはofVideoGrabberが用意されていますが、ゲインなどの値を設定することができません。内部で利用されているvideoInputをそのまま利用した方が良いでしょう。videoInputはWindowsだけで機能し、MacOSやLinuxでは別のライブラリで対応します。
2:グレイスケール化
指の認識にRGB情報は不要なので、画像をグレイスケール化します。OpenCVで以下のようにするだけで、RGB画像をモノクロ画像に変換してくれます。
cv::cvtColor(img, gray, cv::COLOR_RGB2GRAY);
3:パース補正
指の認識にはカメラが天板をゆがみなく映す必要がありますので、GUIで補正ができると便利です。これを行うために、OpenCVには4点の対応する角を指定すると、補正に必要な透視変換行列を計算してくれる機能があります。getPerspectiveTransform()とwarpPerspective()を一組で使います。
cv::Mat M = cv::getPerspectiveTransform(src, dst);
cv::warpPerspective(gray, gray, M, gray.size(), cv::INTER_NEAREST);
下の画像は四隅を指定している様子(左)と補正結果(右)です。チェッカーが画面に揃っているのがわかります。
本来はこの前にレンズのゆがみ補正を行う必要があるのですが、今回はレンズのゆがみがあまりないので行なっていません。レンズゆがみはチェッカーボードをいくつかの画角から撮影したものをOpenCVに渡すと計算してくれます。様々な情報が出ていますが、計算を行うサンプルを下記に置きました。
●openFrameworksとレンズゆがみ計算 · GitHub
https://gist.github.com/toshinoritakata/c4c1909ea05136db416e8b8ea4d4b41d
4:ガンマ補正
指を取得するためには白黒がはっきり見えるのが理想ですが、なかなかねらったようにはなりません。そのようなときは、画像にガンマ補正をかけます。画像の明暗がはっきりしてきました。
5:2値化
これを白黒の画像に変換します、画像を黒と白の2つの値に分けるので「2値化」といいます。
cv::threshold(gray, gray, threshold, 256, CV_THRESH_BINARY);
6:ラベリング(コンター)
続いて、連続している画像をまとめていきます。OpenCVではfindContours関数を使います。
std::vector > allContours;
cv::findContours(thresh, allContours, contourFindingMode, simplifyMode);
白い部分がいくつかの島に分けられていることが確認できます。
ここまでは本当に古典的な方法で、探せば数多くの詳細な情報があります、さらにofxCvに含まれているofxContourFinderを使うと、2値化からラベリングまで一括で行うことができます。
●ofxCvContourFinder | openFrameworks
https://openframeworks.cc/documentation/ofxOpenCv/ofxCvContourFinder/
7:トラッキング
ofxContourFinderは検出オブジェクトのトラッキングも行なってくれます。しかし、検出点がノイズで明滅してしまうようなときにはちらつきの問題が出たので、一定の時間以上見え続ければ"誕生"、消え続ければ"死亡"するようにカスタムのトラッキング処理を追加しました。