Skip to content

Commit

Permalink
Merge pull request #83 from BoysTownorg/eye-points
Browse files Browse the repository at this point in the history
Eye points
  • Loading branch information
sbashford authored Aug 19, 2021
2 parents 68024eb + a6ee0f1 commit 6425a2a
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 98 deletions.
24 changes: 18 additions & 6 deletions lib/core/include/av-speech-in-noise/core/OutputFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ enum class HeadingItem {
target,
correctKeywords,
freeResponse,
leftGaze,
rightGaze,
leftGazePositionRelativeScreen,
rightGazePositionRelativeScreen,
leftGazePositionRelativeTracker,
rightGazePositionRelativeTracker,
leftGazeOriginRelativeTracker,
rightGazeOriginRelativeTracker,
eyeTrackerTime,
targetPlayerTime,
correctConsonant,
Expand Down Expand Up @@ -57,10 +61,18 @@ constexpr auto name(HeadingItem i) -> const char * {
return "response";
case HeadingItem::correctKeywords:
return "# correct keywords";
case HeadingItem::leftGaze:
return "left gaze [x y]";
case HeadingItem::rightGaze:
return "right gaze [x y]";
case HeadingItem::leftGazePositionRelativeScreen:
return "left gaze position relative screen [x y]";
case HeadingItem::rightGazePositionRelativeScreen:
return "right gaze position relative screen [x y]";
case HeadingItem::leftGazePositionRelativeTracker:
return "left gaze position relative tracker [x y z]";
case HeadingItem::rightGazePositionRelativeTracker:
return "right gaze position relative tracker [x y z]";
case HeadingItem::leftGazeOriginRelativeTracker:
return "left gaze origin relative tracker [x y z]";
case HeadingItem::rightGazeOriginRelativeTracker:
return "right gaze origin relative tracker [x y z]";
case HeadingItem::eyeTrackerTime:
return "eye tracker time (us)";
case HeadingItem::targetPlayerTime:
Expand Down
49 changes: 35 additions & 14 deletions lib/core/src/OutputFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ static auto operator<<(std::ostream &os, Syllable item) -> std::ostream & {
return os << name(item);
}

static auto operator<<(std::ostream &os, Point3D point) -> std::ostream & {
return os << point.x << ' ' << point.y << ' ' << point.z;
}

static auto operator<<(std::ostream &os, Point2D point) -> std::ostream & {
return os << point.x << ' ' << point.y;
}

template <typename T>
auto insert(std::ostream &stream, T item) -> std::ostream & {
return stream << item;
Expand Down Expand Up @@ -197,21 +205,34 @@ static auto operator<<(std::ostream &stream,
const BinocularGazeSamples &gazeSamples) -> std::ostream & {
insert(stream, name(HeadingItem::eyeTrackerTime));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::leftGaze));
insert(stream, name(HeadingItem::leftGazePositionRelativeScreen));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::rightGazePositionRelativeScreen));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::leftGazePositionRelativeTracker));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::rightGazePositionRelativeTracker));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::leftGazeOriginRelativeTracker));
insertCommaAndSpace(stream);
insert(stream, name(HeadingItem::rightGaze));
std::for_each(gazeSamples.begin(), gazeSamples.end(), [&](auto g) {
insertNewLine(stream);
insert(stream, g.systemTime.microseconds);
insertCommaAndSpace(stream);
insert(stream, g.left.x);
insert(stream, " ");
insert(stream, g.left.y);
insertCommaAndSpace(stream);
insert(stream, g.right.x);
insert(stream, " ");
insert(stream, g.right.y);
});
insert(stream, name(HeadingItem::rightGazeOriginRelativeTracker));
std::for_each(gazeSamples.begin(), gazeSamples.end(),
[&](const BinocularGazeSample &g) {
insertNewLine(stream);
insert(stream, g.systemTime.microseconds);
insertCommaAndSpace(stream);
insert(stream, g.left.position.relativeScreen);
insertCommaAndSpace(stream);
insert(stream, g.right.position.relativeScreen);
insertCommaAndSpace(stream);
insert(stream, g.left.position.relativeTrackbox);
insertCommaAndSpace(stream);
insert(stream, g.right.position.relativeTrackbox);
insertCommaAndSpace(stream);
insert(stream, g.left.origin.relativeTrackbox);
insertCommaAndSpace(stream);
insert(stream, g.right.origin.relativeTrackbox);
});
return insertNewLine(stream);
}

Expand Down
29 changes: 27 additions & 2 deletions lib/domain/include/av-speech-in-noise/Model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,31 @@ struct EyeGaze {
float y;
};

struct Point2D {
float x;
float y;
};

struct Point3D {
float x;
float y;
float z;
};

struct GazeOrigin {
Point3D relativeTrackbox;
};

struct GazePosition {
Point3D relativeTrackbox;
Point2D relativeScreen;
};

struct Gaze {
GazeOrigin origin;
GazePosition position;
};

struct EyeTrackerSystemTime {
std::int_least64_t microseconds;
};
Expand All @@ -177,8 +202,8 @@ struct EyeTrackerTargetPlayerSynchronization {

struct BinocularGazeSample {
EyeTrackerSystemTime systemTime;
EyeGaze left;
EyeGaze right;
Gaze left;
Gaze right;
};

using BinocularGazeSamples = typename std::vector<BinocularGazeSample>;
Expand Down
71 changes: 34 additions & 37 deletions macos/TobiiProEyeTracker.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@

#include "TobiiProEyeTracker.hpp"
#include <gsl/gsl>
#include <thread>
#include <functional>

#include <tobii_research.h>
#include <tobii_research_calibration.h>
#include <tobii_research_streams.h>

#include <gsl/gsl>

#include <thread>
#include <functional>

namespace av_speech_in_noise {
static auto eyeTracker(TobiiResearchEyeTrackers *eyeTrackers)
-> TobiiResearchEyeTracker * {
Expand Down Expand Up @@ -72,53 +75,47 @@ static auto at(const std::vector<TobiiResearchGazeData> &b, gsl::index i)
return b.at(i);
}

static auto eyeGaze(const TobiiResearchEyeData &d)
static auto gazePositionOnDisplayArea(const TobiiResearchEyeData &d)
-> const TobiiResearchNormalizedPoint2D & {
return d.gaze_point.position_on_display_area;
}

static auto leftEyeGaze(const std::vector<TobiiResearchGazeData> &gaze,
gsl::index i) -> const TobiiResearchNormalizedPoint2D & {
return eyeGaze(at(gaze, i).left_eye);
}

static auto rightEyeGaze(const std::vector<TobiiResearchGazeData> &gaze,
gsl::index i) -> const TobiiResearchNormalizedPoint2D & {
return eyeGaze(at(gaze, i).right_eye);
}

static auto x(const TobiiResearchNormalizedPoint2D &p) -> float { return p.x; }

static auto y(const TobiiResearchNormalizedPoint2D &p) -> float { return p.y; }

static auto leftEyeGaze(std::vector<BinocularGazeSample> &b, gsl::index i)
-> EyeGaze & {
return at(b, i).left;
static auto size(const std::vector<BinocularGazeSample> &v) -> gsl::index {
return v.size();
}

static auto rightEyeGaze(BinocularGazeSamples &b, gsl::index i) -> EyeGaze & {
return at(b, i).right;
static void assign(Point3D &p, TobiiResearchPoint3D other) {
p.x = other.x;
p.y = other.y;
p.z = other.z;
}

static auto x(EyeGaze &p) -> float & { return p.x; }

static auto y(EyeGaze &p) -> float & { return p.y; }

static auto size(const std::vector<BinocularGazeSample> &v) -> gsl::index {
return v.size();
static void assign(Point2D &p, TobiiResearchNormalizedPoint2D other) {
p.x = other.x;
p.y = other.y;
}

auto TobiiProTracker::gazeSamples() -> BinocularGazeSamples {
BinocularGazeSamples gazeSamples_(head > 0 ? head - 1 : 0);
for (gsl::index i{0}; i < size(gazeSamples_); ++i) {
at(gazeSamples_, i).systemTime.microseconds =
BinocularGazeSamples gazeSamples(head > 0 ? head - 1 : 0);
for (gsl::index i{0}; i < size(gazeSamples); ++i) {
at(gazeSamples, i).systemTime.microseconds =
at(gazeData, i).system_time_stamp;
x(leftEyeGaze(gazeSamples_, i)) = x(leftEyeGaze(gazeData, i));
y(leftEyeGaze(gazeSamples_, i)) = y(leftEyeGaze(gazeData, i));
x(rightEyeGaze(gazeSamples_, i)) = x(rightEyeGaze(gazeData, i));
y(rightEyeGaze(gazeSamples_, i)) = y(rightEyeGaze(gazeData, i));
assign(at(gazeSamples, i).left.position.relativeScreen,
gazePositionOnDisplayArea(at(gazeData, i).left_eye));
assign(at(gazeSamples, i).right.position.relativeScreen,
gazePositionOnDisplayArea(at(gazeData, i).right_eye));

assign(at(gazeSamples, i).left.position.relativeTrackbox,
at(gazeData, i).left_eye.gaze_point.position_in_user_coordinates);
assign(at(gazeSamples, i).right.position.relativeTrackbox,
at(gazeData, i).right_eye.gaze_point.position_in_user_coordinates);

assign(at(gazeSamples, i).left.origin.relativeTrackbox,
at(gazeData, i).left_eye.gaze_origin.position_in_user_coordinates);
assign(at(gazeSamples, i).right.origin.relativeTrackbox,
at(gazeData, i).right_eye.gaze_origin.position_in_user_coordinates);
}
return gazeSamples_;
return gazeSamples;
}

auto TobiiProTracker::currentSystemTime() -> EyeTrackerSystemTime {
Expand Down
73 changes: 66 additions & 7 deletions test/OutputFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,11 +666,16 @@ class OutputFileTests : public ::testing::Test {
assertHeadingAtLine(useCase, 3);
}

void setEyeGazes(std::vector<std::int_least64_t> t,
std::vector<EyeGaze> left, std::vector<EyeGaze> right) {
void setGazePositionsRelativeScreenAndEyeTrackerTimes(
std::vector<std::int_least64_t> t, std::vector<EyeGaze> left,
std::vector<EyeGaze> right) {
eyeGazes.resize(t.size());
std::generate(eyeGazes.begin(), eyeGazes.end(), [&, n = 0]() mutable {
BinocularGazeSample gazeSamples{{t.at(n)}, left.at(n), right.at(n)};
BinocularGazeSample gazeSamples{{t.at(n)},
Gaze{GazeOrigin{},
GazePosition{{}, Point2D{left.at(n).x, left.at(n).y}}},
Gaze{GazeOrigin{},
GazePosition{{}, Point2D{right.at(n).x, right.at(n).y}}}};
++n;
return gazeSamples;
});
Expand Down Expand Up @@ -978,14 +983,17 @@ OUTPUT_FILE_TEST(writeFixedLevelTestWithAuditoryOnlyCondition) {
assertConditionNameWritten(writingFixedLevelTest, Condition::auditoryOnly);
}

OUTPUT_FILE_TEST(writeEyeGazes) {
setEyeGazes({1, 2, 3}, {{0.4F, 0.44F}, {0.5F, 0.55F}, {0.6F, 0.66F}},
OUTPUT_FILE_TEST(writeGazePositionsRelativeScreenAndEyeTrackerTime) {
setGazePositionsRelativeScreenAndEyeTrackerTimes({1, 2, 3},
{{0.4F, 0.44F}, {0.5F, 0.55F}, {0.6F, 0.66F}},
{{0.7F, 0.77F}, {0.8F, 0.88F}, {0.9F, 0.99F}});
write(file, eyeGazes);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::eyeTrackerTime, 1, 1);
assertNthCommaDelimitedEntryOfLine(writer, HeadingItem::leftGaze, 2, 1);
assertNthCommaDelimitedEntryOfLine(writer, HeadingItem::rightGaze, 3, 1);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::leftGazePositionRelativeScreen, 2, 1);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::rightGazePositionRelativeScreen, 3, 1);
assertNthCommaDelimitedEntryOfLine(writer, "1", 1, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.4 0.44", 2, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.7 0.77", 3, 2);
Expand All @@ -997,6 +1005,57 @@ OUTPUT_FILE_TEST(writeEyeGazes) {
assertNthCommaDelimitedEntryOfLine(writer, "0.9 0.99", 3, 4);
}

OUTPUT_FILE_TEST(writeGazePositionsRelativeTracker) {
std::vector<Point3D> left{
{0.4F, 0.44F, 0.444F}, {0.5, 0.55F, 0.555F}, {0.6F, 0.66F, 0.666F}};
std::vector<Point3D> right{
{0.7F, 0.77F, 0.777F}, {0.8F, 0.88F, 0.888F}, {0.9F, 0.99F, 0.999F}};
eyeGazes.resize(left.size());
std::generate(eyeGazes.begin(), eyeGazes.end(), [&, n = 0]() mutable {
BinocularGazeSample gazeSamples{{},
Gaze{GazeOrigin{}, GazePosition{left.at(n), {}}},
Gaze{GazeOrigin{}, GazePosition{right.at(n), {}}}};
++n;
return gazeSamples;
});
write(file, eyeGazes);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::leftGazePositionRelativeTracker, 4, 1);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::rightGazePositionRelativeTracker, 5, 1);
assertNthCommaDelimitedEntryOfLine(writer, "0.4 0.44 0.444", 4, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.7 0.77 0.777", 5, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.5 0.55 0.555", 4, 3);
assertNthCommaDelimitedEntryOfLine(writer, "0.8 0.88 0.888", 5, 3);
assertNthCommaDelimitedEntryOfLine(writer, "0.6 0.66 0.666", 4, 4);
assertNthCommaDelimitedEntryOfLine(writer, "0.9 0.99 0.999", 5, 4);
}

OUTPUT_FILE_TEST(writeGazeOrigins) {
std::vector<Point3D> left{
{0.4F, 0.44F, 0.444F}, {0.5, 0.55F, 0.555F}, {0.6F, 0.66F, 0.666F}};
std::vector<Point3D> right{
{0.7F, 0.77F, 0.777F}, {0.8F, 0.88F, 0.888F}, {0.9F, 0.99F, 0.999F}};
eyeGazes.resize(left.size());
std::generate(eyeGazes.begin(), eyeGazes.end(), [&, n = 0]() mutable {
BinocularGazeSample gazeSamples{{}, Gaze{GazeOrigin{left.at(n)}, {}},
Gaze{GazeOrigin{right.at(n)}, {}}};
++n;
return gazeSamples;
});
write(file, eyeGazes);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::leftGazeOriginRelativeTracker, 6, 1);
assertNthCommaDelimitedEntryOfLine(
writer, HeadingItem::rightGazeOriginRelativeTracker, 7, 1);
assertNthCommaDelimitedEntryOfLine(writer, "0.4 0.44 0.444", 6, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.7 0.77 0.777", 7, 2);
assertNthCommaDelimitedEntryOfLine(writer, "0.5 0.55 0.555", 6, 3);
assertNthCommaDelimitedEntryOfLine(writer, "0.8 0.88 0.888", 7, 3);
assertNthCommaDelimitedEntryOfLine(writer, "0.6 0.66 0.666", 6, 4);
assertNthCommaDelimitedEntryOfLine(writer, "0.9 0.99 0.999", 7, 4);
}

OUTPUT_FILE_TEST(writeTargetStartTime) {
writeTargetStartTimeNanoseconds(file, 1);
assertContainsColonDelimitedEntry(writer, "target start time (ns)", "1");
Expand Down
Loading

0 comments on commit 6425a2a

Please sign in to comment.