天天看點

C#開發EyeLink眼動儀的實驗程式

Eyelink眼動儀是SR Research推出的一款眼動儀,很多高校都在使用其做實驗。其官方提供了COM的接口,是以支援COM接口的開發平台都可以開發使用。官方甚至提供了一個C#的樣例供參考,不過這個樣例相比起其他的VC++的樣例而言功能過于簡單,程式本身也比較亂,再加上國内關于EyeLink的資料又比較少,是以這裡我簡要寫下我使用EyeLink眼動儀的開發架構,友善大家開發。

【題外話】

本文位址:http://www.cnblogs.com/mayswind/p/3417211.html

【文章索引】

  1. 準備工作
  2. EyeLink實驗的操作流程
  3. EyeLink的基本使用方法
  4. EyeLink的進階使用方法

【一、準備工作】

雖然SR Research提供的大多數樣例都是基于VC++的,但由于C#開發快速高效,同時在實作複雜功能時C#更容易開發,是以我還是選擇了使用C#開發實驗程式,而不是VC++。SR Research比較有意思的是,官方雖然提供了所有的說明文檔以及開發工具包,但是這些都需要從官網的論壇才可以下載下傳,而且官網論壇的注冊是需要人工稽核的,是以注冊一定千萬不要着急。好在稽核也不會慢,去除時差的原因沒有多久就能通過稽核。

注冊完成以後,首先需要下載下傳開發工具包,該工具包裡包括了SDK檔案以及幾個說明文檔還有幾個示例程式。位址見下:

https://www.sr-support.com/showthread.php?6-Windows-Display-Software

程式員開發手冊(EyeLink Programmers Guide.pdf)就在安裝後的目錄中的Docs中,雖然是C語言版本的而且也不是使用的COM接口,但對于了解SDK還是有幫助的。當然也可以單獨下載下傳,位址見下:

https://www.sr-support.com/showthread.php?4-EyeLink-C-Programmers-Guides

所有的自帶樣例程式都在安裝目錄下的SampleExperiments目錄,COM接口的都在com目錄下,其中還有一個C#的樣例程式。而EyeLink提供的COM接口檔案位于Libs目錄下,檔案名為“SREyeLink.dll”,直接在C#裡引用即可。

【二、EyeLink實驗的操作流程】

一般使用EyeLink進行實驗的都要遵循以下的流程:

1、程式開始界面,可能介紹本實驗的情況及提示接下來按什麼鍵進入實驗等等。

2、一般正式進入實驗流程後首先按C鍵進入Calibration界面進行眼睛的校準,一般為9點校準(可以自行設定),如果一個點校準失敗會在所有點校準後重新校準。

3、眼睛校準成功後按V鍵進入Validation界面進行剛才校準結果的驗證。

4、驗證成功後開始實驗,一個實驗可能有多個Trail組成,在所有Trial之前或每個Trial之前可能需要Drift使受試者注視螢幕中心(位置可自定義)以繼續。

5、如果有多個Trial将依次執行。

【三、EyeLink的基本使用方法】

EyeLink提供的COM接口總體還是很友好的,常見的有以下幾個類:

1、EyeLink:與EyeLink操作主要的類,提供連接配接Host PC、建立EDF檔案、向Host PC發送指令和向EDF中記錄資訊等等,常見的方法如下:

方法名 含義
void open("Host PC IP", 0); 連接配接到指定Host PC,Host PC IP通常為100.1.1.1
void close(); 關閉指定連接配接
void openDataFile("fileName"); 在Host PC建立指定檔案名的EDF檔案以供記錄
void receiveDataFile("fileName", "localPath"); 将Host PC記錄的指定檔案名的EDF傳回到本地路徑
void startRecording(true, true, true, true); 開始記錄注視點資訊(四個選項可以設定是否記錄指定内容)
void stopRecording(); 結束記錄注視點資訊
bool isRecording(); 擷取是否正在記錄注視點資訊
void sendCommand("command"); 向Host PC發送指定的指令
void sendMessage("message"); 向Host PC發送指定的資訊,記錄在EDF檔案中
void doTrackerSetup(); 進入實驗流程,允許使用者按C和V執行相應操作或按Enter顯示鏡頭内容
void doDriftCorrect(x, y, true, true); 要求受試者看住指定的(x, y)點以繼續
void setOfflineMode(); Host PC進入Offline Mode,通常實驗結束後要執行該操作
short eyeAvailable(); 擷取攝像頭跟蹤到的眼睛(可以轉換為EL_EYE枚舉) 
short getLastButtonPress(out pressTime); 擷取按鍵資訊(比如可以擷取連接配接在Host PC上的搖桿的按鍵)
Sample getNewestSample(); 擷取注視點資訊 

2、EyeLinkUtil:提供了幾個常用工具方法,如提供目前時間、進入實時模式等等。

含義 
void beginRealTimeMode(time); 開始指定時間(毫秒)的實時模式 
void endRealTimeMode(); 結束實時模式 
double currentTime(); 擷取目前時間(毫秒) 
ELGDICal getGDICal(); 擷取EyeLink GDICal 

3、ELGDICal:通過将視窗的Handle傳給該類,實作由EyeLink SDK控制直接在界面上繪制Calibration、Validation以及Drift等界面,同時開發者也可以直接在界面線程上執行邏輯或繪制相應内容,而無需擔心界面無響應而建立子線程等等。

void setCalibrationColors(foreColor, backColor); 設定Calibration、Validation、Drift等界面的前景色和背景色
void setCalibrationWindow(hWnd); 設定上述界面的視窗Handle
void enableKeyCollection(true/false); 設定是否允許通過鍵盤控制EyeLink SDK操作(如C、V、Enter)

EyeLink基本使用非常簡單,就如同給出的C#的樣例一樣,在這裡給出一個簡單的使用,比如直接在界面線程上執行代碼(this這裡就是指目前窗體):

1 EyeLink eyeLink = new EyeLink();//建立EyeLink對象
 2 eyeLink.open("100.1.1.1", 0);//連接配接到Host PC
 3 eyeLink.openDataFile("test.edf");//在Host PC建立指定檔案名的EDF以供記錄
 4 eyeLink.sendCommand("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON");//設定事件或記錄的内容,下同
 5 eyeLink.sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS");
 6 eyeLink.sendCommand("link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON");
 7 eyeLink.sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS");
 8 eyeLink.sendMessage(String.Format("DISPLAY_COORDS {0} {1} {2} {3}", this.Left, this.Top, this.Width, this.Height));//設定視窗的範圍
 9 
10 EyeLinkUtil eyeLinkUtil = new EyeLinkUtil();//建立EyeLinkUtil對象
11 ELGDICal gdiCal = eyeLinkUtil.getGDICal();//建立EyeLinkGDICal對象
12 
13 gdiCal.setCalibrationWindow(this.Handle.ToInt32());//将窗體句柄傳給EyeLink SDK
14 gdiCal.enableKeyCollection(true);
15 eyeLink.doTrackerSetup();//開始進行設定階段(Calibration、Vadation等)
16 gdiCal.enableKeyCollection(false);
17 
18 gdiCal.enableKeyCollection(true);
19 eyeLink.doDriftCorrect((Int16)(this.Width / 2), (Int16)(this.Height / 2), true, true);//所有Trial前或每個Trial前讓受試者注視指定位置以開始Trial
20 gdiCal.enableKeyCollection(false);
21 
22 eyeLink.startRecording(true, true, true, true);//開始記錄EDF檔案
23 Double st = eyeLinkUtil.currentTime();
24 
25 while ((st + 20000) > eyeLinkUtil.currentTime())//循環20秒程式結束
26 {
27     EL_EYE eyeUsed = (EL_EYE)eyeLink.eyeAvailable();//擷取目前眼睛使用情況
28     Sample sample = eyeLink.getNewestSample();//擷取目前注視點
29     
30     if (sample == null || eyeUsed == EL_EYE.EL_EYE_NONE)
31     {
32         continue;
33     }
34     
35     if (eyeUsed == EL_EYE.EL_BINOCULAR)
36     {
37         eyeUsed = EL_EYE.EL_LEFT;//如果兩隻眼睛同時被捕捉到則使用左眼
38     }
39     
40     Single x = sample.get_gx(eyeUsed);//擷取注視點相對視窗的橫坐标
41     Single y = sample.get_gy(eyeUsed);//擷取注視點相對視窗的縱坐标
42     Single pa = sample.get_pa(eyeUsed);//擷取瞳孔大小
43     
44     if (x == (Single)EL_CONSTANT.EL_MISSING_DATA || y == (Single)EL_CONSTANT.EL_MISSING_DATA || pa <= 0)
45     {
46         continue;
47     }
48     
49     //TODO: 在這裡處理x、y坐标等
50 }
51 
52 eyeLink.sendCommand("close_data_file");//關閉Host PC上的EDF檔案
53 eyeLink.receiveDataFile("test.edf", "本機檔案路徑");//将Host PC上的EDF檔案傳回到測試機
54 
55 eyeLink.setOfflineMode();//将Host PC設定為離線狀态(非實驗進行狀态)
56 eyeLink.stopRecording();//結束記錄EDF檔案
57 eyeLink.close();//關閉與Host PC的連接配接
58 
59 eyeLink = null;
60 eyeLinkUtil = null;      

【四、EyeLink的進階使用方法】

有了上述這些還不夠,比如:

1、雖然上述我們能記錄EDF檔案,但是如果有多個Trial的話我們還希望能在EDF檔案中予以區分。是以在每個Trial前後我們還需要這樣去做:

1 eyeLink.sendCommand(String.Format("record_status_message 'TRIAL {0}'", trialIndex));
2 eyeLink.sendMessage(String.Format("TRIALID {0}", trialIndex));
3 eyeLink.startRecording(true, true, true, true);
4 
5 //Trial邏輯
6 
7 eyeLink.stopRecording();      

2、如果我們在Trial中顯示了圖檔,同時我們希望EDF Viewer在檢視時也能将對應的圖檔顯示出來,那麼我們還需要在startRecording之前執行這樣一句話,去讓EDF Viewer打開指定路徑下的圖檔。

1 eyeLink.sendMessage(String.Format("!V IMGLOAD FILL {0}", imagePath));      

3、如果我們希望受測者能夠使用按鍵進行Trial的操作(比如按某鍵表示完成或跳過該Trial之類的),我們還需要擷取按鍵的資訊,其中如果我們還需要在EDF檔案中顯示出按鍵的情況,我們還需要向Host PC發送一條資訊(下列代碼中第6行)。

1 Double pressTime = 0;
2 Int16 buttonCode = eyeLink.getLastButtonPress(out pressTime);
3 
4 if (buttonCode != 0)
5 {
6     eyeLink.sendMessage(String.Format("ENDBUTTON {0}", buttonCode));
7     //TODO: 執行相關按鍵的邏輯
8 }      

4、如果我們希望在Drift的過程中,受測者在将眼睛注視到指定的點後還需要按指定的鍵才能繼續的話,我們還需要在開始的配置過程中寫如下的代碼(這裡使用的是按鍵5):

1 eyeLink.sendCommand("button_function 5 'accept_target_fixation'");      

附一個我寫的C#上使用EyeLink的架構(MIT LICENSE):https://files.cnblogs.com/mayswind/DotMaysWind.EyeLink.rar

【相關連結】

1、SR Research Support Site:https://www.sr-support.com/forums/

如果您覺得本文對您有所幫助,不妨點選下方的“推薦”按鈕來支援我!

本文及文章中代碼均基于“署名-非商業性使用-相同方式共享 3.0”,文章歡迎轉載,但請您務必注明文章的作者和出處連結,如有疑問請私信我聯系!