glassesTools 1.3.2__tar.gz → 1.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {glassestools-1.3.2/src/glassesTools.egg-info → glassestools-1.3.4}/PKG-INFO +5 -4
- {glassestools-1.3.2 → glassestools-1.3.4}/README.md +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4}/requirements.txt +3 -2
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/aruco.py +4 -6
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/gaze_headref.py +6 -6
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/gaze_worldref.py +13 -16
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/SMI_ETG.py +4 -3
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/SeeTrue_STONE.py +3 -3
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/adhawk_mindlink.py +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/pupilLabs.py +2 -2
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/tobii_G2.py +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/tobii_G3.py +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/ocv.py +2 -2
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/plane.py +14 -15
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/version.py +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/video_gui.py +17 -6
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/video_utils.py +1 -1
- {glassestools-1.3.2 → glassestools-1.3.4/src/glassesTools.egg-info}/PKG-INFO +5 -4
- glassestools-1.3.4/src/glassesTools.egg-info/requires.txt +10 -0
- glassestools-1.3.2/src/glassesTools.egg-info/requires.txt +0 -9
- {glassestools-1.3.2 → glassestools-1.3.4}/LICENSE +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/MANIFEST.in +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/pyproject.toml +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/setup.cfg +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/setup.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/__init__.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/data_files.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/drawing.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/eyetracker.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/importing/__init__.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/intervals.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/marker.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/__init__.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/core.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/iso.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/mpeglookups.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/non_iso.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/summary.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/mp4analyser/util.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/recording.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/timestamps.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/transforms.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools/utils.py +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools.egg-info/SOURCES.txt +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools.egg-info/dependency_links.txt +0 -0
- {glassestools-1.3.2 → glassestools-1.3.4}/src/glassesTools.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: glassesTools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: Tools for processing wearable eye tracker recordings.
|
|
5
5
|
Home-page: https://github.com/dcnieho/glassesTools
|
|
6
6
|
Author: Diederick Niehorster
|
|
@@ -38,9 +38,10 @@ License-File: LICENSE
|
|
|
38
38
|
Requires-Dist: numpy
|
|
39
39
|
Requires-Dist: pandas
|
|
40
40
|
Requires-Dist: msgpack
|
|
41
|
-
Requires-Dist:
|
|
41
|
+
Requires-Dist: imgui_bundle==1.3.0
|
|
42
|
+
Requires-Dist: opencv-python-headless==4.10.0.82
|
|
42
43
|
Requires-Dist: ffpyplayer
|
|
43
|
-
Requires-Dist:
|
|
44
|
+
Requires-Dist: eulerangles
|
|
44
45
|
Provides-Extra: gui
|
|
45
46
|
Requires-Dist: imgui_bundle==1.3.0; extra == "gui"
|
|
46
47
|
|
|
@@ -48,7 +49,7 @@ Requires-Dist: imgui_bundle==1.3.0; extra == "gui"
|
|
|
48
49
|
[](https://pypi.org/project/glassesTools/)
|
|
49
50
|
[](https://pypi.org/project/glassesTools/)
|
|
50
51
|
|
|
51
|
-
# GlassesTools v1.3.
|
|
52
|
+
# GlassesTools v1.3.4
|
|
52
53
|
Tools for processing wearable eye tracker recordings.
|
|
53
54
|
|
|
54
55
|
If you use this package or any of the code in this repository, please cite:<br>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[](https://pypi.org/project/glassesTools/)
|
|
3
3
|
[](https://pypi.org/project/glassesTools/)
|
|
4
4
|
|
|
5
|
-
# GlassesTools v1.3.
|
|
5
|
+
# GlassesTools v1.3.4
|
|
6
6
|
Tools for processing wearable eye tracker recordings.
|
|
7
7
|
|
|
8
8
|
If you use this package or any of the code in this repository, please cite:<br>
|
|
@@ -136,10 +136,10 @@ def create_board(board_corner_points: list[np.ndarray], ids: list[int], ArUco_di
|
|
|
136
136
|
return cv2.aruco.Board(board_corner_points, ArUco_dict, np.array(ids))
|
|
137
137
|
|
|
138
138
|
def run_pose_estimation(in_video, frame_timestamp_file, camera_calibration_file,
|
|
139
|
-
output_dir,
|
|
139
|
+
output_dir,
|
|
140
140
|
processing_intervals,
|
|
141
141
|
ArUco_board, aruco_params, min_num_markers,
|
|
142
|
-
gui, arm_length, sub_pixel_fac = 8, show_rejected_markers = False):
|
|
142
|
+
gui, arm_length, sub_pixel_fac = 8, show_rejected_markers = False) -> tuple[bool, list[plane.Pose]]:
|
|
143
143
|
show_visualization = gui is not None
|
|
144
144
|
|
|
145
145
|
# open video
|
|
@@ -193,16 +193,14 @@ def run_pose_estimation(in_video, frame_timestamp_file, camera_calibration_file,
|
|
|
193
193
|
# keys is populated above
|
|
194
194
|
if 's' in keys:
|
|
195
195
|
# screenshot
|
|
196
|
-
cv2.imwrite(
|
|
196
|
+
cv2.imwrite(output_dir / f'detect_frame_{frame_idx}.png', frame)
|
|
197
197
|
gui.update_image(frame, frame_ts/1000., frame_idx)
|
|
198
198
|
closed, = gui.get_state()
|
|
199
199
|
if closed:
|
|
200
200
|
stop_all_processing = True
|
|
201
201
|
break
|
|
202
202
|
|
|
203
|
-
plane.Pose.writeToFile(poses, output_dir / out_file, skip_failed=True)
|
|
204
|
-
|
|
205
203
|
if show_visualization:
|
|
206
204
|
gui.stop()
|
|
207
205
|
|
|
208
|
-
return stop_all_processing
|
|
206
|
+
return stop_all_processing, poses
|
|
@@ -30,11 +30,6 @@ class Gaze:
|
|
|
30
30
|
self.gaze_dir_r : np.ndarray = gaze_dir_r
|
|
31
31
|
self.gaze_ori_r : np.ndarray = gaze_ori_r
|
|
32
32
|
|
|
33
|
-
@staticmethod
|
|
34
|
-
def readFromFile(fileName:str|pathlib.Path) -> tuple[dict[int,list['Gaze']], int]:
|
|
35
|
-
return data_files.read_file(fileName,
|
|
36
|
-
Gaze, False, False, True)
|
|
37
|
-
|
|
38
33
|
def draw(self, img:np.ndarray, cameraParams:ocv.CameraParams=None, subPixelFac=1):
|
|
39
34
|
drawing.openCVCircle(img, self.gaze_pos_vid, 8, (0,255,0), 2, subPixelFac)
|
|
40
35
|
# draw 3D gaze point as well, usually coincides with 2D gaze point, but not always. E.g. the Adhawk MindLink may
|
|
@@ -43,4 +38,9 @@ class Gaze:
|
|
|
43
38
|
camRot = np.zeros((1,3)) if cameraParams.rotation_vec is None else cameraParams.rotation_vec
|
|
44
39
|
camPos = np.zeros((1,3)) if cameraParams.position is None else cameraParams.position
|
|
45
40
|
a = cv2.projectPoints(np.array(self.gaze_pos_3d).reshape(1,3),camRot,camPos,cameraParams.camera_mtx,cameraParams.distort_coeffs)[0][0][0]
|
|
46
|
-
drawing.openCVCircle(img, a, 5, (0,255,255), -1, subPixelFac)
|
|
41
|
+
drawing.openCVCircle(img, a, 5, (0,255,255), -1, subPixelFac)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_dict_from_file(fileName:str|pathlib.Path) -> tuple[dict[int,list[Gaze]], int]:
|
|
45
|
+
return data_files.read_file(fileName,
|
|
46
|
+
Gaze, False, False, True)
|
|
@@ -50,18 +50,6 @@ class Gaze:
|
|
|
50
50
|
self.gazePosPlane2DLeft = gazePosPlane2DLeft # lGaze3D in poster space
|
|
51
51
|
self.gazePosPlane2DRight = gazePosPlane2DRight # rGaze3D in poster space
|
|
52
52
|
|
|
53
|
-
@staticmethod
|
|
54
|
-
def readFromFile(fileName:str|pathlib.Path, start:int=None, end:int=None) -> dict[int,list['Gaze']]:
|
|
55
|
-
return data_files.read_file(fileName,
|
|
56
|
-
Gaze, False, False, True,
|
|
57
|
-
start=start,end=end)[0]
|
|
58
|
-
|
|
59
|
-
@staticmethod
|
|
60
|
-
def writeToFile(gazes: list['Gaze'] | dict[int,list['Gaze']], fileName:str|pathlib.Path, skip_missing=False):
|
|
61
|
-
data_files.write_array_to_file(gazes, fileName,
|
|
62
|
-
Gaze._columns_compressed,
|
|
63
|
-
skip_all_nan=skip_missing)
|
|
64
|
-
|
|
65
53
|
def drawOnWorldVideo(self, img, cameraParams: ocv.CameraParams, subPixelFac=1):
|
|
66
54
|
# project to camera, display
|
|
67
55
|
def project_and_draw(img,pos,sz,clr):
|
|
@@ -107,14 +95,23 @@ class Gaze:
|
|
|
107
95
|
if self.gazePosPlane2D_vidPos_ray is not None:
|
|
108
96
|
reference.draw(img, *self.gazePosPlane2D_vidPos_ray, subPixelFac, (255,255,0), 3)
|
|
109
97
|
|
|
98
|
+
def read_dict_from_file(fileName:str|pathlib.Path, start:int=None, end:int=None) -> dict[int,list[Gaze]]:
|
|
99
|
+
return data_files.read_file(fileName,
|
|
100
|
+
Gaze, False, False, True,
|
|
101
|
+
start=start,end=end)[0]
|
|
102
|
+
|
|
103
|
+
def write_dict_to_file(gazes: list[Gaze] | dict[int,list[Gaze]], fileName:str|pathlib.Path, skip_missing=False):
|
|
104
|
+
data_files.write_array_to_file(gazes, fileName,
|
|
105
|
+
Gaze._columns_compressed,
|
|
106
|
+
skip_all_nan=skip_missing)
|
|
110
107
|
|
|
111
108
|
def gazes_head_to_world(poses: list[plane.Pose], gazes_head: dict[int,list[gaze_headref.Gaze]], cameraParams: ocv.CameraParams) -> dict[int,list[Gaze]]:
|
|
112
109
|
from . import transforms
|
|
113
|
-
|
|
110
|
+
plane_gazes = {}
|
|
114
111
|
for frame_idx in poses:
|
|
115
112
|
if frame_idx in gazes_head:
|
|
116
|
-
|
|
113
|
+
plane_gazes[frame_idx] = []
|
|
117
114
|
for gaze in gazes_head[frame_idx]:
|
|
118
115
|
gazePoster = transforms.gazeToPlane(gaze, poses[frame_idx], cameraParams)
|
|
119
|
-
|
|
120
|
-
return
|
|
116
|
+
plane_gazes[frame_idx].append(gazePoster)
|
|
117
|
+
return plane_gazes
|
|
@@ -20,7 +20,7 @@ import numpy as np
|
|
|
20
20
|
import math
|
|
21
21
|
import configparser
|
|
22
22
|
from io import StringIO
|
|
23
|
-
|
|
23
|
+
import eulerangles
|
|
24
24
|
|
|
25
25
|
from ..recording import Recording
|
|
26
26
|
from ..eyetracker import EyeTracker
|
|
@@ -187,7 +187,8 @@ def getCameraFromFile(inputDir: str|pathlib.Path, outputDir: str|pathlib.Path):
|
|
|
187
187
|
# 2. sensor offsets seem to be relative to center of sensor
|
|
188
188
|
camera['principalPoint'] = camera['resolution']/2.+camera['sensorOffsets']
|
|
189
189
|
# 3. turn euler angles into rotation matrix (180-Rz because poster space has positive X rightward and positive Y downward)
|
|
190
|
-
|
|
190
|
+
eulers = [camera['eulerAngles'][0], camera['eulerAngles'][1], 180-camera['eulerAngles'][2]]
|
|
191
|
+
camera['rotation'] = eulerangles.euler2matrix(eulers, 'xyz', intrinsic=True, right_handed_rotation=True)
|
|
191
192
|
|
|
192
193
|
# turn into camera matrix and distortion coefficients as used by OpenCV
|
|
193
194
|
camera['cameraMatrix'] = np.identity(3)
|
|
@@ -203,7 +204,7 @@ def getCameraFromFile(inputDir: str|pathlib.Path, outputDir: str|pathlib.Path):
|
|
|
203
204
|
|
|
204
205
|
|
|
205
206
|
# store to file
|
|
206
|
-
fs = cv2.FileStorage(
|
|
207
|
+
fs = cv2.FileStorage(outputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
207
208
|
for key,value in camera.items():
|
|
208
209
|
fs.write(name=key,val=value)
|
|
209
210
|
fs.release()
|
|
@@ -127,7 +127,7 @@ def copySeeTrueRecording(inputDir: pathlib.Path, outputDir: pathlib.Path, recInf
|
|
|
127
127
|
# get scene video dimensions by interrogating a frame in sceneVidDir
|
|
128
128
|
sceneVidDir = inputDir / ('ScenePics_' + recInfo.name)
|
|
129
129
|
frame = next(sceneVidDir.glob('*.jpeg'))
|
|
130
|
-
h,w,_ = cv2.imread(
|
|
130
|
+
h,w,_ = cv2.imread(frame).shape
|
|
131
131
|
|
|
132
132
|
# prep gaze data and get video frame timestamps from it
|
|
133
133
|
print(' Prepping gaze data...')
|
|
@@ -182,7 +182,7 @@ def copySeeTrueRecording(inputDir: pathlib.Path, outputDir: pathlib.Path, recInf
|
|
|
182
182
|
blackIm = np.zeros((h,w,3), np.uint8) # black image
|
|
183
183
|
for f in blackFrames:
|
|
184
184
|
# store black frame to file
|
|
185
|
-
cv2.imwrite(
|
|
185
|
+
cv2.imwrite(sceneVidDir / f'frame_{f:d}.jpeg',blackIm)
|
|
186
186
|
frames.append(f)
|
|
187
187
|
frames = sorted(frames)
|
|
188
188
|
|
|
@@ -253,7 +253,7 @@ def getCameraHardcoded(outputDir: str|pathlib.Path):
|
|
|
253
253
|
camera['resolution'] = np.array([640, 480])
|
|
254
254
|
|
|
255
255
|
# store to file
|
|
256
|
-
fs = cv2.FileStorage(
|
|
256
|
+
fs = cv2.FileStorage(outputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
257
257
|
for key,value in camera.items():
|
|
258
258
|
fs.write(name=key,val=value)
|
|
259
259
|
fs.release()
|
|
@@ -168,7 +168,7 @@ def getCameraHardcoded(outputDir: str|pathlib.Path):
|
|
|
168
168
|
camera['rotation'] = cv2.Rodrigues(np.radians(np.array([12.000000000000043, 0.0, 0.0])))[0]
|
|
169
169
|
|
|
170
170
|
# store to file
|
|
171
|
-
fs = cv2.FileStorage(
|
|
171
|
+
fs = cv2.FileStorage(outputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
172
172
|
for key,value in camera.items():
|
|
173
173
|
fs.write(name=key,val=value)
|
|
174
174
|
fs.release()
|
|
@@ -354,7 +354,7 @@ def getCameraCalFromCloudExport(inputDir: str|pathlib.Path, outputDir: str|pathl
|
|
|
354
354
|
return camInfo['resolution']
|
|
355
355
|
|
|
356
356
|
def storeCameraCalibration(camInfo: dict[str, typing.Any], outputDir: str|pathlib.Path):
|
|
357
|
-
fs = cv2.FileStorage(
|
|
357
|
+
fs = cv2.FileStorage(outputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
358
358
|
for key,value in camInfo.items():
|
|
359
359
|
fs.write(name=key,val=value)
|
|
360
360
|
fs.release()
|
|
@@ -376,7 +376,7 @@ def getSceneCameraResolution(inputDir: str|pathlib.Path, recInfo: Recording):
|
|
|
376
376
|
return np.array(getCamInfo(inputDir / 'world.intrinsics')['resolution'])
|
|
377
377
|
else:
|
|
378
378
|
import cv2
|
|
379
|
-
cap = cv2.VideoCapture(
|
|
379
|
+
cap = cv2.VideoCapture(recInfo.get_scene_video_path())
|
|
380
380
|
if cap.isOpened():
|
|
381
381
|
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
382
382
|
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
@@ -205,7 +205,7 @@ def getCameraFromTSLV(inputDir: str|pathlib.Path):
|
|
|
205
205
|
|
|
206
206
|
|
|
207
207
|
# store to file
|
|
208
|
-
fs = cv2.FileStorage(
|
|
208
|
+
fs = cv2.FileStorage(inputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
209
209
|
for key,value in camera.items():
|
|
210
210
|
fs.write(name=key,val=value)
|
|
211
211
|
fs.release()
|
|
@@ -185,7 +185,7 @@ def getCameraFromJson(inputDir: str|pathlib.Path, outputDir: str|pathlib.Path):
|
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
# store to file
|
|
188
|
-
fs = cv2.FileStorage(
|
|
188
|
+
fs = cv2.FileStorage(outputDir / 'calibration.xml', cv2.FILE_STORAGE_WRITE)
|
|
189
189
|
for key,value in camera.items():
|
|
190
190
|
fs.write(name=key,val=value)
|
|
191
191
|
fs.release()
|
|
@@ -26,7 +26,7 @@ class CameraParams:
|
|
|
26
26
|
if not fileName.is_file():
|
|
27
27
|
return CameraParams(None,None)
|
|
28
28
|
|
|
29
|
-
fs = cv2.FileStorage(
|
|
29
|
+
fs = cv2.FileStorage(fileName, cv2.FILE_STORAGE_READ)
|
|
30
30
|
resolution = fs.getNode("resolution").mat()
|
|
31
31
|
cameraMatrix = fs.getNode("cameraMatrix").mat()
|
|
32
32
|
distCoeff = fs.getNode("distCoeff").mat()
|
|
@@ -53,7 +53,7 @@ class CV2VideoReader:
|
|
|
53
53
|
else:
|
|
54
54
|
self.ts = timestamps
|
|
55
55
|
|
|
56
|
-
self.cap = cv2.VideoCapture(
|
|
56
|
+
self.cap = cv2.VideoCapture(self.file)
|
|
57
57
|
if not self.cap.isOpened():
|
|
58
58
|
raise RuntimeError('the file "{}" could not be opened'.format(str(self.file)))
|
|
59
59
|
self.nframes= float(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
@@ -44,7 +44,7 @@ class Plane:
|
|
|
44
44
|
img = None
|
|
45
45
|
# read from file if image exists
|
|
46
46
|
if ref_image_store_path is not None and ref_image_store_path.is_file():
|
|
47
|
-
img = cv2.imread(
|
|
47
|
+
img = cv2.imread(ref_image_store_path, cv2.IMREAD_COLOR)
|
|
48
48
|
# if image doesn't exist or is the wrong size, create
|
|
49
49
|
if img is None or img.shape[1]!=ref_image_width:
|
|
50
50
|
img = self._store_reference_image(ref_image_store_path, ref_image_width)
|
|
@@ -209,7 +209,7 @@ class Plane:
|
|
|
209
209
|
img[idxs[0]:idxs[1], idxs[2]:idxs[3]] = mark
|
|
210
210
|
|
|
211
211
|
if path:
|
|
212
|
-
cv2.imwrite(
|
|
212
|
+
cv2.imwrite(path, img)
|
|
213
213
|
|
|
214
214
|
return img
|
|
215
215
|
|
|
@@ -246,18 +246,6 @@ class Pose:
|
|
|
246
246
|
self._RtMatInv = None
|
|
247
247
|
self._iH = None
|
|
248
248
|
|
|
249
|
-
@staticmethod
|
|
250
|
-
def readFromFile(fileName:str|pathlib.Path, start:int=None, end:int=None) -> dict[int,'Pose']:
|
|
251
|
-
return data_files.read_file(fileName,
|
|
252
|
-
Pose, True, True, False,
|
|
253
|
-
start=start, end=end)[0]
|
|
254
|
-
|
|
255
|
-
@staticmethod
|
|
256
|
-
def writeToFile(poses: list['Pose'], fileName:str|pathlib.Path, skip_failed=False):
|
|
257
|
-
data_files.write_array_to_file(poses, fileName,
|
|
258
|
-
Pose._columns_compressed,
|
|
259
|
-
skip_all_nan=skip_failed)
|
|
260
|
-
|
|
261
249
|
def camToWorld(self, point: np.ndarray):
|
|
262
250
|
if (self.pose_R_vec is None) or (self.pose_T_vec is None) or np.any(np.isnan(point)):
|
|
263
251
|
return np.full((3,), np.nan)
|
|
@@ -335,4 +323,15 @@ class Pose:
|
|
|
335
323
|
vector /= np.sqrt((vector**2).sum())
|
|
336
324
|
|
|
337
325
|
# find intersection of 3D gaze with poster
|
|
338
|
-
return transforms.intersect_plane_ray(self._planeNormal, self._planePoint, vector.flatten(), origin.flatten())
|
|
326
|
+
return transforms.intersect_plane_ray(self._planeNormal, self._planePoint, vector.flatten(), origin.flatten())
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def read_dict_from_file(fileName:str|pathlib.Path, start:int=None, end:int=None) -> dict[int,Pose]:
|
|
330
|
+
return data_files.read_file(fileName,
|
|
331
|
+
Pose, True, True, False,
|
|
332
|
+
start=start, end=end)[0]
|
|
333
|
+
|
|
334
|
+
def write_list_to_file(poses: list[Pose], fileName:str|pathlib.Path, skip_failed=False):
|
|
335
|
+
data_files.write_array_to_file(poses, fileName,
|
|
336
|
+
Pose._columns_compressed,
|
|
337
|
+
skip_all_nan=skip_failed)
|
|
@@ -7,6 +7,7 @@ except ImportError:
|
|
|
7
7
|
|
|
8
8
|
import time
|
|
9
9
|
import threading
|
|
10
|
+
import numpy as np
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# simple GUI provider for viewer and coder windows in glassesValidator.process
|
|
@@ -31,6 +32,7 @@ class GUI:
|
|
|
31
32
|
imgui.WindowFlags_.always_auto_resize
|
|
32
33
|
)
|
|
33
34
|
self._window_visible = {}
|
|
35
|
+
self._window_frame = {}
|
|
34
36
|
|
|
35
37
|
self._interesting_keys = {}
|
|
36
38
|
self._pressed_keys = {}
|
|
@@ -47,6 +49,7 @@ class GUI:
|
|
|
47
49
|
self._new_frame[id] = (None, None, -1)
|
|
48
50
|
self._current_frame[id] = (None, None, -1)
|
|
49
51
|
self._window_visible[id] = False
|
|
52
|
+
self._window_frame[id] = -1
|
|
50
53
|
|
|
51
54
|
self._next_window_id += 1
|
|
52
55
|
return id
|
|
@@ -139,6 +142,7 @@ class GUI:
|
|
|
139
142
|
params = hello_imgui.RunnerParams()
|
|
140
143
|
params.app_window_params.window_geometry.size_auto = True
|
|
141
144
|
params.app_window_params.restore_previous_geometry = False
|
|
145
|
+
params.ini_folder_type = hello_imgui.IniFolderType.temp_folder # so we don't have endless ini files in the app folder, since we don't use them anyway (see previous line, restore_previous_geometry = False)
|
|
142
146
|
params.app_window_params.window_title = self._windows[self._get_main_window_id()]
|
|
143
147
|
params.app_window_params.hidden = True
|
|
144
148
|
params.fps_idling.fps_idle = 0
|
|
@@ -219,6 +223,8 @@ class GUI:
|
|
|
219
223
|
return
|
|
220
224
|
|
|
221
225
|
if need_begin_end:
|
|
226
|
+
if isinstance(self._window_size[w][0], np.ndarray):
|
|
227
|
+
imgui.set_next_window_size()
|
|
222
228
|
opened, self._window_visible[w] = imgui.begin(self._windows[w], self._window_visible[w], self._window_flags)
|
|
223
229
|
if not opened:
|
|
224
230
|
imgui.end()
|
|
@@ -226,15 +232,20 @@ class GUI:
|
|
|
226
232
|
|
|
227
233
|
imgui.set_cursor_pos((0,0))
|
|
228
234
|
# draw image
|
|
229
|
-
|
|
235
|
+
self._window_frame[w] += 1
|
|
236
|
+
win_sz = np.array([*imgui.get_window_size()])
|
|
237
|
+
img_sz = np.array([self._current_frame[w][0].shape[1]*self._dpi_fac, self._current_frame[w][0].shape[0]*self._dpi_fac])
|
|
238
|
+
if self._window_frame[w]>2: # it takes two frames for autosize to take effect
|
|
239
|
+
sfac = min([np.min(win_sz/img_sz), 1.]) # clamp to 1: don't scale up
|
|
240
|
+
img_sz = (img_sz * sfac).astype('int')
|
|
241
|
+
imgui.image(self._texID[w], img_sz)
|
|
230
242
|
|
|
231
243
|
# draw bottom status overlay
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
win_bottom
|
|
235
|
-
imgui.set_cursor_pos_y(win_bottom-ts.y)
|
|
244
|
+
txt_sz = imgui.calc_text_size('')
|
|
245
|
+
win_bottom = min(self._current_frame[w][0].shape[0]*self._dpi_fac, win_sz[1]+imgui.get_scroll_y())
|
|
246
|
+
imgui.set_cursor_pos_y(win_bottom-txt_sz.y)
|
|
236
247
|
imgui.push_style_color(imgui.Col_.child_bg, (0.0, 0.0, 0.0, 0.6))
|
|
237
|
-
imgui.begin_child("##status_overlay", size=(-imgui.FLT_MIN,
|
|
248
|
+
imgui.begin_child("##status_overlay", size=(-imgui.FLT_MIN,txt_sz.y))
|
|
238
249
|
if (self._current_frame[w][1]):
|
|
239
250
|
imgui.text(" %8.3f [%d]" % (self._current_frame[w][1], self._current_frame[w][2]))
|
|
240
251
|
else:
|
|
@@ -118,7 +118,7 @@ def getFrameTimestampsFromVideo(vid_file):
|
|
|
118
118
|
frameTs = (dts+empty_duration)/media_time_scale*1000
|
|
119
119
|
else:
|
|
120
120
|
# open file with opencv and get timestamps of each frame
|
|
121
|
-
vid = cv2.VideoCapture(
|
|
121
|
+
vid = cv2.VideoCapture(vid_file)
|
|
122
122
|
nframes = float(vid.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
123
123
|
frameTs = []
|
|
124
124
|
frame_idx = 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: glassesTools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: Tools for processing wearable eye tracker recordings.
|
|
5
5
|
Home-page: https://github.com/dcnieho/glassesTools
|
|
6
6
|
Author: Diederick Niehorster
|
|
@@ -38,9 +38,10 @@ License-File: LICENSE
|
|
|
38
38
|
Requires-Dist: numpy
|
|
39
39
|
Requires-Dist: pandas
|
|
40
40
|
Requires-Dist: msgpack
|
|
41
|
-
Requires-Dist:
|
|
41
|
+
Requires-Dist: imgui_bundle==1.3.0
|
|
42
|
+
Requires-Dist: opencv-python-headless==4.10.0.82
|
|
42
43
|
Requires-Dist: ffpyplayer
|
|
43
|
-
Requires-Dist:
|
|
44
|
+
Requires-Dist: eulerangles
|
|
44
45
|
Provides-Extra: gui
|
|
45
46
|
Requires-Dist: imgui_bundle==1.3.0; extra == "gui"
|
|
46
47
|
|
|
@@ -48,7 +49,7 @@ Requires-Dist: imgui_bundle==1.3.0; extra == "gui"
|
|
|
48
49
|
[](https://pypi.org/project/glassesTools/)
|
|
49
50
|
[](https://pypi.org/project/glassesTools/)
|
|
50
51
|
|
|
51
|
-
# GlassesTools v1.3.
|
|
52
|
+
# GlassesTools v1.3.4
|
|
52
53
|
Tools for processing wearable eye tracker recordings.
|
|
53
54
|
|
|
54
55
|
If you use this package or any of the code in this repository, please cite:<br>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|