remote-calibrator 0.3.0-rc.3 → 0.5.0-beta.10
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +35 -3
- package/README.md +34 -49
- package/homepage/example.css +4 -3
- package/homepage/example.js +42 -22
- package/homepage/index.html +19 -4
- package/i18n/fetch-languages-sheets.js +11 -1
- package/lib/RemoteCalibrator.min.js +1 -1
- package/lib/RemoteCalibrator.min.js.LICENSE.txt +19 -2
- package/lib/RemoteCalibrator.min.js.map +1 -1
- package/netlify.toml +1 -1
- package/package.json +25 -24
- package/src/WebGazer4RC/package-lock.json +198 -143
- package/src/WebGazer4RC/package.json +2 -2
- package/src/WebGazer4RC/src/index.mjs +161 -52
- package/src/WebGazer4RC/test/webgazer_test.js +1 -1
- package/src/check/checkScreenSize.js +84 -0
- package/src/components/buttons.js +21 -4
- package/src/components/input.js +82 -0
- package/src/components/keyBinder.js +5 -6
- package/src/components/language.js +5 -0
- package/src/components/mediaPermission.js +21 -0
- package/src/components/onCanvas.js +2 -2
- package/src/components/sound.js +30 -2
- package/src/components/swalOptions.js +6 -3
- package/src/components/utils.js +27 -1
- package/src/components/video.js +9 -6
- package/src/const.js +15 -0
- package/src/core.js +103 -48
- package/src/css/buttons.scss +34 -7
- package/src/css/components.scss +57 -0
- package/src/css/distance.scss +71 -1
- package/src/css/gaze.css +9 -5
- package/src/css/main.css +22 -6
- package/src/css/panel.scss +33 -3
- package/src/css/screenSize.css +6 -5
- package/src/css/swal.css +1 -1
- package/src/css/video.scss +19 -0
- package/src/distance/distance.js +194 -41
- package/src/distance/distanceCheck.js +241 -0
- package/src/distance/distanceTrack.js +165 -68
- package/src/{interpupillaryDistance.js → distance/interPupillaryDistance.js} +27 -19
- package/src/gaze/gaze.js +4 -7
- package/src/gaze/gazeAccuracy.js +9 -4
- package/src/gaze/gazeCalibration.js +14 -4
- package/src/gaze/gazeTracker.js +40 -9
- package/src/i18n.js +1 -1
- package/src/index.js +7 -2
- package/src/media/two-side-arrow.svg +1 -0
- package/src/media/two-sided-horizontal.svg +1 -0
- package/src/media/two-sided-vertical.svg +3 -0
- package/src/panel.js +130 -65
- package/src/screenSize.js +38 -5
- package/webpack.config.js +7 -4
- package/media/measureDistance.png +0 -0
- package/media/panel.png +0 -0
- package/media/screenSize.png +0 -0
- package/media/trackGaze.png +0 -0
- package/src/displaySize.js +0 -28
@@ -6,9 +6,11 @@ import {
|
|
6
6
|
constructInstructions,
|
7
7
|
blurAll,
|
8
8
|
sleep,
|
9
|
+
safeExecuteFunc,
|
9
10
|
} from '../components/utils'
|
10
11
|
import { iRepeat } from '../components/iRepeat'
|
11
12
|
import { phrases } from '../i18n'
|
13
|
+
import { spaceForLanguage } from '../components/language'
|
12
14
|
|
13
15
|
const originalStyles = {
|
14
16
|
video: false,
|
@@ -41,17 +43,26 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
41
43
|
{
|
42
44
|
fullscreen: false,
|
43
45
|
repeatTesting: 2,
|
44
|
-
sparkle:
|
46
|
+
sparkle: false,
|
45
47
|
pipWidthPx:
|
46
48
|
this._CONST.N.VIDEO_W[this.isMobile.value ? 'MOBILE' : 'DESKTOP'],
|
47
49
|
showVideo: true,
|
48
50
|
showFaceOverlay: false,
|
49
51
|
decimalPlace: 1,
|
50
|
-
framerate: 3, //
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
framerate: 3, // tracking rate
|
53
|
+
desiredDistanceCm: undefined,
|
54
|
+
desiredDistanceTolerance: 1.2,
|
55
|
+
desiredDistanceMonitor: false,
|
56
|
+
desiredDistanceMonitorCancelable: false,
|
57
|
+
nearPoint: true,
|
58
|
+
showNearPoint: false,
|
59
|
+
headline: '🙂 ' + phrases.RC_distanceTrackingTitle[this.L],
|
60
|
+
description:
|
61
|
+
phrases.RC_distanceTrackingIntroStart[this.L] +
|
62
|
+
spaceForLanguage(this.L) +
|
63
|
+
phrases.RC_viewingDistanceIntro[this.L] +
|
64
|
+
spaceForLanguage(this.L) +
|
65
|
+
phrases.RC_distanceTrackingIntroEnd[this.L],
|
55
66
|
},
|
56
67
|
options
|
57
68
|
)
|
@@ -85,9 +96,7 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
85
96
|
if (this.gazeTracker.checkInitialized('gaze', false))
|
86
97
|
this.showGazer(originalGazer)
|
87
98
|
|
88
|
-
|
89
|
-
callbackStatic(distData)
|
90
|
-
|
99
|
+
safeExecuteFunc(callbackStatic, distData)
|
91
100
|
stdDist.current = distData
|
92
101
|
}
|
93
102
|
|
@@ -112,6 +121,12 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
112
121
|
trackingOptions.nearPoint = options.nearPoint
|
113
122
|
trackingOptions.showNearPoint = options.showNearPoint
|
114
123
|
|
124
|
+
trackingOptions.desiredDistanceCm = options.desiredDistanceCm
|
125
|
+
trackingOptions.desiredDistanceTolerance = options.desiredDistanceTolerance
|
126
|
+
trackingOptions.desiredDistanceMonitor = options.desiredDistanceMonitor
|
127
|
+
trackingOptions.desiredDistanceMonitorCancelable =
|
128
|
+
options.desiredDistanceMonitorCancelable
|
129
|
+
|
115
130
|
originalStyles.video = options.showVideo
|
116
131
|
|
117
132
|
this.gazeTracker._init(
|
@@ -123,41 +138,52 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
123
138
|
'distance'
|
124
139
|
)
|
125
140
|
|
141
|
+
this._trackingSetupFinishedStatus.distance = false
|
142
|
+
|
143
|
+
const trackingConfig = {
|
144
|
+
options: options,
|
145
|
+
callbackStatic: callbackStatic,
|
146
|
+
callbackTrack: callbackTrack,
|
147
|
+
}
|
148
|
+
|
126
149
|
if (options.nearPoint) {
|
127
150
|
startTrackingPupils(
|
128
151
|
this,
|
129
152
|
() => {
|
130
|
-
this.
|
153
|
+
this._measurePD({}, _)
|
131
154
|
},
|
132
|
-
callbackTrack
|
155
|
+
callbackTrack,
|
156
|
+
trackingConfig
|
133
157
|
)
|
134
158
|
} else {
|
135
|
-
startTrackingPupils(this, _, callbackTrack)
|
159
|
+
startTrackingPupils(this, _, callbackTrack, trackingConfig)
|
136
160
|
}
|
137
161
|
}
|
138
162
|
|
139
163
|
/* -------------------------------------------------------------------------- */
|
140
164
|
|
141
|
-
const startTrackingPupils = async (
|
165
|
+
const startTrackingPupils = async (
|
166
|
+
RC,
|
167
|
+
beforeCallbackTrack,
|
168
|
+
callbackTrack,
|
169
|
+
trackingConfig
|
170
|
+
) => {
|
142
171
|
RC.gazeTracker.beginVideo({ pipWidthPx: trackingOptions.pipWidthPx }, () => {
|
143
172
|
RC._removeFloatInstructionElement()
|
144
|
-
beforeCallbackTrack
|
145
|
-
_tracking(RC, trackingOptions, callbackTrack)
|
173
|
+
safeExecuteFunc(beforeCallbackTrack)
|
174
|
+
_tracking(RC, trackingOptions, callbackTrack, trackingConfig)
|
146
175
|
})
|
147
176
|
}
|
148
177
|
|
149
178
|
const eyeDist = (a, b) => {
|
150
|
-
return Math.
|
151
|
-
Math.pow(a[0] - b[0], 2) +
|
152
|
-
Math.pow(a[1] - b[1], 2) +
|
153
|
-
Math.pow(a[2] - b[2], 2)
|
154
|
-
)
|
179
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])
|
155
180
|
}
|
156
181
|
|
157
182
|
const cyclopean = (video, a, b) => {
|
158
|
-
|
159
|
-
|
160
|
-
|
183
|
+
return [
|
184
|
+
(-a[0] - b[0] + video.videoWidth) / 2,
|
185
|
+
(-a[1] - b[1] + video.videoHeight) / 2,
|
186
|
+
]
|
161
187
|
}
|
162
188
|
|
163
189
|
/* -------------------------------------------------------------------------- */
|
@@ -167,6 +193,10 @@ const trackingOptions = {
|
|
167
193
|
framerate: 3,
|
168
194
|
nearPoint: true,
|
169
195
|
showNearPoint: false,
|
196
|
+
desiredDistanceCm: undefined,
|
197
|
+
desiredDistanceTolerance: 1.2,
|
198
|
+
desiredDistanceMonitor: false,
|
199
|
+
desiredDistanceMonitorCancelable: false,
|
170
200
|
}
|
171
201
|
|
172
202
|
const stdDist = { current: null }
|
@@ -177,22 +207,31 @@ let iRepeatOptions = { framerate: 20, break: true }
|
|
177
207
|
let nearPointDot = null
|
178
208
|
/* -------------------------------------------------------------------------- */
|
179
209
|
|
180
|
-
|
210
|
+
let readyToGetFirstData = false
|
211
|
+
let averageDist = 0
|
212
|
+
let distCount = 1
|
213
|
+
|
214
|
+
const _tracking = async (
|
215
|
+
RC,
|
216
|
+
trackingOptions,
|
217
|
+
callbackTrack,
|
218
|
+
trackingConfig
|
219
|
+
) => {
|
181
220
|
const video = document.querySelector('#webgazerVideoFeed')
|
182
221
|
|
183
222
|
const _ = async () => {
|
184
223
|
// const canvas = RC.gazeTracker.webgazer.videoCanvas
|
185
224
|
let model, faces
|
186
225
|
|
187
|
-
// Get the average of
|
188
|
-
|
189
|
-
|
226
|
+
// Get the average of 5 estimates for one measure
|
227
|
+
averageDist = 0
|
228
|
+
distCount = 1
|
190
229
|
const targetCount = 5
|
191
230
|
|
192
231
|
model = await RC.gazeTracker.webgazer.getTracker().model
|
193
232
|
|
194
233
|
// Near point
|
195
|
-
let ppi = RC.screenPpi ? RC.screenPpi.value :
|
234
|
+
let ppi = RC.screenPpi ? RC.screenPpi.value : RC._CONST.N.PPI_DONT_USE
|
196
235
|
if (!RC.screenPpi && trackingOptions.nearPoint)
|
197
236
|
console.error(
|
198
237
|
'Screen size measurement is required to get accurate near point tracking.'
|
@@ -215,29 +254,54 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
215
254
|
})
|
216
255
|
}
|
217
256
|
|
257
|
+
readyToGetFirstData = false
|
258
|
+
const {
|
259
|
+
desiredDistanceCm,
|
260
|
+
desiredDistanceTolerance,
|
261
|
+
desiredDistanceMonitor,
|
262
|
+
desiredDistanceMonitorCancelable,
|
263
|
+
} = trackingOptions
|
264
|
+
|
265
|
+
// Always enable correct on a fresh start
|
266
|
+
RC._distanceTrackNudging.distanceCorrectEnabled = true
|
267
|
+
RC._distanceTrackNudging.distanceDesired = desiredDistanceCm
|
268
|
+
RC._distanceTrackNudging.distanceAllowedRatio = desiredDistanceTolerance
|
269
|
+
|
218
270
|
viewingDistanceTrackingFunction = async () => {
|
271
|
+
//
|
272
|
+
const videoTimestamp = new Date().getTime()
|
273
|
+
//
|
219
274
|
faces = await model.estimateFaces(video)
|
220
275
|
if (faces.length) {
|
221
276
|
// There's at least one face in video
|
222
|
-
|
277
|
+
RC._tackingVideoFrameTimestamps.distance += videoTimestamp
|
223
278
|
// https://github.com/tensorflow/tfjs-models/blob/master/facemesh/mesh_map.jpg
|
279
|
+
const mesh = faces[0].scaledMesh
|
280
|
+
|
224
281
|
if (targetCount === distCount) {
|
225
282
|
averageDist += eyeDist(mesh[133], mesh[362])
|
226
|
-
averageDist /=
|
283
|
+
averageDist /= targetCount
|
284
|
+
RC._tackingVideoFrameTimestamps.distance /= targetCount
|
227
285
|
|
286
|
+
// TODO Add more samples for the first estimate
|
228
287
|
if (stdDist.current !== null) {
|
229
288
|
if (!stdFactor) {
|
230
289
|
// ! First time estimate
|
231
290
|
// Face_Known_Px * Distance_Known_Cm = Face_Now_Px * Distance_x_Cm
|
232
291
|
// Get the factor to be used for future predictions
|
233
292
|
stdFactor = averageDist * stdDist.current.value
|
234
|
-
// FINISH
|
293
|
+
// ! FINISH
|
235
294
|
RC._removeBackground()
|
295
|
+
RC._trackingSetupFinishedStatus.distance = true
|
296
|
+
readyToGetFirstData = true
|
236
297
|
}
|
237
298
|
|
238
299
|
/* -------------------------------------------------------------------------- */
|
239
300
|
|
240
301
|
const timestamp = new Date()
|
302
|
+
const latency = Math.round(
|
303
|
+
timestamp.getTime() - RC._tackingVideoFrameTimestamps.distance
|
304
|
+
)
|
241
305
|
|
242
306
|
const data = (RC.newViewingDistanceData = {
|
243
307
|
value: toFixedNumber(
|
@@ -245,9 +309,20 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
245
309
|
trackingOptions.decimalPlace
|
246
310
|
),
|
247
311
|
timestamp: timestamp,
|
248
|
-
method:
|
312
|
+
method: RC._CONST.VIEW_METHOD.F,
|
313
|
+
latencyMs: latency,
|
249
314
|
})
|
250
315
|
|
316
|
+
if (readyToGetFirstData || desiredDistanceMonitor) {
|
317
|
+
// ! Check distance
|
318
|
+
if (desiredDistanceCm)
|
319
|
+
RC.checkDistance(
|
320
|
+
desiredDistanceMonitorCancelable,
|
321
|
+
trackingConfig
|
322
|
+
)
|
323
|
+
readyToGetFirstData = false
|
324
|
+
}
|
325
|
+
|
251
326
|
/* -------------------------------------------------------------------------- */
|
252
327
|
|
253
328
|
// Near point
|
@@ -260,7 +335,8 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
260
335
|
mesh,
|
261
336
|
averageDist,
|
262
337
|
timestamp,
|
263
|
-
ppi
|
338
|
+
ppi,
|
339
|
+
latency
|
264
340
|
)
|
265
341
|
}
|
266
342
|
|
@@ -272,15 +348,18 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
272
348
|
value: {
|
273
349
|
viewingDistanceCm: data.value,
|
274
350
|
nearPointCm: nPData ? nPData.value : [null, null],
|
351
|
+
latencyMs: latency,
|
275
352
|
},
|
276
353
|
timestamp: timestamp,
|
277
|
-
method:
|
354
|
+
method: RC._CONST.VIEW_METHOD.F,
|
278
355
|
})
|
279
356
|
}
|
280
357
|
}
|
281
358
|
|
282
359
|
averageDist = 0
|
283
360
|
distCount = 1
|
361
|
+
|
362
|
+
RC._tackingVideoFrameTimestamps.distance = 0
|
284
363
|
} else {
|
285
364
|
averageDist += eyeDist(mesh[133], mesh[362])
|
286
365
|
++distCount
|
@@ -289,11 +368,11 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
289
368
|
}
|
290
369
|
|
291
370
|
iRepeatOptions.break = false
|
292
|
-
iRepeatOptions.framerate = targetCount * trackingOptions.framerate // Default
|
371
|
+
iRepeatOptions.framerate = targetCount * trackingOptions.framerate // Default 5 * 3
|
293
372
|
iRepeat(viewingDistanceTrackingFunction, iRepeatOptions)
|
294
373
|
}
|
295
374
|
|
296
|
-
sleep(
|
375
|
+
sleep(1000).then(_)
|
297
376
|
}
|
298
377
|
|
299
378
|
const _getNearPoint = (
|
@@ -303,45 +382,42 @@ const _getNearPoint = (
|
|
303
382
|
mesh,
|
304
383
|
averageDist,
|
305
384
|
timestamp,
|
306
|
-
ppi
|
385
|
+
ppi,
|
386
|
+
latency
|
307
387
|
) => {
|
308
|
-
let
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
const videoFactor = video.videoHeight / video.clientHeight
|
315
|
-
offsetToVideoMid.forEach((e, i) => {
|
316
|
-
// Average interpupillary distance - 6.4cm
|
317
|
-
offsetToVideoMid[i] =
|
318
|
-
((RC.PDCm ? RC.PDCm.value : 6.4) * e) /
|
319
|
-
(averageDist * (videoFactor / 2)) /* Should this be videoFactor? */
|
388
|
+
let offsetToVideoCenter = cyclopean(video, mesh[133], mesh[362])
|
389
|
+
offsetToVideoCenter.forEach((offset, i) => {
|
390
|
+
// Average inter-pupillary distance - 6.4cm
|
391
|
+
offsetToVideoCenter[i] =
|
392
|
+
((RC.PDCm ? RC.PDCm.value : RC._CONST.N.PD_DONT_USE) * offset) /
|
393
|
+
averageDist
|
320
394
|
})
|
321
395
|
|
322
396
|
let nPData = (RC.newNearPointData = {
|
323
397
|
value: {
|
324
|
-
x: toFixedNumber(
|
398
|
+
x: toFixedNumber(offsetToVideoCenter[0], trackingOptions.decimalPlace),
|
325
399
|
y: toFixedNumber(
|
326
|
-
|
400
|
+
offsetToVideoCenter[1] + ((screen.height / 2) * 2.54) / ppi, // Commonly the webcam is 0.5cm above the screen
|
327
401
|
trackingOptions.decimalPlace
|
328
402
|
),
|
403
|
+
latencyMs: latency,
|
329
404
|
},
|
330
405
|
timestamp: timestamp,
|
331
406
|
})
|
332
407
|
|
333
408
|
// SHOW
|
409
|
+
const dotR = 5
|
334
410
|
if (trackingOptions.showNearPoint) {
|
335
411
|
let offsetX = (nPData.value.x * ppi) / 2.54
|
336
412
|
let offsetY = (nPData.value.y * ppi) / 2.54
|
337
413
|
Object.assign(nearPointDot.style, {
|
338
|
-
left: `${screen.width / 2 - window.screenLeft
|
414
|
+
left: `${screen.width / 2 - window.screenLeft + offsetX - dotR}px`,
|
339
415
|
top: `${
|
340
416
|
screen.height / 2 -
|
341
417
|
window.screenTop -
|
342
|
-
|
343
|
-
|
344
|
-
|
418
|
+
(window.outerHeight - window.innerHeight) -
|
419
|
+
offsetY -
|
420
|
+
dotR
|
345
421
|
}px`,
|
346
422
|
})
|
347
423
|
}
|
@@ -353,6 +429,7 @@ RemoteCalibrator.prototype.pauseDistance = function () {
|
|
353
429
|
if (this.gazeTracker.checkInitialized('distance', true)) {
|
354
430
|
iRepeatOptions.break = true
|
355
431
|
if (nearPointDot) nearPointDot.style.display = 'none'
|
432
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
356
433
|
return this
|
357
434
|
}
|
358
435
|
return null
|
@@ -362,6 +439,11 @@ RemoteCalibrator.prototype.resumeDistance = function () {
|
|
362
439
|
if (this.gazeTracker.checkInitialized('distance', true)) {
|
363
440
|
iRepeatOptions.break = false
|
364
441
|
if (nearPointDot) nearPointDot.style.display = 'block'
|
442
|
+
|
443
|
+
averageDist = 0
|
444
|
+
distCount = 1
|
445
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
446
|
+
|
365
447
|
iRepeat(viewingDistanceTrackingFunction, iRepeatOptions)
|
366
448
|
return this
|
367
449
|
}
|
@@ -379,21 +461,30 @@ RemoteCalibrator.prototype.endDistance = function (endAll = false, _r = true) {
|
|
379
461
|
trackingOptions.nearPoint = true
|
380
462
|
trackingOptions.showNearPoint = false
|
381
463
|
|
464
|
+
trackingOptions.desiredDistanceCm = undefined
|
465
|
+
trackingOptions.desiredDistanceTolerance = 1.2
|
466
|
+
trackingOptions.desiredDistanceMonitor = false
|
467
|
+
trackingOptions.desiredDistanceMonitorCancelable = false
|
468
|
+
|
382
469
|
stdDist.current = null
|
383
470
|
stdFactor = null
|
384
471
|
viewingDistanceTrackingFunction = null
|
385
472
|
|
473
|
+
readyToGetFirstData = false
|
474
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
475
|
+
|
386
476
|
// Near point
|
387
477
|
if (nearPointDot) {
|
388
478
|
document.body.removeChild(nearPointDot)
|
389
479
|
nearPointDot = null
|
390
480
|
}
|
391
481
|
|
392
|
-
|
482
|
+
// Nudger
|
483
|
+
this.endNudger()
|
393
484
|
|
485
|
+
if (_r) this.gazeTracker.end('distance', endAll)
|
394
486
|
return this
|
395
487
|
}
|
396
|
-
|
397
488
|
return null
|
398
489
|
}
|
399
490
|
|
@@ -409,6 +500,7 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
409
500
|
|
410
501
|
let v = document.querySelector('#webgazerVideoFeed')
|
411
502
|
let m = await this.gazeTracker.webgazer.getTracker().model
|
503
|
+
const videoTimestamp = new Date().getTime()
|
412
504
|
let f = await m.estimateFaces(v)
|
413
505
|
|
414
506
|
if (f.length) {
|
@@ -416,11 +508,15 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
416
508
|
const dist = eyeDist(mesh[133], mesh[362])
|
417
509
|
|
418
510
|
let timestamp = new Date()
|
511
|
+
//
|
512
|
+
const latency = timestamp.getTime() - videoTimestamp
|
513
|
+
//
|
419
514
|
|
420
515
|
const data = (this.newViewingDistanceData = {
|
421
516
|
value: toFixedNumber(stdFactor / dist, trackingOptions.decimalPlace),
|
422
517
|
timestamp: timestamp,
|
423
|
-
method:
|
518
|
+
method: this._CONST.VIEW_METHOD.F,
|
519
|
+
latencyMs: latency,
|
424
520
|
})
|
425
521
|
|
426
522
|
let nPData
|
@@ -432,19 +528,20 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
432
528
|
mesh,
|
433
529
|
dist,
|
434
530
|
timestamp,
|
435
|
-
this.screenPpi ? this.screenPpi.value :
|
531
|
+
this.screenPpi ? this.screenPpi.value : this._CONST.N.PPI_DONT_USE,
|
532
|
+
latency
|
436
533
|
)
|
437
534
|
}
|
438
535
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
536
|
+
safeExecuteFunc(c, {
|
537
|
+
value: {
|
538
|
+
viewingDistanceCm: data.value,
|
539
|
+
nearPointCm: nPData ? nPData.value : null,
|
540
|
+
latencyMs: latency,
|
541
|
+
},
|
542
|
+
timestamp: timestamp,
|
543
|
+
method: this._CONST.VIEW_METHOD.F,
|
544
|
+
})
|
448
545
|
return data
|
449
546
|
}
|
450
547
|
|
@@ -1,19 +1,20 @@
|
|
1
1
|
import Swal from 'sweetalert2'
|
2
2
|
|
3
|
-
import RemoteCalibrator from '
|
3
|
+
import RemoteCalibrator from '../core'
|
4
4
|
|
5
5
|
import {
|
6
6
|
blurAll,
|
7
7
|
constructInstructions,
|
8
|
+
safeExecuteFunc,
|
8
9
|
toFixedNumber,
|
9
|
-
} from '
|
10
|
-
import { swalInfoOptions } from '
|
11
|
-
import Arrow from '
|
12
|
-
import PD from '
|
13
|
-
import { bindKeys, unbindKeys } from '
|
14
|
-
import { addButtons } from '
|
15
|
-
import { setDefaultVideoPosition } from '
|
16
|
-
import { phrases } from '
|
10
|
+
} from '../components/utils'
|
11
|
+
import { swalInfoOptions } from '../components/swalOptions'
|
12
|
+
import Arrow from '../media/arrow.svg'
|
13
|
+
import PD from '../media/pd.png?width=480&height=240'
|
14
|
+
import { bindKeys, unbindKeys } from '../components/keyBinder'
|
15
|
+
import { addButtons } from '../components/buttons'
|
16
|
+
import { setDefaultVideoPosition } from '../components/video'
|
17
|
+
import { phrases } from '../i18n'
|
17
18
|
|
18
19
|
// let selfVideo = false // No WebGazer video available and an extra video element needs to be created
|
19
20
|
|
@@ -29,7 +30,7 @@ const originalStyles = {
|
|
29
30
|
const videoWidthFactor = 0.9
|
30
31
|
const videoHeightFactor = 0.3
|
31
32
|
|
32
|
-
RemoteCalibrator.prototype.
|
33
|
+
RemoteCalibrator.prototype._measurePD = function (options = {}, callback) {
|
33
34
|
////
|
34
35
|
if (!this.checkInitialized()) return
|
35
36
|
blurAll()
|
@@ -39,7 +40,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
39
40
|
{
|
40
41
|
fullscreen: false,
|
41
42
|
headline: '👁️ ' + phrases.RC_nearPointTitle[this.L],
|
42
|
-
description: phrases.
|
43
|
+
description: phrases.RC_nearPointIntro[this.L],
|
43
44
|
shortDescription: phrases.RC_nearPointIntro[this.L],
|
44
45
|
},
|
45
46
|
options
|
@@ -50,9 +51,11 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
50
51
|
this._replaceBackground()
|
51
52
|
|
52
53
|
this._replaceBackground(
|
53
|
-
constructInstructions(options.headline, options.shortDescription)
|
54
|
+
constructInstructions(options.headline, options.shortDescription, true)
|
54
55
|
)
|
55
|
-
const screenPpi = this.screenPpi
|
56
|
+
const screenPpi = this.screenPpi
|
57
|
+
? this.screenPpi.value
|
58
|
+
: this._CONST.N.PPI_DONT_USE
|
56
59
|
|
57
60
|
let [videoWidth, videoHeight] = setupVideo(this)
|
58
61
|
let [ruler, rulerListener] = setupRuler(
|
@@ -64,7 +67,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
64
67
|
|
65
68
|
const RC = this
|
66
69
|
|
67
|
-
const breakFunction = () => {
|
70
|
+
const breakFunction = (toBreak = true) => {
|
68
71
|
ruler.removeEventListener('mousedown', rulerListener)
|
69
72
|
this._removeBackground()
|
70
73
|
|
@@ -77,7 +80,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
77
80
|
height: originalStyles.videoHeight,
|
78
81
|
width: originalStyles.videoWidth,
|
79
82
|
opacity: originalStyles.opacity,
|
80
|
-
borderRadius: '
|
83
|
+
borderRadius: '5px',
|
81
84
|
})
|
82
85
|
setDefaultVideoPosition(
|
83
86
|
RC,
|
@@ -99,6 +102,11 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
99
102
|
originalStyles.gaze = false
|
100
103
|
originalStyles.faceOverlay = false
|
101
104
|
|
105
|
+
if (!RC._trackingSetupFinishedStatus.distance && toBreak) {
|
106
|
+
RC._trackingSetupFinishedStatus.distance = true
|
107
|
+
RC.endDistance()
|
108
|
+
}
|
109
|
+
|
102
110
|
unbindKeys(bindKeysFunction)
|
103
111
|
}
|
104
112
|
|
@@ -110,14 +118,14 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
110
118
|
}
|
111
119
|
this.newPDData = newPDData
|
112
120
|
|
113
|
-
breakFunction()
|
114
|
-
callback
|
121
|
+
breakFunction(false)
|
122
|
+
safeExecuteFunc(callback, newPDData)
|
115
123
|
}
|
116
124
|
}
|
117
125
|
|
118
|
-
// if (callback && typeof callback === 'function') callback()
|
119
126
|
const bindKeysFunction = bindKeys({
|
120
127
|
Escape: breakFunction,
|
128
|
+
Enter: finishFunction,
|
121
129
|
' ': finishFunction,
|
122
130
|
})
|
123
131
|
addButtons(
|
@@ -133,7 +141,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
133
141
|
// TODO To be removed
|
134
142
|
setTimeout(() => {
|
135
143
|
Swal.fire({
|
136
|
-
...swalInfoOptions(this),
|
144
|
+
...swalInfoOptions(this, { showIcon: false }),
|
137
145
|
icon: undefined,
|
138
146
|
imageUrl: PD,
|
139
147
|
imageWidth: 480,
|
package/src/gaze/gaze.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import RemoteCalibrator from '../core'
|
2
2
|
|
3
|
-
import { blurAll } from '../components/utils'
|
3
|
+
import { blurAll, safeExecuteFunc } from '../components/utils'
|
4
4
|
import { gazeCalibrationPrepare } from './gazeCalibration'
|
5
5
|
import { phrases } from '../i18n'
|
6
6
|
|
@@ -97,17 +97,14 @@ RemoteCalibrator.prototype.trackGaze = function (
|
|
97
97
|
description: options.description,
|
98
98
|
}
|
99
99
|
this.gazeTracker.begin(gazeTrackerBeginOptions, () => {
|
100
|
+
this._trackingSetupFinishedStatus.gaze = false
|
100
101
|
this.calibrateGaze(calibrateGazeOptions, onCalibrationEnded)
|
101
102
|
})
|
102
103
|
|
103
104
|
// Calibration
|
104
105
|
|
105
|
-
const onCalibrationEnded =
|
106
|
-
|
107
|
-
callbackOnCalibrationEnd &&
|
108
|
-
typeof callbackOnCalibrationEnd === 'function'
|
109
|
-
)
|
110
|
-
callbackOnCalibrationEnd()
|
106
|
+
const onCalibrationEnded = data => {
|
107
|
+
safeExecuteFunc(callbackOnCalibrationEnd, data)
|
111
108
|
|
112
109
|
// ! greedyLearner
|
113
110
|
if (!this.gazeTracker.webgazer.params.greedyLearner) {
|
package/src/gaze/gazeAccuracy.js
CHANGED
@@ -3,7 +3,12 @@ import Swal from 'sweetalert2/dist/sweetalert2.js'
|
|
3
3
|
import RemoteCalibrator from '../core'
|
4
4
|
|
5
5
|
import { _cross } from '../components/onCanvas'
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
blurAll,
|
8
|
+
safeExecuteFunc,
|
9
|
+
sleep,
|
10
|
+
toFixedNumber,
|
11
|
+
} from '../components/utils'
|
7
12
|
import { swalInfoOptions } from '../components/swalOptions'
|
8
13
|
|
9
14
|
let inGetAccuracy = false
|
@@ -57,7 +62,7 @@ RemoteCalibrator.prototype.getGazeAccuracy = function (
|
|
57
62
|
_resizeCanvas()
|
58
63
|
|
59
64
|
Swal.fire({
|
60
|
-
...swalInfoOptions(this),
|
65
|
+
...swalInfoOptions(this, { showIcon: true }),
|
61
66
|
// title: text.getGazeAccuracy.headline,
|
62
67
|
html: `We will measure your gaze accuracy. Please do not move the mouse and look at the fixation at the middle of the screen for the next 5 seconds.`,
|
63
68
|
}).then(() => {
|
@@ -88,8 +93,8 @@ RemoteCalibrator.prototype.getGazeAccuracy = function (
|
|
88
93
|
|
89
94
|
if (averageDegree < options.thresholdDeg)
|
90
95
|
// Success
|
91
|
-
callbackSuccess
|
92
|
-
else callbackFail
|
96
|
+
safeExecuteFunc(callbackSuccess)
|
97
|
+
else safeExecuteFunc(callbackFail)
|
93
98
|
|
94
99
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
95
100
|
resizeObserver.unobserve(this.background)
|