Skip to content

Commit 8916a48

Browse files
authored
Merge pull request #26 from ruxailab/develop
GSoC 2024: Eye Tracking Algorithm Optimization Based on Low-Resolution Cameras (Final Project Submission)
2 parents c18618d + 9e13df9 commit 8916a48

39 files changed

+51586
-10535
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
name: CI
44

5-
# Controls when the action will run.
5+
# Controls when the action will run.
66
on:
77
# Triggers the workflow on push or pull request events but only for the main branch
88
push:
9-
branches: [ main ]
9+
branches: [main]
1010
pull_request:
11-
branches: [ main ]
11+
branches: [main]
1212

1313
# Allows you to run this workflow manually from the Actions tab
1414
workflow_dispatch:
@@ -19,7 +19,7 @@ jobs:
1919
build:
2020
# The type of runner that the job will run on
2121
runs-on: ubuntu-latest
22-
22+
2323
# Steps represent a sequence of tasks that will be executed as part of the job
2424
steps:
2525
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
@@ -29,5 +29,3 @@ jobs:
2929
heroku_api_key: ${{secrets.HEROKU_API_KEY}} # Located in GitHub secrets
3030
heroku_app_name: "web-eye-tracker-1204" # Must be unique in Heroku
3131
heroku_email: "karine.pistili@gmail.com"
32-
33-

README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
# 👁️ Eye Lab: Gaze Tracker API
22

3-
Eye Lab is an open source tool to create eye tracking usability tests. It started as a final undergraduation work for Computer Engineering of student [Karine Pistili](https://www.linkedin.com/in/karine-pistili/) that created the first prototype. The idea is to evolve it to a more complete and useful tool with the help of the community.
3+
Eye Lab is an open-source tool to create eye-tracking usability tests. It started as a final undergraduate work for the Computer Engineering student [Karine Pistili](https://www.linkedin.com/in/karine-pistili/) who made the prototype. The idea is to evolve it into a more complete and useful tool with the community's help.
44

5-
The current version of the software allows users to create their usability sessions of an website, recording the webcam, screen and mouse movements and use this information to find out where the user has been looking into the screen by using heatmaps.
5+
The current version of the software allows users to create their usability sessions of a website, recording the webcam, screen, and mouse movements and use this information to find out where the user has been looking into the screen by using heatmaps.
66

77
## 👩‍💻 Setting up project locally
88

9-
The project consists of two parts, this repository contains the backend of the application and the frontend can be found [here](https://github.com/uramakilab/web-eye-tracker-front). Install it as well.
9+
The project consists of two parts, this repository contains the backend of the application, and the frontend can be found [here](https://github.com/uramakilab/web-eye-tracker-front). Install it as well to have the full application running.
1010

1111
### Prerequisites
1212

13-
* [Python 3x](https://www.python.org/downloads/)
13+
- [Python 3x](https://www.python.org/downloads/)
1414

1515
### 1. Create virtual environment
1616

17-
Before installing all dependencies and starting your Flask Server, it is better to create a python virtual environment. You can use the [venv package](https://docs.python.org/3/library/venv.html)
17+
Before installing all dependencies and starting your Flask Server, it is better to create a Python virtual environment. You can use the [venv package](https://docs.python.org/3/library/venv.html) to create a virtual environment. To create a new virtual environment, run the following command:
1818

1919
```
2020
python -m venv /path/to/new/virtual/environment
2121
```
2222

23-
Then activate your env. On windows for example you can activate with the script:
23+
Then activate your environment. On Windows for example you can activate with the script:
2424

2525
```
2626
name-of-event/Scripts/activate
@@ -40,12 +40,18 @@ pip install -r requirements.txt
4040
flask run
4141
```
4242

43+
## Contributors ✨
44+
45+
The project is selected to be part of the [Google Summer of Code 2024](https://summerofcode.withgoogle.com/programs/2024/organizations/uramaki-lab) program, and [Vinícius Cavalcanti](https://github.com/hvini) is the main mentor of the project along with [Marc Gonzalez Capdevila](https://github.com/marcgc21), [Karine Pistili Rodrigues](https://github.com/KarinePistili). The active development of the project is being done by [Sitam Meur](https://github.com/sitamgithub-MSIT), selected as a GSoC'24 student for the project. Here are the project details in the [GSoC'24 website](https://summerofcode.withgoogle.com/programs/2024/projects/lEPzZg7S).
46+
47+
To see the full list of contributions, check out the ahead commits of the "develop" branch concerning the "main" branch. Full logs of the project development can be found in the [Daily Work Progress](https://docs.google.com/document/d/1RjCnGjYYgPKvFUrN8hSjPX29aayWr6eEopeCN3QZwEQ/edit?usp=sharing) file. Hoping to see your name in the list of contributors soon! 🚀
48+
4349
## 🧑‍🤝‍🧑 Contributing
4450

45-
Anyone is free to contribute to this project. Just do a pull request with your code and if it is all good we will accept it. You can also help us look for bugs, if you find anything create and issue.
51+
Anyone is free to contribute to this project. Just do a pull request with your code and if it is all good we will accept it. You can also help us look for bugs if you find anything that creates an issue.
4652

4753
## 📃 License
4854

49-
This software is under the [MIT License](https://opensource.org/licenses/MIT).
55+
This software is under the [MIT License](https://opensource.org/licenses/MIT).
5056

5157
Copyright 2021 Uramaki Lab

app/main.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
# Necessary imports
12
from flask import Flask, request, Response
23
from flask_cors import CORS
4+
5+
# Local imports from app
36
from app.routes import session as session_route
4-
import os
57

8+
9+
# Initialize Flask app and enable CORS
610
app = Flask(__name__)
711
CORS(app)
812

13+
914
# @app.route('/', methods=['GET'])
1015
# def welcome():
1116
# return Response(f'Welcome to EyeLab!', status=200, mimetype='application/json')
@@ -51,8 +56,18 @@
5156
# return Response('Invalid request method for route', status=405, mimetype='application/json')
5257

5358

54-
@app.route('/api/session/calib_validation', methods=['POST'])
59+
# Route for validating calibration
60+
@app.route("/api/session/calib_validation", methods=["POST"])
5561
def calib_validation():
56-
if request.method == 'POST':
62+
"""
63+
Validates the calibration request.
64+
65+
Returns:
66+
If the request method is 'POST', it calls the `calib_results` function from the `session_route` module.
67+
Otherwise, it returns a `Response` object with an error message and status code 405.
68+
"""
69+
if request.method == "POST":
5770
return session_route.calib_results()
58-
return Response('Invalid request method for route', status=405, mimetype='application/json')
71+
return Response(
72+
"Invalid request method for route", status=405, mimetype="application/json"
73+
)

app/models/session.py

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
class Session:
2-
def __init__(self, id, title, description, user_id, created_date, website_url, screen_record_url, webcam_record_url, heatmap_url, calib_points, iris_points):
2+
"""
3+
Represents a session in the eye tracking application.
4+
5+
Attributes:
6+
- id (int): The unique identifier of the session.
7+
- title (str): The title of the session.
8+
- description (str): The description of the session.
9+
- user_id (int): The user ID associated with the session.
10+
- created_date (datetime): The date and time when the session was created.
11+
- website_url (str): The URL of the website being tracked.
12+
- screen_record_url (str): The URL of the screen recording for the session.
13+
- webcam_record_url (str): The URL of the webcam recording for the session.
14+
- heatmap_url (str): The URL of the heatmap image for the session.
15+
- calib_points (list): The calibration points used in the session.
16+
- iris_points (list): The iris tracking points recorded in the session.
17+
"""
18+
19+
def __init__(
20+
self,
21+
id,
22+
title,
23+
description,
24+
user_id,
25+
created_date,
26+
website_url,
27+
screen_record_url,
28+
webcam_record_url,
29+
heatmap_url,
30+
calib_points,
31+
iris_points,
32+
):
333
self.id = id
434
self.title = title
535
self.description = description
@@ -13,16 +43,22 @@ def __init__(self, id, title, description, user_id, created_date, website_url, s
1343
self.iris_points = iris_points
1444

1545
def to_dict(self):
46+
"""
47+
Converts the session object to a dictionary.
48+
49+
Returns:
50+
dict: A dictionary representation of the session object.
51+
"""
1652
return {
17-
u'id': self.id,
18-
u'title': self.title,
19-
u'description': self.description,
20-
u'user_id': self.user_id,
21-
u'created_date': self.created_date,
22-
u'website_url': self.website_url,
23-
u'screen_record_url': self.screen_record_url,
24-
u'webcam_record_url': self.webcam_record_url,
25-
u'heatmap_url': self.heatmap_url,
26-
u'callib_points': self.calib_points,
27-
u'iris_points': self.iris_points
28-
}
53+
"id": self.id,
54+
"title": self.title,
55+
"description": self.description,
56+
"user_id": self.user_id,
57+
"created_date": self.created_date,
58+
"website_url": self.website_url,
59+
"screen_record_url": self.screen_record_url,
60+
"webcam_record_url": self.webcam_record_url,
61+
"heatmap_url": self.heatmap_url,
62+
"callib_points": self.calib_points,
63+
"iris_points": self.iris_points,
64+
}

app/routes/session.py

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
from flask import Flask, request, Response, send_file
2-
from app.services.storage import save_file_locally
3-
from app.models.session import Session
4-
#from app.services import database as db
5-
from app.services import gaze_tracker
1+
# Necesary imports
2+
import os
3+
import re
64
import time
75
import json
86
import csv
7+
98
from pathlib import Path
10-
import os
11-
import re
9+
from flask import Flask, request, Response, send_file
10+
11+
# Local imports from app
12+
from app.services.storage import save_file_locally
13+
from app.models.session import Session
14+
15+
# from app.services import database as db
16+
from app.services import gaze_tracker
1217

13-
ALLOWED_EXTENSIONS = {'txt', 'webm'}
14-
COLLECTION_NAME = u'session'
1518

19+
# Constants
20+
ALLOWED_EXTENSIONS = {"txt", "webm"}
21+
COLLECTION_NAME = "session"
22+
23+
# Initialize Flask app
1624
app = Flask(__name__)
1725

1826

@@ -139,49 +147,85 @@
139147

140148

141149
def calib_results():
142-
file_name = json.loads(request.form['file_name'])
143-
fixed_points = json.loads(request.form['fixed_circle_iris_points'])
144-
calib_points = json.loads(request.form['calib_circle_iris_points'])
145-
screen_height = json.loads(request.form['screen_height'])
146-
screen_width = json.loads(request.form['screen_width'])
147-
k = json.loads(request.form['k'])
150+
"""
151+
Generate calibration results.
152+
153+
This function generates calibration results based on the provided form data.
154+
It saves the calibration points to a CSV file. Then, it uses the gaze_tracker module to predict the calibration results.
155+
156+
Returns:
157+
Response: A JSON response containing the calibration results.
158+
159+
Raises:
160+
IOError: If there is an error while writing to the CSV files.
161+
"""
162+
# Get form data from request
163+
file_name = json.loads(request.form["file_name"])
164+
fixed_points = json.loads(request.form["fixed_circle_iris_points"])
165+
calib_points = json.loads(request.form["calib_circle_iris_points"])
166+
screen_height = json.loads(request.form["screen_height"])
167+
screen_width = json.loads(request.form["screen_width"])
168+
k = json.loads(request.form["k"])
169+
model = json.loads(request.form["model"])
148170

149171
# Generate csv dataset of calibration points
150172
os.makedirs(
151-
f'{Path().absolute()}/app/services/calib_validation/csv/data/', exist_ok=True)
152-
calib_csv_file = f'{Path().absolute()}/app/services/calib_validation/csv/data/{file_name}_fixed_train_data.csv'
153-
csv_columns = ['left_iris_x', 'left_iris_y',
154-
'right_iris_x', 'right_iris_y', 'point_x', 'point_y', 'screen_height', 'screen_width']
173+
f"{Path().absolute()}/app/services/calib_validation/csv/data/", exist_ok=True
174+
)
175+
176+
# Generate csv of calibration points with following columns
177+
calib_csv_file = f"{Path().absolute()}/app/services/calib_validation/csv/data/{file_name}_fixed_train_data.csv"
178+
csv_columns = [
179+
"left_iris_x",
180+
"left_iris_y",
181+
"right_iris_x",
182+
"right_iris_y",
183+
"point_x",
184+
"point_y",
185+
"screen_height",
186+
"screen_width",
187+
]
188+
189+
# Save calibration points to CSV file
155190
try:
156-
with open(calib_csv_file, 'w') as csvfile:
191+
# Open CSV file
192+
with open(calib_csv_file, "w") as csvfile:
157193
writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
158194
writer.writeheader()
195+
196+
# Write calibration points to CSV file
159197
for data in fixed_points:
160-
data['screen_height'] = screen_height
161-
data['screen_width'] = screen_width
198+
data["screen_height"] = screen_height
199+
data["screen_width"] = screen_width
162200
writer.writerow(data)
201+
202+
# Handle I/O error
163203
except IOError:
164204
print("I/O error")
165205

166206
# Generate csv of iris points of session
167207
os.makedirs(
168-
f'{Path().absolute()}/app/services/calib_validation/csv/data/', exist_ok=True)
169-
predict_csv_file = f'{Path().absolute()}/app/services/calib_validation/csv/data/{file_name}_predict_train_data.csv'
170-
csv_columns = ['left_iris_x', 'left_iris_y',
171-
'right_iris_x', 'right_iris_y']
208+
f"{Path().absolute()}/app/services/calib_validation/csv/data/", exist_ok=True
209+
)
210+
predict_csv_file = f"{Path().absolute()}/app/services/calib_validation/csv/data/{file_name}_predict_train_data.csv"
211+
csv_columns = ["left_iris_x", "left_iris_y", "right_iris_x", "right_iris_y"]
172212
try:
173-
with open(predict_csv_file, 'w') as csvfile:
213+
with open(predict_csv_file, "w") as csvfile:
174214
writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
175215
writer.writeheader()
176216
for data in calib_points:
217+
# print(data)
177218
writer.writerow(data)
178219
except IOError:
179220
print("I/O error")
180221

181222
# data = gaze_tracker.train_to_validate_calib(calib_csv_file, predict_csv_file)
182-
data = gaze_tracker.predict(calib_csv_file, calib_csv_file, k)
183223

184-
return Response(json.dumps(data), status=200, mimetype='application/json')
224+
# Predict calibration results
225+
data = gaze_tracker.predict(calib_csv_file, k, model_X=model, model_Y=model)
226+
227+
# Return calibration results
228+
return Response(json.dumps(data), status=200, mimetype="application/json")
185229

186230

187231
# def session_results():
File renamed without changes.

0 commit comments

Comments
 (0)