package edu.cmu.cs.eyetrack.analysis.score;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.cmu.cs.eyetrack.analysis.score.compare.PointComparator;
import edu.cmu.cs.eyetrack.analysis.score.distance.DistanceFunction;
import edu.cmu.cs.eyetrack.analysis.struct.Trajectory;
import edu.cmu.cs.eyetrack.analysis.struct.tobii.TobiiData;
import edu.cmu.cs.eyetrack.analysis.struct.tobii.TobiiFrame;
import edu.cmu.cs.eyetrack.analysis.struct.trackit.Experiment;
import edu.cmu.cs.eyetrack.analysis.struct.trackit.TrackItFrame;
import edu.cmu.cs.eyetrack.analysis.struct.trackit.Trial;
import edu.cmu.cs.eyetrack.test.SingleRunScore;
public abstract class Scorer {
protected PointComparator pFunc;
protected DistanceFunction dist;
public Scorer(PointComparator pFunc, DistanceFunction dist) {
this.pFunc = pFunc;
this.dist = dist;
}
public abstract SingleRunScore calcScore(Trajectory<TrackItFrame> actual, Trajectory<TobiiFrame> subject, List<Double> recordList);
public abstract boolean calcFixationStats(SingleRunScore record, Trial actual, Trajectory<TobiiFrame> subject);
public Map<Integer, SingleRunScore> scoreSubject(Experiment goldStandard, TobiiData subjectData, Map<Integer, List<Double>> recordMap, double lookThreshold) {
Map<Integer, SingleRunScore> scoreMap = new HashMap<Integer, SingleRunScore>();
for(Integer trialID : goldStandard.getTrials().keySet()) {
Trial trial = goldStandard.getTrials().get(trialID);
// Old version: exactly as many Tobii trials as there are Track-It trials
//Trajectory<TobiiFrame> subjectTrajectory = subjectData.getTrajectories().get(trialID);
// Newer version: must infer Tobii trials by Spacebar press + closest timestamp match
Trajectory<TobiiFrame> subjectTrajectory = subjectData.getClosestTrajectoryTo(goldStandard.getTrials().get(trialID).getStartTime());
if(null == subjectTrajectory) {
// Some error occurred while parsing the file (we've had some students with blank Tobii files); skip this
continue;
}
int targetIdx = trial.getTargetIdx();
Trajectory<TrackItFrame> goldTrajectory = trial.getTrajectories().get(targetIdx);
if(recordMap != null) {
recordMap.put(trialID, new ArrayList<Double>());
}
SingleRunScore record = calcScore(goldTrajectory,
subjectTrajectory,
recordMap == null ? null : recordMap.get(trialID));
// If the subject's eyes weren't tracked by Tobii enough, then nil the score
if( ((double)record.getFrameCountOnTarget() / (double)record.getFrameCountOverall() ) < lookThreshold) {
record.setScore(Double.NaN);
}
// Some scoring function implement secondary features, like thinking about non-target objectsp; put that stuff here
calcFixationStats(record, trial, subjectTrajectory);
scoreMap.put(trialID, record);
}
return scoreMap;
}
// For use with old version of Track-It + Tobii that ran on movies, stopped in December 2011
public Map<Integer, SingleRunScore> scoreSubjectDec2011(Experiment goldStandard, TobiiData subjectData, Map<Integer, List<Double>> recordMap) {
Map<Integer, SingleRunScore> scoreMap = new HashMap<Integer, SingleRunScore>();
for(Integer trialID : subjectData.getTrajectories().keySet()) {
// Old version: exactly as many Tobii trials as there are Track-It trials
Trajectory<TobiiFrame> subjectTrajectory = subjectData.getTrajectories().get(trialID);
if( goldStandard.getTrials().containsKey(trialID)) {
int targetIdx = goldStandard.getTrials().get(trialID).getTargetIdx();
Trajectory<TrackItFrame> goldTrajectory = goldStandard.getTrials().get(trialID).getTrajectories().get(targetIdx);
if(recordMap != null) {
recordMap.put(trialID, new ArrayList<Double>());
}
SingleRunScore record = calcScore(goldTrajectory,
subjectTrajectory,
recordMap == null ? null : recordMap.get(trialID));
scoreMap.put(trialID, record);
} else {
System.err.println("No matching trial found in gold standard for Trial ID: " + trialID);
}
}
return scoreMap;
}
public abstract String getName();
}