/// <summary> /// I-VT algorithm from "Identifying fixations and saccades in eye-tracking protocols", Salvucci, 2000 /// </summary> /// <param name="maxSpeedFixation">If the speed is inferior to maxSpeedFixation, it is considered as a fixation</param> public void fixationVelocity(int maxSpeedFixation = 40) { //With Tobii EyeX, the distance and velocity are similar because the data is recorded at a regular time - every x ms) //Compute the velocity for (int i = 1; i < gazes.Count; i++) { gazes[i].squaredVelocity = (gazes[i - 1].gazeX - gazes[i].gazeX) * (gazes[i - 1].gazeX - gazes[i].gazeX) + (gazes[i - 1].gazeY - gazes[i].gazeY) * (gazes[i - 1].gazeY - gazes[i].gazeY); } List <Gaze> fixations = new List <Gaze>(); for (int i = 0; i < gazes.Count; i++) { List <Gaze> microFixations = new List <Gaze>(); int j = 0; while (i + j < gazes.Count && gazes[i + j].squaredVelocity < maxSpeedFixation * maxSpeedFixation) { microFixations.Add(gazes[i + j]); j++; } if (microFixations.Count > 1) { //Compute the centroid Gaze centre = centroid(microFixations); fixations.Add(centre); i += j - 1; } if (microFixations.Count == 1) { fixations.Add(microFixations.First()); } } gazes = fixations; }
private IEnumerable <Gaze> readTobiiCsvNew(List <string> linesTxt, char split = ';') { List <Gaze> gazes = new List <Gaze>(); if (linesTxt.Count < 2) { return(gazes); } //Remove the header linesTxt.RemoveAt(0); //Convert decimal marker in numbers if the format it is a dot or a comma 00.000 (USA / Japan) or 00,000 (Europe) double timeZero = double.Parse(linesTxt[0].Split(split)[1].Replace(',', '.'), new CultureInfo("en-US")); foreach (string line in linesTxt) { string[] cols = line.Split(split); Gaze g = new Gaze(); g.windowTime = DateTime.ParseExact(cols[0], "MM/dd/yyyy HH:mm:ss.FFF", null); g.timestamp = (float)(double.Parse(cols[1].Replace(',', '.'), new CultureInfo("en-US")) - timeZero); g.gazeX = float.Parse(cols[2].Replace(',', '.'), new CultureInfo("en-US")); g.gazeY = float.Parse(cols[3].Replace(',', '.'), new CultureInfo("en-US")); g.pupilDiameter = float.Parse(cols[4].Replace(',', '.'), new CultureInfo("en-US")); gazes.Add(g); } return(gazes); }
private void readSMiRaw(List <string> linesTxt) { //Remove the non-data lines string curentLine = linesTxt[0]; int indexGazeX = 0; int indexGazeY = 0; while (curentLine.StartsWith("##") || curentLine.StartsWith("Time")) { //Save the saze of the screen if (curentLine.StartsWith("## Calibration Area: ")) { width = int.Parse(curentLine.Split('\t')[1].Replace(',', '.'), new CultureInfo("en-US")); height = int.Parse(curentLine.Split('\t')[2].Replace(',', '.'), new CultureInfo("en-US")); } //Save the index of the collomn that contain the position of the gaze, because it change depending on the files! else if (curentLine.StartsWith("Time")) { string[] cols = curentLine.Split('\t'); for (int i = 0; i < cols.Length; i++) { if (cols[i] == "R POR X [px]") { indexGazeX = i; } else if (cols[i] == "R POR Y [px]") { indexGazeY = i; } } } linesTxt.RemoveAt(0); curentLine = linesTxt[0]; } double timeZero = double.Parse(linesTxt[0].Split('\t')[0].Replace(',', '.'), new CultureInfo("en-US")); foreach (var line in linesTxt) { string[] cols = line.Split('\t'); if (indexGazeX < cols.Length && indexGazeY < cols.Length) { Gaze g = new Gaze(); g.timestamp = (float)(double.Parse(cols[0].Replace(',', '.'), new CultureInfo("en-US")) - timeZero); g.gazeX = float.Parse(cols[indexGazeX].Replace(',', '.'), new CultureInfo("en-US")); g.gazeY = float.Parse(cols[indexGazeY].Replace(',', '.'), new CultureInfo("en-US")); gazes.Add(g); } } }
public static Gaze centroid(List <Gaze> gazes) { Gaze g = gazes.First(); g.duration = gazes.Last().timestamp - g.timestamp; for (int i = 1; i < gazes.Count; i++) { g.gazeX += gazes[i].gazeX; g.gazeY += gazes[i].gazeY; } g.gazeX /= gazes.Count; g.gazeY /= gazes.Count; return(g); }
public static List <Gaze> readTobiiCsv(List <string> linesTxt, char split = ';') { List <Gaze> gazes = new List <Gaze>(); if (linesTxt.Count < 2) { return(gazes); } //Remove the header linesTxt.RemoveAt(0); //Convert decimal marker in numbers if the format it is a dot or a comma 00.000 (USA / Japan) or 00,000 (Europe) double timeZero = double.Parse(linesTxt[0].Split(split)[0].Replace(',', '.'), new CultureInfo("en-US")); foreach (string line in linesTxt) { string[] cols = line.Split(split); Gaze g = new Gaze(); g.timestamp = (float)(double.Parse(cols[0].Replace(',', '.'), new CultureInfo("en-US")) - timeZero); g.gazeX = float.Parse(cols[1].Replace(',', '.'), new CultureInfo("en-US")); g.gazeY = float.Parse(cols[2].Replace(',', '.'), new CultureInfo("en-US")); gazes.Add(g); } return(gazes); }
private void initialize(string nameWithoutExtention) { paramFilename = nameWithoutExtention + "_param.txt"; filename = nameWithoutExtention + ".csv"; aviOriginal = nameWithoutExtention + ".avi"; pngOriginal = nameWithoutExtention + "_gaze.png"; pngFixations = nameWithoutExtention + "_fixations.png"; aviFixations = nameWithoutExtention + "_fixations.avi"; pngLineBreak = nameWithoutExtention + "_line.png"; aviLineBreak = nameWithoutExtention + "_line.avi"; pngMedian = nameWithoutExtention + "_median.png"; backgroundPath = nameWithoutExtention + "_back.png"; csvFixations = nameWithoutExtention + "_fix.csv"; //Read the data if (!File.Exists(filename)) { //Second possibility because of SMi files filename = filename.Replace(".csv", ".txt"); if (!File.Exists(filename)) { throw new Exception("Error, the file " + filename + " cannot be found. Press a key to exit the program..."); } } List <string> linesTxt = File.ReadAllLines(filename).ToList(); //Inspect the first line, if "Timestamp;GazeX;GazeY;LeftEye;RightEye" it is a Tobii raw data if (linesTxt[0] == "Timestamp;GazeX;GazeY;LeftEye;RightEye" || linesTxt[0] == "TimeStamp;GazeX;GazeY" || linesTxt[0] == "TimeStamp;GazeX;GazeY;TimeStampWindows") { readTobii(linesTxt); } else if (linesTxt[0] == "WindowsTime;Timestamp;GazeX;GazeY;PupilDiameter") { readTobiiNew(linesTxt); } //Else if it is "## [BeGaze]" it is SMi else if (linesTxt[0] == "## [BeGaze]") { readSMiRaw(linesTxt); } //Else if the last line is "finish", it is a Fixation file made by the student from SMi else if (linesTxt[linesTxt.Count - 1] == "finish") { int i = 0; double timeZero = double.Parse(linesTxt[0].Split('\t')[0].Replace(',', '.'), new CultureInfo("en-US")); while (linesTxt[i] != "blink") { string[] cols = linesTxt[i].Split('\t'); Gaze g = new Gaze(); g.timestamp = (float)(double.Parse(cols[0].Replace(',', '.'), new CultureInfo("en-US")) - timeZero); g.gazeX = float.Parse(cols[2].Replace(',', '.'), new CultureInfo("en-US")); g.gazeY = float.Parse(cols[3].Replace(',', '.'), new CultureInfo("en-US")); gazes.Add(g); i++; } } //a Tobii raw data made by Shoya else if (linesTxt[0] == "# timestamp,x,y") { gazes = readTobiiCsv(linesTxt, ','); } //Else exeption else { throw new Exception("Unknown type of input"); } }