記事の目次

    今回は、赤外線とプロジェクタを利用したタッチテーブルの実装方法を紹介します。

    TEXT_高田稔則 / Toshinori Takata(Codelight
    EDIT_小村仁美 / Hitomi Komura(CGWORLD)

    今回のサンプルデータはこちら
    https://github.com/toshinoritakata/FingerTrackerSample

    赤外線とプロジェクタによるタッチテーブル

    こんにちは、高田です。今回は赤外線とプロジェクタを利用したタッチテーブルのしくみを考えていこうと思います。

    様々なコンテンツや実装が出ていますが、中でも「reactable」と呼ばれるタッチテーブル式の電子楽器は歌手のビョークがライブツアーで利用したことで一躍有名になりました。

    Reactable Live! Demo Performance

    結構歴史が古く面白いしかけなのですが、最近はあまり積極的に利用されていない印象です。インターネットから得られる情報は大体10年ほど前のものばかりで、リンク切れも少なくありません。

    大きめのタッチパネルを使えばコンテンツを作るのも楽ですし、タッチだけのモジュールも多く出ています。プロジェクタ、赤外線ライト、カメラを使うのでハードの調整が面倒なことやそれらを納める装置を作る必要があるのが難しいところだと思っています。

    しかし、タッチパネルではreactableのように多様なマーカーの種類や角度を検出したりはできませんし、タッチテーブルは点以外の形状を認識できる強みがあります。

    以下はそれを上手く使ったコンテンツ例です。
    ●Maker Faire Tokyo 2015 にレゴ x ハードウェア x プロジェクションなシューティングゲーム LITTAI を出展してきた
    http://tips.hecomi.com/entry/2015/08/06/003049

    しくみがシンプルなため、自分で実験することが可能です。下記の写真は筆者が自宅で作ったタッチテーブルです。これを見ていきましょう。

    枠組みは、ごみ袋をかけるタイプのごみ箱で作っています。

    ハードウェアの準備

    ●赤外線カメラとライト

    まずハードウェアを準備していきます。先ほどの写真の右下に映っているのはOptiTrackのSlim 3Uというモーションキャプチャ用の小型カメラです。小型で安定性が高いので納品物としても問題ない品ですが、価格は10万円程度と、個人で用意するにはちょっとハードルが高いかもしれません。

    赤外線カメラであればKinectでもRealSenseでも問題ありませんので、もう少し安価なものを探してみます。Amazonで調べるとELPというメーカーのものがいくつか出ていましたので、試しに基板むき出しのこちらのカメラを買ってみました。PCはWebカメラとして認識します。


    このカメラを見ると、赤外線を使ったコンテンツにとって大切な事柄をいくつか学ぶことができます。ライト部分を見てみると赤外線のLEDに混ざって照度センサがついています。このおかげで環境が一定以上明るいとLEDライトが発光しないようになっています。市販されている赤外線ライトにはほぼこの照度センサが付いています。利用した赤外線ライトも中央に照度センサが見えます。

    この手の製品は取扱説明書が付いてこないことが多いため、このセンサのことを知らないとライトが点灯せず悩むことになります。照度センサをパテで埋めてみると、うっすら紫の光が見えるようになりました。



    赤外線ライトを点灯してみた様子。照度センサをパテで埋める前(左)、埋めた後(右)

    ●IRフィルタ

    カメラ画像を確認すると、モノクロカメラと変わらない画像が見えます。赤外線カメラと書いてあっても暗視カメラとして売っている場合は、ある程度可視光も見えるようになっていることが多いですが、赤外線だけ見えるようにしたいのでちょっと加工します。ネジでマウント部分を外すことができるので、レンズとイメージセンサの間にIR(赤外線)フィルタを挟み込みます。

    IRフィルタとして入手しやすいのはFUJIFILMのもので、IR-84やIR-90などの型番があります。「IR-XX」とはXX0nm以下の波長の光をカットすることができるという意味です。IR84は840nm以下の波長をカットできます。

    赤外線カメラは850nmと940nmの赤外線を使っている製品が多いため、IR-84とIR-90が用意してあると便利です。このカメラのように基板に直接レンズが付いているカメラを「ボードカメラ」と呼び、ほとんどが「M12レンズ」と呼ばれるレンズを使用しています。M12レンズには多種多様な種類があるので、状況に応じてレンズを選ぶことができます。これはRealSenseなどの、レンズが組み込まれたカメラを選択した場合には得られない利点です。


    IR-84のフィルタを取り付けて画面を撮影した状態をモニタに表示させ(下図右上)、iPhoneのカメラでスクリーン全体を撮影しました。


    赤外線フィルタを通すと、このように画面に表示されている画像が見えなくなります。この特性を利用することでスクリーンに投影した映像を無視して、タッチした指のみを認識することができるようになります。

    ●プロジェクタ

    映像はカメラと同様真下から投影します。今回はSONYの小型プロジェクタを利用しました。こちらも投影サイズがたまたまぴったりでした。

    ●アクリル板

    天板には180mm✕320mmの透明アクリルパネルを置き、40g/㎡のトレーシングペーパーを貼って簡易的な投影面にしています。ここまでの部品を組み合わせてカメラで見た映像は、下画像のように投影面に接地している指先だけ白く見えるようになります。この白い部分を画像処理で取り出すのがタッチテーブルの基本的なしくみです。


    次ページ:
    タッチテーブルのしくみ

    [[SplitPage]]

    タッチテーブルのしくみ

    なぜ接地している面だけ明るく見えるのでしょうか? こちらのサイトに、タッチテーブルの作り方が詳しく書かれていました。

    タッチテーブルの実装には、大きく分けて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は検出オブジェクトのトラッキングも行なってくれます。しかし、検出点がノイズで明滅してしまうようなときにはちらつきの問題が出たので、一定の時間以上見え続ければ"誕生"、消え続ければ"死亡"するようにカスタムのトラッキング処理を追加しました。

    次ページ:
    Unityとの連動

    [[SplitPage]]

    Unityとの連動

    ●TUIO

    多くのタッチインターフェイスは「TUIO」と呼ばれるプロトコルを利用して通信することができます。今回もTUIOで通信できるようにしてみました。

    TUIOは通信プロトコル「OSC」の上に構築されたインターフェイスです。「タッチされた」、「移動した」、「離された」という状態をコマンドとしてやり取りします。タッチに変化がないとデータ送信が発生しません。

    公式のTUIOのC#実装はUnityとは相性が悪く、ビルドするとデータを受信することができませんでした。Unityフォーラムのこちらのディスカッションを参考にして、下記の実装を使いました。

    ●GitHub - valyard/TUIOsharp
    https://github.com/valyard/TUIOsharp

    Unity側では以下のような記述で認識したデータを利用することができます。

    public class MyTUIOCursor : MonoBehaviour
    {
        private TuioServer _tuioServer;
        private Dictionary _cursorList = null;
    
        void Start()
        {
            _cursorList = new Dictionary();
            _tuioServer = new TuioServer();
            var cursorProcessor = new CursorProcessor();
    
            // 新しく発生した点データを受信
            // カーソル情報をIDと紐づけておく
            cursorProcessor.CursorAdded += (sender, e) =>
            {
                var entity = e.Cursor;
                lock (_tuioServer) { _cursorList.Add(entity.Id, entity); }
            };
    
            // 死亡した点データを受信
            // 死亡IDを削除
            cursorProcessor.CursorRemoved += (sender, e) =>
            {
                var entity = e.Cursor;
                lock (_tuioServer) { _cursorList.Remove(entity.Id); }
            };
    
            // 位置の更新、カーソルのリストは変更しなくてもいい
            cursorProcessor.CursorUpdated += (sender, e) => { };
    
            _tuioServer.Connect();
            _tuioServer.AddDataProcessor(cursorProcessor);
        }
    }
    

    位置データを利用するときは、以下のようにします。

    foreach (var item in _tuioCursor.Cursors.Values) {
        var p = new Vector3(item.X, item.Y, 0);
    }
    

    完成

    認識側で指を認識してトラッキングしている様子です。


    認識した情報をTUIOでUnityに送り、VFXGraphのパーティクルの発生源にしてみました。


    Profile.

    高田稔則/Toshinori Takata(Codelight)
    Codelight株式会社 代表取締役・インタラクションエンジニア
    フリーランス、株式会社TBSテレビ等で映画CG制作、株式会社ソニー・コンピュータエンタテインメント(現 ソニー・インタラクティブエンタテインメント)でPS4のOSD開発などを経て2006年にCodelight株式会社を設立。インタラクティブコンテンツの制作を中核として、製造業向けのプロトタイプ開発なども行う
    www.codelight.co.jp