作った
雑感
良い点はほぼ前回書いてる通りなので、以下に追加で気になった点を書く。
見えてる領域に視線そのまま表示すると引きづられる
慣れれば何とかなりそうだが、地味に影響あるので分離したほうがよいと思われる。
ログを取るなり別撮りで合成するか等の手間が掛かるので割と面倒である。
画面は大きすぎても小さすぎてもよくない
でかすぎると端の精度が落ちる。「 The monitor must be max 24”」とある通りなのでこれは仕方ない。
下限については特に記載が無かったが、小さすぎるものも止しておいたほうが良い。
距離が近づくせいもあると思うが、キャリブレーションの難易度が上がり頭動かして良いエリアがだいぶ狭まる。
6.2は無理、9.7でもかなり厳しい。15~26インチくらいがベストな気がする。
ゲーム無理でしょ
公式のPVでWinタブっぽいのにEyeTribe貼り付けて手持ちでFruitNinjaやってる様子があるが、そもそも操作自体を高精度にするのが難しい上、上記の画面サイズの理由もあり実際全然使い物にならんと思う。
カーソル動かないか、爆弾切りまくるか、そうでないにしても手を前に突き出して固定する筋トレと同時プレイすることになるし誇大広告感ある。
同画面に表示さえできれば何でも使える
キャリブレートの際見ていた画面と同じ画面を見る状況にさえすれば、同座標を見ていることになる。
ソフト自体はWin/Macのみ対応だが、測定対象としては同画面から出力さえできれば何でも使える。
タブレットからEyeTribeの接続された機器にVNCすれば、難易度は高いが無理やりキャリブレート出来なくはない。
キャリブレート時のみ一時的にPCつなげば済むので、環境が許すなら表示側に外部入力さえ使えればサイネージでもゲーセンの筐体でもなんでもいけるかと。
ディスプレイ設置ユニットほしい
これも公式にディスプレイ下部にスタンド使わずくっつけてる絵があるが実際には付いてこない。
画面の前に三脚置くの結構邪魔なのであれも付けて欲しかった。
ある程度上向きの角度をつけないとならないので、そのまま貼り付けるだけでは使えず、三脚使わないと実用的な認識ができない。
EyeProofについて
公式から出た解析用webアプリ。
抽選式のβ版だがEyeTribe持ってると数日でアカウント貰える。
今のところ静止画に対しヒートマップとスキャンパスを作成可能。ヒートマップはこれを使った。
解析対象をWeb上から登録した上でそれと連動するデスクトップアプリのレコーダを起動しデータ収集する。
多数のサンプルからのデータ収集を前提としているらしく、登録時に氏名年齢性別のデータを付加させることが出来る。解析時に特定の対象群のみに絞るような処理やそれを元にした統計処理が出来るっぽい。
選択した可視化の形式が保持されないので一枚一枚選ばなきゃならなかったり、
解析時の画像サイズが解像度に依存しないので高解像度液晶だと画面真ん中に小さすぎる表示になったり、など細かい点は気になったが全体として使い勝手は悪くないしまだβなのでそのうち何とかなると思う。
対応形式増やすつもりもあるようでwebサイトの解析はまもなく対応すると書いてある。
この調子で動画にも対応してほしい。
ソース
SDKが優秀なのでサンプルにちょっと書き足しただけ。簡単。
描画の仕方いじるだけでも地味に遊べる。
やる気はないけどメディアアートとかそういう方向に使えるんじゃねと思った。
import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.Color; import java.awt.Graphics; import com.theeyetribe.client.GazeManager; import com.theeyetribe.client.GazeManager.ApiVersion; import com.theeyetribe.client.GazeManager.ClientMode; import com.theeyetribe.client.IGazeListener; import com.theeyetribe.client.data.GazeData; public class draw extends JPanel { //ウィンドウサイズ final static int winwidth = 854; final static int winheight= 480; //○のサイズ static final int ovalsize = 30; //描画数 static final int trackmax = 30; //解像度取得 static java.awt.GraphicsEnvironment env = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(); static java.awt.DisplayMode displayMode = env.getDefaultScreenDevice().getDisplayMode(); static int width = displayMode.getWidth(); static int height = displayMode.getHeight(); static int[][] positions = new int[trackmax][2]; public static void main(String[] args){ JFrame frame = new JFrame(); draw app = new draw(); frame.getContentPane().add(app); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(winwidth, winheight); frame.setTitle(""); frame.setVisible(true); final GazeManager gm = GazeManager.getInstance(); boolean success = gm.activate(ApiVersion.VERSION_1_0, ClientMode.PUSH); final GazeListener gazeListener = new GazeListener(frame); gm.addGazeListener(gazeListener); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { gm.removeGazeListener(gazeListener); gm.deactivate(); } }); } public void paintComponent(Graphics g){ g.clearRect(0, 0, winwidth, winheight); for ( int i = positions.length-1; 0 <= i ; --i ) { //グラデーションかける Color c = new Color(255 - i * (255/positions.length), 0, i * (255/positions.length), 80 - i * (80/positions.length) ); g.setColor(c); //ポイント間の直線 if(i!=0){ g.drawLine(positions[i-1][0], positions[i-1][1], positions[i][0], positions[i][1]); } //ポイント int size = ovalsize - (ovalsize/2 * i / positions.length); g.fillOval(positions[i][0] - size/2, positions[i][1] - size/2, size, size); if(i==0){ g.setColor(Color.WHITE); size = ovalsize / 2; g.fillOval(positions[i][0] - size/2, positions[i][1] - size/2, size, size); } } } private static class GazeListener implements IGazeListener { static JFrame parent; public GazeListener(JFrame frame) { parent = frame; } @Override public void onGazeUpdate(GazeData gazeData) { int Xpos = (int) gazeData.smoothedCoordinates.x; int Ypos = (int) gazeData.smoothedCoordinates.y; //履歴の保持 for ( int i = (positions.length-1); 0 < i; --i ) { positions[i][0] = positions[i-1][0]; positions[i][1] = positions[i-1][1]; } positions[0][0] = Xpos * winwidth / width; positions[0][1] = Ypos * winheight/ height; if(Xpos == 0 && Ypos ==0){ positions[0][0] = positions[1][0]; positions[0][1] = positions[1][1]; } parent.repaint(); if(Xpos ==0 && Ypos ==0){ return; } } } }