public SingleEyeGazeData SelectSingleEye(GazeData gaze) { var average = (gaze.LeftEye.Validity == EyeValidity.Valid && gaze.RightEye.Validity == EyeValidity.Valid) ? new EyeData(EyeValidity.Valid, EyeSampleUtils.Average(gaze.LeftEye, gaze.RightEye)) : EyeData.Default; return(new SingleEyeGazeData(average, gaze.Timestamp)); }
// .-|.-|.-| public static IObservable <EyeMovement> MergeAdjacentFixations(this IObservable <EyeMovement> movements, TimeSpan maxTimeBetweenFixations, double maxAngleBetweenFixations) { return(movements.Buffer(m => m.MovementType == EyeMovementType.Fixation) .Scan((aggregate, buffer) => { if (aggregate.Any()) { var lastFixation = aggregate.FirstOrDefault(m => m.MovementType == EyeMovementType.Fixation); var nextFixation = buffer.FirstOrDefault(m => m.MovementType == EyeMovementType.Fixation); var nextMovements = buffer.Except(new[] { nextFixation }).ToArray(); if (lastFixation != null && nextFixation != null) { var timeBetweenFixations = (nextFixation.Timestamp - lastFixation.EndTimestamp); if (timeBetweenFixations <= maxTimeBetweenFixations) { var lastSample = lastFixation.Samples.Last().Eye; var nextSample = nextFixation.Samples.First().Eye; var averageSample = EyeSampleUtils.Average(lastSample, nextSample); double angle = averageSample.GetVisualAngle(lastFixation.AverageSample, nextFixation.AverageSample); if (angle <= maxAngleBetweenFixations) { var mergedSamples = aggregate.SelectMany(m => m.Samples).Concat(nextFixation.Samples); var newAverageSample = EyeSampleUtils.Average(lastFixation.Samples.Concat(nextFixation.Samples).Select(s => s.Eye)); // merge var mergedFixation = new List <EyeMovement>() { new EyeMovement ( EyeMovementType.Fixation, mergedSamples, newAverageSample, lastFixation.Timestamp, nextFixation.EndTimestamp ) }; if (nextMovements != null && nextMovements.Any()) { mergedFixation.AddRange(nextMovements); } return mergedFixation; } } } } return buffer; }) .Where(b => b.Any()) .Buffer((first, current) => first.First().Timestamp != current.First().Timestamp) .Where(b => b.Any()) .Select(b => b.Last()) .SelectMany(b => b)); }
public static IObservable <EyeMovement> ClassifyMovements(this IObservable <EyeVelocity> velocities, double velocityThreshold) { return(velocities.Buffer((first, current) => ClassifyMovement(first, velocityThreshold) != ClassifyMovement(current, velocityThreshold)) .Where(b => b.Any()) .Buffer(2, 1) .Scan <IList <IList <EyeVelocity> >, EyeMovement>(null, (movement, buffers) => { var current = buffers.First(); var last = buffers.LastOrDefault(); var currentFirstSample = current.First(); DateTimeOffset startTimestamp = currentFirstSample.Eye.Timestamp; if (movement != null) { var startTicksDiff = movement.EndTimestamp - movement.Samples.Last().Eye.Timestamp; startTimestamp = startTimestamp.Subtract(startTicksDiff.Duration()); } var currentLastSample = current.Last(); DateTimeOffset endTimestamp = currentLastSample.Eye.Timestamp; if (last != null && last != current) { var endTicksDiff = (last.First().Eye.Timestamp - endTimestamp).Duration().Ticks / 2; endTimestamp = endTimestamp.Add(TimeSpan.FromTicks(endTicksDiff)); } EyeMovementType movementType = ClassifyMovement(current.First(), velocityThreshold); EyeSample averageSample = null; if (movementType == EyeMovementType.Fixation) { averageSample = EyeSampleUtils.Average(current.Select(s => s.Eye)); } return new EyeMovement(movementType, current, averageSample, startTimestamp, endTimestamp); })); }