Skip to content

Commit

Permalink
Merge pull request #105 from BoysTownOrg/save-eye-tracker-calibration…
Browse files Browse the repository at this point in the history
…-points

Save eye tracker calibration points
  • Loading branch information
sbashford authored Nov 3, 2023
2 parents 1ebab77 + 0ee3569 commit c5ff84f
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_EYETRACKERCALIBRATIONHPP_

#include <av-speech-in-noise/Interface.hpp>

#include <utility>
#include <vector>
#include <ostream>

namespace av_speech_in_noise::eye_tracker_calibration {
struct Point {
Expand All @@ -17,6 +19,8 @@ struct Result {
Point point{};
};

void write(std::ostream &, const std::vector<Result> &);

class Interactor {
public:
AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(Interactor);
Expand Down Expand Up @@ -57,6 +61,12 @@ class Calibrator {
virtual auto results() -> std::vector<Result> = 0;
};

class ResultsWriter {
public:
AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(ResultsWriter);
virtual void write(const std::vector<Result> &) = 0;
};

namespace validation {
struct Angle {
float degrees;
Expand Down Expand Up @@ -116,7 +126,7 @@ class InteractorImpl : public Interactor, public SubjectPresenter::Observer {
class InteractorImpl : public SubjectPresenter::Observer, public Interactor {
public:
InteractorImpl(SubjectPresenter &, TesterPresenter &, Calibrator &,
std::vector<Point>);
ResultsWriter &, std::vector<Point>);
void start() override;
void finish() override;
void notifyThatPointIsReady() override;
Expand All @@ -128,6 +138,7 @@ class InteractorImpl : public SubjectPresenter::Observer, public Interactor {
SubjectPresenter &subjectPresenter;
TesterPresenter &testerPresenter;
Calibrator &calibrator;
ResultsWriter &writer;
};
}

Expand Down
47 changes: 42 additions & 5 deletions lib/core/src/EyeTrackerCalibration.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
#include "EyeTrackerCalibration.hpp"

#include <algorithm>
#include <cmath>

namespace av_speech_in_noise::eye_tracker_calibration {
static void write(std::ostream &stream, const Point &p) {
stream << p.x;
stream << ", ";
stream << p.y;
}

static void write(std::ostream &stream, const std::vector<Point> &v) {
stream << '[';
auto first{true};
for (const auto p : v) {
if (!first) {
stream << "; ";
}
write(stream, p);
first = false;
}
stream << ']';
}

void write(std::ostream &stream, const std::vector<Result> &v) {
stream << "Point|Left|Right\n";
for (const auto &result : v) {
stream << '[';
write(stream, result.point);
stream << ']';

stream << '|';
write(stream, result.leftEyeMappedPoints);
stream << '|';
write(stream, result.rightEyeMappedPoints);
stream << '\n';
}
}

static auto distance(Point a, Point b) -> float {
return std::hypot(a.x - b.x, a.y - b.y);
}
Expand All @@ -14,18 +49,20 @@ static void present(

InteractorImpl::InteractorImpl(SubjectPresenter &subjectPresenter,
TesterPresenter &testerPresenter, Calibrator &calibrator,
std::vector<Point> points)
ResultsWriter &writer, std::vector<Point> points)
: points{std::move(points)}, subjectPresenter{subjectPresenter},
testerPresenter{testerPresenter}, calibrator{calibrator} {
testerPresenter{testerPresenter}, calibrator{calibrator}, writer{writer} {
subjectPresenter.attach(this);
}

void InteractorImpl::notifyThatPointIsReady() {
calibrator.collect(pointsToCalibrate.front());
pointsToCalibrate.erase(pointsToCalibrate.begin());
if (pointsToCalibrate.empty())
testerPresenter.present(calibrator.results());
else
if (pointsToCalibrate.empty()) {
const auto results{calibrator.results()};
testerPresenter.present(results);
writer.write(results);
} else
present(subjectPresenter, pointsToCalibrate);
}

Expand Down
31 changes: 30 additions & 1 deletion macos/main/with-tobii.mm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@

#include <vector>
#include <algorithm>
#include <fstream>
#include <filesystem>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <string>

@interface AvSpeechInNoiseEyeTrackerCalibrationControlObserverProxy : NSObject
@end
Expand Down Expand Up @@ -316,6 +322,28 @@ void clear() override {
AvSpeechInNoiseEyeTrackerCalibrationControlObserverProxy *observerProxy;
NSWindow *window;
};

class NaiveResultsWriter : public ResultsWriter {
public:
void write(const std::vector<Result> &results) {
std::filesystem::path homeDirectory{
[NSURL fileURLWithPath:@"~".stringByExpandingTildeInPath]
.fileSystemRepresentation};
std::filesystem::path directory{homeDirectory / "Documents" /
"av-speech-eye-tracker-calibration-results"};
std::filesystem::create_directories(directory);

const auto now{std::chrono::system_clock::to_time_t(
std::chrono::system_clock::now())};
std::stringstream fileName;
fileName << std::put_time(std::localtime(&now), "%F_%H-%M-%S")
<< ".txt";

std::string filePath{directory / fileName.str()};
std::ofstream file{filePath};
eye_tracker_calibration::write(file, results);
}
};
}

namespace validation {
Expand Down Expand Up @@ -396,8 +424,9 @@ static void initialize(TobiiProTracker &tracker,
validationSubjectPresenter, validationTesterPresenter, validator,
{{0.5, 0.5}, {0.3F, 0.3F}, {0.3F, 0.7F}, {0.7F, 0.3F}, {0.7F, 0.7F}}};
static auto calibrator{tracker.calibrator()};
static NaiveResultsWriter resultsWriter;
static InteractorImpl interactor{subjectPresenter, testerPresenter,
calibrator,
calibrator, resultsWriter,
{{0.5, 0.5}, {0.1F, 0.1F}, {0.1F, 0.9F}, {0.9F, 0.1F}, {0.9F, 0.9F}}};
static Controller controller{testerUI, interactor};
static validation::Controller validationController{
Expand Down
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ add_executable(
PredeterminedTargetPlaylist.cpp
AudioRecording.cpp
EyeTracking.cpp
RevealImage.cpp)
RevealImage.cpp
EyeTrackerCalibrationSerialization.cpp)
target_compile_features(av-speech-in-noise-test-exe PRIVATE cxx_std_17)
set_target_properties(av-speech-in-noise-test-exe PROPERTIES CXX_EXTENSIONS OFF)
target_compile_options(av-speech-in-noise-test-exe
Expand Down
37 changes: 36 additions & 1 deletion test/EyeTrackerCalibrationInteractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,24 @@ class CalibratorStub : public Calibrator {
bool released_{};
};

class ResultsWriterStub : public ResultsWriter {
public:
void write(const std::vector<Result> &v) override { results_ = v; };

auto results() -> std::vector<Result> { return results_; }

private:
std::vector<Result> results_;
};

class EyeTrackerCalibrationInteractorTests : public ::testing::Test {
protected:
SubjectPresenterStub subjectPresenter;
TesterPresenterStub testerPresenter;
CalibratorStub calibrator;
ResultsWriterStub writer;
InteractorImpl interactor{subjectPresenter, testerPresenter, calibrator,
{{0.1F, 0.2F}, {0.3F, 0.4F}, {0.5, 0.6F}}};
writer, {{0.1F, 0.2F}, {0.3F, 0.4F}, {0.5, 0.6F}}};
};
}

Expand Down Expand Up @@ -218,6 +229,30 @@ EYE_TRACKER_CALIBRATION_INTERACTOR_TEST(stopsPresenterOnFinish) {
AV_SPEECH_IN_NOISE_EXPECT_TRUE(testerPresenter.stopped());
}

EYE_TRACKER_CALIBRATION_INTERACTOR_TEST(
writesResultsAfterFinalPointCalibrated) {
calibrator.set({{{{0.11F, 0.22F}, {0.33F, 0.44F}},
{{0.55F, 0.66F}, {0.77F, 0.88F}}, {0.1F, 0.2F}},
{{{0.99F, 0.111F}, {0.222F, 0.333F}},
{{0.444F, 0.555F}, {0.666F, 0.777F}}, {0.3F, 0.4F}},
{{{0.888F, 0.999F}, {0.01F, 0.02F}}, {{0.03F, 0.04F}, {0.05F, 0.06F}},
{0.5F, 0.6F}}});
interactor.start();
notifyThatPointIsReady(subjectPresenter);
notifyThatPointIsReady(subjectPresenter);
notifyThatPointIsReady(subjectPresenter);

::assertEqual<Result>(
{{{{0.11F, 0.22F}, {0.33F, 0.44F}}, {{0.55F, 0.66F}, {0.77F, 0.88F}},
{0.1F, 0.2F}},
{{{0.99F, 0.111F}, {0.222F, 0.333F}},
{{0.444F, 0.555F}, {0.666F, 0.777F}}, {0.3F, 0.4F}},
{{{0.888F, 0.999F}, {0.01F, 0.02F}},
{{0.03F, 0.04F}, {0.05F, 0.06F}}, {0.5F, 0.6F}}},
writer.results(),
[](const Result &a, const Result &b) { assertEqual(a, b); });
}

EYE_TRACKER_CALIBRATION_INTERACTOR_TEST(startsPresenter) {
interactor.start();
AV_SPEECH_IN_NOISE_EXPECT_TRUE(subjectPresenter.started());
Expand Down
32 changes: 32 additions & 0 deletions test/EyeTrackerCalibrationSerialization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "assert-utility.hpp"

#include <av-speech-in-noise/core/EyeTrackerCalibration.hpp>

#include <gtest/gtest.h>

#include <sstream>

namespace av_speech_in_noise {
class EyeTrackerCalibrationSerializationTests : public ::testing::Test {};

#define EYE_TRACKER_CALIBRATION_SERIALIZATION_TEST(a) \
TEST_F(EyeTrackerCalibrationSerializationTests, a)

EYE_TRACKER_CALIBRATION_SERIALIZATION_TEST(writesResults) {
std::stringstream stream;
std::vector<eye_tracker_calibration::Result> results = {
{{{0.49, 0.51}, {0.48, 0.5}, {0.5, 0.49}},
{{0.39, 0.61}, {0.38, 0.6}, {0.6, 0.39}}, {0.5, 0.5}},
{{{0.29, 0.31}, {0.28, 0.3}, {0.3, 0.29}},
{{0.19, 0.41}, {0.18, 0.4}, {0.4, 0.19}}, {0.3, 0.3}},
{{{0.79, 0.81}, {0.78, 0.8}, {0.8, 0.79}},
{{0.69, 0.91}, {0.68, 0.9}, {0.9, 0.69}}, {0.8, 0.8}}};
write(stream, results);
assertEqual(R"(Point|Left|Right
[0.5, 0.5]|[0.49, 0.51; 0.48, 0.5; 0.5, 0.49]|[0.39, 0.61; 0.38, 0.6; 0.6, 0.39]
[0.3, 0.3]|[0.29, 0.31; 0.28, 0.3; 0.3, 0.29]|[0.19, 0.41; 0.18, 0.4; 0.4, 0.19]
[0.8, 0.8]|[0.79, 0.81; 0.78, 0.8; 0.8, 0.79]|[0.69, 0.91; 0.68, 0.9; 0.9, 0.69]
)",
stream.str());
}
}

0 comments on commit c5ff84f

Please sign in to comment.