package edu.cmu.cs.eyetrack.test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import au.com.bytecode.opencsv.CSVWriter; import edu.cmu.cs.eyetrack.analysis.io.Loader; import edu.cmu.cs.eyetrack.analysis.score.PointByPointScorer; import edu.cmu.cs.eyetrack.analysis.score.Scorer; import edu.cmu.cs.eyetrack.analysis.score.compare.DeltaOffsetPointComparator; import edu.cmu.cs.eyetrack.analysis.score.compare.PointComparator; import edu.cmu.cs.eyetrack.analysis.score.distance.DistanceFunction; import edu.cmu.cs.eyetrack.analysis.score.distance.EuclideanDistanceFunction; import edu.cmu.cs.eyetrack.analysis.struct.tobii.TobiiData; import edu.cmu.cs.eyetrack.analysis.struct.tobii.TobiiEventMap; import edu.cmu.cs.eyetrack.analysis.struct.trackit.Experiment; import edu.cmu.cs.eyetrack.helper.Util; /** * * @author spook * Deals with data from the pilot runs of Track-It, where we pre-recorded runs of Track-It and tested subjects * against them on Tobii using *videos* of these pre-recorded runs. Thus, each run of a subject is compared against * an overall "gold standard" .csv output from the video-recorded Track-It run. Later, we changed this so that Track-It * ran live in Tobii, so each run plays against its own gold standard (see, e.g., AnalysisTester.java) */ public class AnalysisTesterDec2011 { public static void main(String args[]) { // // Sets paths for our dev systems based on Windows/Linux boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); String subjRootDir, goldPath; if(isWindows) { subjRootDir = "C:\\sideprojects\\TrackIt\\experiments\\real_run_data\\"; goldPath = "C:\\sideprojects\\TrackIt\\experiments\\gold_standard\\all_same_test1.csv"; Util.dPrintln("Detected Windows-based operating system"); } else { subjRootDir = "/home/spook/sideprojects/TrackIt/experiments/real_run_data/"; goldPath = "/home/spook/sideprojects/TrackIt/experiments/gold_standard/all_same_test1.csv"; Util.dPrintln("Detected *nix-based operating system"); } // // Load the actual trajectories for each object in this task, taken straight from Track-It Experiment goldExperiment = null; try { Util.dPrintln("Attempting to load gold standard data from " + goldPath); goldExperiment = Loader.loadGoldStandard(new File(goldPath)); } catch(IOException e) { System.err.println("IO Error loading gold standard file " + goldPath); e.printStackTrace(); return; } Util.dPrintln("Loaded " + goldPath); Util.dPrintln("Found " + goldExperiment.getNumTrials() + " trials total."); // Start traversal at root. This root is organized as follows: // root -> {Subject1 -> {tobii1, tobbi2, ..}, Subject2 -> {tobii1, tobii2, ..}, ..} File rootDir = new File(subjRootDir); if(!rootDir.isDirectory()) { Util.dPrintln("Need root directory for files; path not directory: " + subjRootDir); return; } PilotOutputRecord recorder = new PilotOutputRecord(); for(File subjectDir : rootDir.listFiles()) { // Skip any non-directory files sitting around at the subject directory level if(!subjectDir.isDirectory()) { Util.dPrintln(subjectDir.getAbsolutePath() + " is not a directory; skipping."); continue; } String subjectID = subjectDir.getName(); for(String subjEventPath : subjectDir.list()) { // Ignore non-Tobii files if(!subjEventPath.endsWith(".tsv")) { continue; } // Ignore non-Tobii Event-Map files int eventDataIndex = subjEventPath.lastIndexOf("-Event-Data"); if(eventDataIndex < 0) { continue; } // If the file is a Tobii Event Map file, find its matching // All Data file and move on to loading both String allDataPrefix = subjEventPath.substring(0, eventDataIndex) + "-All-Data"; String subjDataPath = null; for(String candSubjDataPath : subjectDir.list()) { if(candSubjDataPath.startsWith(allDataPrefix)) { subjDataPath = candSubjDataPath; break; } } // If we couldn't find a match to this Event Data, tell the user and skip if(subjDataPath == null) { Util.dPrintln("Couldn't find a match to expected Event Data file " + subjEventPath); return; } // Try to figure out what type of trial this is (ALL DIFF A, ALL SAME B, etc) PilotOutputRecord.TRIAL_TYPE trialType = PilotOutputRecord.TRIAL_TYPE.inferTrialType(subjEventPath); if(trialType.equals(PilotOutputRecord.TRIAL_TYPE.UNKNOWN)) { Util.dPrintln("Couldn't discern what type of trial (e.g., ALL DIFF A, ALL SAME B) this was from the Event Data file " + subjEventPath); return; } else { Util.dPrintln("I think I've found trial type " + trialType + " from filename " + subjEventPath); } File subjEventFile = new File(subjectDir, subjEventPath); File subjDataFile = new File(subjectDir, subjDataPath); // // Grab the overview event data for a single user TobiiEventMap eventMap = null; try { Util.dPrintln("Attempting to load trial data from subject path " + subjEventPath); eventMap = Loader.loadTobiiEvent2(subjEventFile); } catch(IOException e) { System.err.println("IO Error loading event data file " + subjEventPath); e.printStackTrace(); return; } Util.dPrintln("Loaded event data from file " + subjEventPath); // // Grab the overview event data for a single user TobiiData subjectData = null; try { Util.dPrintln("Attempting to load Tobii data chunk from " + subjDataPath); subjectData = Loader.loadTobiiData2(subjDataFile, eventMap); } catch(IOException e) { System.err.println("Error loading Tobii data chunk from " + subjDataPath); e.printStackTrace(); return; } Util.dPrintln("Loaded Tobii data chunk from " + subjDataPath); // // Score the subject's curves against the gold standard Util.dPrintln("Comparing subject's trajectory against gold standard."); long offset = 500; // in ms PointComparator f = new DeltaOffsetPointComparator(offset); double exponent = 2.0; DistanceFunction dist = new EuclideanDistanceFunction(); //DistanceFunction dist = new FalloffDistanceFunction(exponent); Scorer scorer = new PointByPointScorer(f, dist); // Record ALL score data in separate files, if desired boolean recordEverything = false; Map<Integer, List<Double>> recordMap = null; if(recordEverything) { recordMap = new HashMap<Integer, List<Double>>(); } double lookThreshold = 0.0; Map<Integer, SingleRunScore> scores = scorer.scoreSubject(goldExperiment, subjectData, recordMap, lookThreshold); // Possibly record every single score in a separate file if(recordEverything) { try { for(Integer trialID : scores.keySet()) { CSVWriter writer = new CSVWriter(new FileWriter(new File("exact_" + trialType + "_" + subjectID + "_" + trialID + "_" + System.currentTimeMillis() + ".csv"))); for(Double score : recordMap.get(trialID)) { if(score == null) { // For now, record invalid looks as negative score //score = -1.0; continue; } writer.writeNext(new String[] {String.valueOf(score)}); } writer.close(); } } catch(IOException e) { e.printStackTrace(); } } // Record the subject's score in our map of scores, for charting later recorder.addHeader("offset", offset + " ms"); recorder.addHeader("exponent", String.valueOf(exponent)); recorder.addHeader("comparator function", f.getClass().getName()); recorder.addHeader("distance function", dist.getClass().getName()); recorder.addHeader("scorer", scorer.getClass().getName()); recorder.addScore(trialType, subjectID, scores); } // end of individual subject's listing loop } // end of root directory listing loop // Write all our records to a .csv file try { recorder.writeToCSV("eyetrack_" + System.currentTimeMillis() + ".csv"); } catch(IOException e) { e.printStackTrace(); } return; } }