Tobii Eye Tracker 4cを使ってVisual Studioで視線座標，目の位置座標，頭の位置座標と回転座標を取得する．

#はじめに
ゲーム用で安価な[Tobii Eye Tracker 4C](https://www.tobiipro.com/ja/landing-pages/tobii_Eye_Tracker_4C/)を使って視線座標，目の位置座標，頭の位置座標と回転座標を取得して，簡単なゲームなどのソフト開発を行う．商用で使う場合には別途ライセンスが必要なため注意が必要．

なぜか開発用のストリームエンジンライブラリ：StreamEngine libraryが本当に見つけづらい．色々と面白い開発ができそうなので紹介する．

使用機材
[Tobii Eye Tracker 4C](https://www.tobiipro.com/ja/landing-pages/tobii_Eye_Tracker_4C/)　：約45000円

環境
Windows10
[Visual Studio professional 2015/2017](https://visualstudio.microsoft.com/ja/vs/older-downloads/) ：動作確認済み
Win32コンソールアプリケーション

前提としてVisual Studioでの開発ができる．



######初回の作業として
1．Tobii Eye Tracker 4Cのセットアップ
2．開発用のストリームエンジンライブラリをダウンロード

######プロジェクトの作成として
3．インクルードを設定

######実際に使ってみる
4．データを抜き出してみる

という流れ．

#1. Tobii Eye Tracker 4Cのセットアップ

[ダウンロードサイト](https://gaming.tobii.com/getstarted/)からtobii EYETRACKINGを選択し，デバイスはTobii Eye Tracker 4Cを選んでDownloadを押す．

![01-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/c7bd79b1-e694-b7a2-21f5-c66722e2cbd3.jpeg)
![01-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/e04015cf-8d90-21ad-8f98-e0ec8ce4d015.jpeg)

細かい設定は省略，基本的にはTobii Eye Tracker 4Cを挿して指示通りに設定する．

#2. 開発用のストリームエンジンライブラリをダウンロード

[tobii/developer zone](https://developer.tobii.com/)内の

Consumer Eye Trackers
↓
Stream Engine
↓
[Getting Started](https://developer.tobii.com/consumer-eye-trackers/stream-engine/getting-started/)にアクセス．

ページ下部のDownload for Windowsから
Stream Engine x.x.x for Windows x86
をダウンロードする．

1.1系，2.1系，4.1系で動作確認済み．
![01-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/4360f191-67e6-d31d-23f8-4c0a34b1052d.jpeg)

#3．インクルードを設定

Stream Engine x.x.x for Windows x86をVisual Studioのプロジェクト内に解凍する

プロジェクト名\プロジェクト名\consumer_stream_engine_windows_x.x.x.xとする．

ここでは
Tobii4c\Tobii4c\stream_engine_windows_x86_4.1.0.3
としている．

![02-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/adf59b3f-9fb3-0995-b5a3-9732104bbda1.jpeg)

Visual Studioのプロジェクトプロバディから，
C/C++の全般タブから
追加のインクルードディレクトリに
$(SolutionDir)\tobii4c\stream_engine_windows_x86_4.1.0.3\include\tobii;%(AdditionalIncludeDirectories)
を追加．
バージョンによって若干違うが，tobii.hがフォルダ内にあればok．

![03-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/fc5b57de-0f7a-47dd-3e56-989c9ebc0a85.jpeg)

リンカーの全般タブから
追加のライブラリディレクトリに
$(SolutionDir)\tobii4c\stream_engine_windows_x86_4.1.0.3\lib\tobii;%(AdditionalLibraryDirectories)
を追加．
バージョンによって若干違うが，tobii_stream_engine.libとtobii_stream_engine.dllがフォルダ内にあればok．

![03-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/57c2e78a-5198-bd1e-d033-57b1c8bfe350.jpeg)

リンカーの入力タブから
tobii_stream_engine.lib
を追加する．

![03-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/ceba53c2-61c0-d3b2-aa11-0462aa305b66.jpeg)

Tobii4c\Tobii4c\stream_engine_windows_x86_4.1.0.3\lib\tobii
の中にあるtobii_stream_engine.libをソリューション内のフォルダに置く．

![03-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/862b351d-56d8-2279-9280-977d7adbd8a3.jpeg)

これで準備は完了．


#4．データを抜き出してみる

計測前には1.でインストールしたTobiiのアプリからキャリブレーションを行ってから計測する．（個人差が大きいので面倒だがしっかりと設定する．）

プログラムの流れとしては，

デバイスの呼び出し
↓
取りたいデータを有効化
↓
デバイスを呼び出し（有効化したデータが取得される）
↓
デバイスを初期化して終了

計測範囲外のときは-1をデータとして入力しています．

[Git  Hub](https://github.com/RyoyaHase-1111/tobii_streamengine_sample)にもサンプルコードをアップしています．

https://github.com/RyoyaHase-1111/tobii_streamengine_sample


```main.c
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <tobii.h>
#include <tobii_streams.h>


/*VSでfopenを使うためのおまじない*/
#pragma warning(disable: 4996)

//ファイル作成用ポインタ
static FILE *fp_tobii_eye, *fp_tobii_eyep, *fp_tobii_headp;

//各データ格納用変数
int eyedata;
int eyepositiondata_left, eyepositiondata_right;
double wink_left, wink_right;
int headpositiondata;


//頭のポジション
void head_pose_callback(tobii_head_pose_t const* head_pose, void* user_data)
{
	if (head_pose->position_validity == TOBII_VALIDITY_VALID) {
		fprintf(fp_tobii_headp, "Position:,%f,%f,%f,",
			head_pose->position_xyz[0],
			head_pose->position_xyz[1],
			head_pose->position_xyz[2]);

		if (headpositiondata == 0) {
			headpositiondata = 1;
		}
	}
	else {
		fprintf(fp_tobii_headp, "Position:,-1,-1,-1,");

		if (headpositiondata == 1) {
			headpositiondata = 0;
		}
	}

	if (head_pose->position_validity == TOBII_VALIDITY_VALID) {
		fprintf(fp_tobii_headp, "Rotation:,%f,%f,%f\n",
			head_pose->rotation_xyz[0],
			head_pose->rotation_xyz[1],
			head_pose->rotation_xyz[2]);
	}
	else {
		fprintf(fp_tobii_headp, "Rotation:,-1,-1,-1\n");
	}

}


//目の位置
void eye_position_callback(tobii_eye_position_normalized_t const* eye_pos, void* user_data)
{
	if (eye_pos->left_validity == TOBII_VALIDITY_VALID) {
		fprintf(fp_tobii_eyep, "Left:,%f,%f,%f,",
			eye_pos->left_xyz[0],
			eye_pos->left_xyz[1],
			eye_pos->left_xyz[2]);
	}
	else {
		fprintf(fp_tobii_eyep, "Left:,-1,-1,-1,");
		if (eyepositiondata_left == 1) {
			eyepositiondata_left = 0;
		}
	}

	if (eye_pos->right_validity == TOBII_VALIDITY_VALID) {

		fprintf(fp_tobii_eyep, "Right:,%f,%f,%f\n",
			eye_pos->right_xyz[0],
			eye_pos->right_xyz[1],
			eye_pos->right_xyz[2]);

	}
	else {
		fprintf(fp_tobii_eyep, "Right:,-1,-1,-1\n");
		if (eyepositiondata_right == 1) {
			eyepositiondata_right = 0;
		}
	}


}


//視線計測
static void gaze_point_callback(tobii_gaze_point_t const* gaze_point, void* user_data)
{
	double x, y;
	if (gaze_point->validity == TOBII_VALIDITY_VALID) {
		fprintf(fp_tobii_eye, "%f,%f\n",
			gaze_point->position_xy[0],
			gaze_point->position_xy[1]);

		x = double(gaze_point->position_xy[0]);
		y = double(gaze_point->position_xy[1]);


	}
	else {
		fprintf(fp_tobii_eye, "-1,-1\n");
	}

}


static void url_receiver(char const* url, void* user_data)
{
	char* buffer = (char*)user_data;
	if (*buffer != '\0') return; // only keep first value

	if (strlen(url) < 256)
		strcpy(buffer, url);
}


int tobii()
{
	/*ファイル用*/
	char filename[3][50];

	//ファイルを作成---------------------------------------------------
	sprintf(filename[0], "eye.csv");
	sprintf(filename[1], "eyep.csv");
	sprintf(filename[2], "headp.csv");
	fp_tobii_eye = fopen(filename[0], "a");
	fp_tobii_eyep = fopen(filename[1], "a");
	fp_tobii_headp = fopen(filename[2], "a");
	//-----------------------------------------------------------------

	eyedata = 0;
	eyepositiondata_left = 0;
	eyepositiondata_right = 0;
	wink_left = 0;
	wink_right = 0;
	headpositiondata = 0;

	tobii_api_t* api;
	tobii_error_t error = tobii_api_create(&api, NULL, NULL);
	assert(error == TOBII_ERROR_NO_ERROR);

	char url[256] = { 0 };
	error = tobii_enumerate_local_device_urls(api, url_receiver, url);
	assert(error == TOBII_ERROR_NO_ERROR && *url != '\0');

	tobii_device_t* device;
	error = tobii_device_create(api, url, TOBII_FIELD_OF_USE_INTERACTIVE, &device);
	assert(error == TOBII_ERROR_NO_ERROR);

	//頭の位置計測を有効化
	error = tobii_head_pose_subscribe(device, head_pose_callback, 0);
	assert(error == TOBII_ERROR_NO_ERROR);

	//目の位置計測を有効化
	error = tobii_eye_position_normalized_subscribe(device, eye_position_callback, 0);
	assert(error == TOBII_ERROR_NO_ERROR);

	//目線の計測を有効化
	error = tobii_gaze_point_subscribe(device, gaze_point_callback, 0);
	assert(error == TOBII_ERROR_NO_ERROR);


	//とりあえず100データぶん
	for (int i = 0; i <= 100; i++) {

		//計測を呼びだし（有効化したもの全部，結構重くなる）
		error = tobii_wait_for_callbacks(1, &device);
		assert(error == TOBII_ERROR_NO_ERROR || error == TOBII_ERROR_TIMED_OUT);

		error = tobii_device_process_callbacks(device);
		assert(error == TOBII_ERROR_NO_ERROR);


	}
	
	//デバイスの初期化
	error = tobii_gaze_point_unsubscribe(device);
	assert(error == TOBII_ERROR_NO_ERROR);

	error = tobii_device_destroy(device);
	assert(error == TOBII_ERROR_NO_ERROR);

	error = tobii_api_destroy(api);
	assert(error == TOBII_ERROR_NO_ERROR);

	//エクセルデータをfclose処理（fcloseをしないとうまく書き出されないので注意）
	fclose(fp_tobii_eye);
	fclose(fp_tobii_eyep);
	fclose(fp_tobii_headp);

	return 1;

}

```

#おまけ（とれるデータの話）

視線データはモニタ内に対して(0.0)から(1.1)の範囲でデータの書き出しを行っています．
結構高精度なのでゲームだけじゃなく様々なアプリケーション開発ができそうなのでぜひ試しに触ってみてください．
Web会議とかでうまく共有とかできないかな．．．

![視線計測概要(日本語).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758746/89931445-36e7-0a97-f33f-a8b6d08e7f13.jpeg)


有料版じゃないと取れないデータなどもあるので最新の[APIリファレンス](https://developer.tobii.com/consumer-eye-trackers/stream-engine/getting-started/)を見て検討してみてください．