remote-calibrator 0.2.2-beta.5 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/CHANGELOG.md +4 -1
  2. package/lib/RemoteCalibrator.min.js +1 -1
  3. package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
  4. package/lib/RemoteCalibrator.min.js.map +1 -1
  5. package/media/measureDistance.png +0 -0
  6. package/media/panel.png +0 -0
  7. package/media/screenSize.png +0 -0
  8. package/media/trackGaze.png +0 -0
  9. package/package.json +4 -4
  10. package/src/WebGazer4RC/.gitattributes +0 -10
  11. package/src/WebGazer4RC/LICENSE.md +0 -15
  12. package/src/WebGazer4RC/README.md +0 -142
  13. package/src/WebGazer4RC/gnu-lgpl-v3.0.md +0 -163
  14. package/src/WebGazer4RC/gplv3.md +0 -636
  15. package/src/WebGazer4RC/package-lock.json +0 -1133
  16. package/src/WebGazer4RC/package.json +0 -28
  17. package/src/WebGazer4RC/src/dom_util.mjs +0 -27
  18. package/src/WebGazer4RC/src/facemesh.mjs +0 -150
  19. package/src/WebGazer4RC/src/index.mjs +0 -1213
  20. package/src/WebGazer4RC/src/mat.mjs +0 -301
  21. package/src/WebGazer4RC/src/params.mjs +0 -29
  22. package/src/WebGazer4RC/src/pupil.mjs +0 -109
  23. package/src/WebGazer4RC/src/ridgeReg.mjs +0 -104
  24. package/src/WebGazer4RC/src/ridgeRegThreaded.mjs +0 -161
  25. package/src/WebGazer4RC/src/ridgeWeightedReg.mjs +0 -125
  26. package/src/WebGazer4RC/src/ridgeWorker.mjs +0 -135
  27. package/src/WebGazer4RC/src/util.mjs +0 -348
  28. package/src/WebGazer4RC/src/util_regression.mjs +0 -240
  29. package/src/WebGazer4RC/src/worker_scripts/mat.js +0 -306
  30. package/src/WebGazer4RC/src/worker_scripts/util.js +0 -398
  31. package/src/WebGazer4RC/test/regression_test.js +0 -182
  32. package/src/WebGazer4RC/test/run_tests_and_server.sh +0 -24
  33. package/src/WebGazer4RC/test/util_test.js +0 -60
  34. package/src/WebGazer4RC/test/webgazerExtract_test.js +0 -40
  35. package/src/WebGazer4RC/test/webgazer_test.js +0 -160
  36. package/src/WebGazer4RC/test/www_page_test.js +0 -41
@@ -1,301 +0,0 @@
1
- const mat = {};
2
- /**
3
- * Transposes an mxn array
4
- * @param {Array.<Array.<Number>>} matrix - of 'M x N' dimensionality
5
- * @return {Array.<Array.<Number>>} transposed matrix
6
- */
7
- mat.transpose = function(matrix){
8
- var m = matrix.length;
9
- var n = matrix[0].length;
10
- var transposedMatrix = new Array(n);
11
-
12
- for (var i = 0; i < m; i++){
13
- for (var j = 0; j < n; j++){
14
- if (i === 0) transposedMatrix[j] = new Array(m);
15
- transposedMatrix[j][i] = matrix[i][j];
16
- }
17
- }
18
-
19
- return transposedMatrix;
20
- };
21
-
22
- /**
23
- * Get a sub-matrix of matrix
24
- * @param {Array.<Array.<Number>>} matrix - original matrix
25
- * @param {Array.<Number>} r - Array of row indices
26
- * @param {Number} j0 - Initial column index
27
- * @param {Number} j1 - Final column index
28
- * @returns {Array} The sub-matrix matrix(r(:),j0:j1)
29
- */
30
- mat.getMatrix = function(matrix, r, j0, j1){
31
- var X = new Array(r.length),
32
- m = j1-j0+1;
33
-
34
- for (var i = 0; i < r.length; i++){
35
- X[i] = new Array(m);
36
- for (var j = j0; j <= j1; j++){
37
- X[i][j-j0] = matrix[r[i]][j];
38
- }
39
- }
40
- return X;
41
- };
42
-
43
- /**
44
- * Get a submatrix of matrix
45
- * @param {Array.<Array.<Number>>} matrix - original matrix
46
- * @param {Number} i0 - Initial row index
47
- * @param {Number} i1 - Final row index
48
- * @param {Number} j0 - Initial column index
49
- * @param {Number} j1 - Final column index
50
- * @return {Array} The sub-matrix matrix(i0:i1,j0:j1)
51
- */
52
- mat.getSubMatrix = function(matrix, i0, i1, j0, j1){
53
- var size = j1 - j0 + 1,
54
- X = new Array(i1-i0+1);
55
-
56
- for (var i = i0; i <= i1; i++){
57
- var subI = i-i0;
58
-
59
- X[subI] = new Array(size);
60
-
61
- for (var j = j0; j <= j1; j++){
62
- X[subI][j-j0] = matrix[i][j];
63
- }
64
- }
65
- return X;
66
- };
67
-
68
- /**
69
- * Linear algebraic matrix multiplication, matrix1 * matrix2
70
- * @param {Array.<Array.<Number>>} matrix1
71
- * @param {Array.<Array.<Number>>} matrix2
72
- * @return {Array.<Array.<Number>>} Matrix product, matrix1 * matrix2
73
- */
74
- mat.mult = function(matrix1, matrix2){
75
-
76
- if (matrix2.length != matrix1[0].length){
77
- console.log('Matrix inner dimensions must agree:');
78
-
79
- }
80
-
81
- var X = new Array(matrix1.length),
82
- Bcolj = new Array(matrix1[0].length);
83
-
84
- for (var j = 0; j < matrix2[0].length; j++){
85
- for (var k = 0; k < matrix1[0].length; k++){
86
- Bcolj[k] = matrix2[k][j];
87
- }
88
- for (var i = 0; i < matrix1.length; i++){
89
-
90
- if (j === 0)
91
- X[i] = new Array(matrix2[0].length);
92
-
93
- var Arowi = matrix1[i];
94
- var s = 0;
95
- for (var k = 0; k < matrix1[0].length; k++){
96
- s += Arowi[k]*Bcolj[k];
97
- }
98
- X[i][j] = s;
99
- }
100
- }
101
- return X;
102
- };
103
-
104
-
105
- /**
106
- * LUDecomposition to solve A*X = B, based on WEKA code
107
- * @param {Array.<Array.<Number>>} A - left matrix of equation to be solved
108
- * @param {Array.<Array.<Number>>} B - right matrix of equation to be solved
109
- * @return {Array.<Array.<Number>>} X so that L*U*X = B(piv,:)
110
- */
111
- mat.LUDecomposition = function(A,B){
112
- var LU = new Array(A.length);
113
-
114
- for (var i = 0; i < A.length; i++){
115
- LU[i] = new Array(A[0].length);
116
- for (var j = 0; j < A[0].length; j++){
117
- LU[i][j] = A[i][j];
118
- }
119
- }
120
-
121
- var m = A.length;
122
- var n = A[0].length;
123
- var piv = new Array(m);
124
- for (var i = 0; i < m; i++){
125
- piv[i] = i;
126
- }
127
- var pivsign = 1;
128
- var LUrowi = new Array();
129
- var LUcolj = new Array(m);
130
- // Outer loop.
131
- for (var j = 0; j < n; j++){
132
- // Make a copy of the j-th column to localize references.
133
- for (var i = 0; i < m; i++){
134
- LUcolj[i] = LU[i][j];
135
- }
136
- // Apply previous transformations.
137
- for (var i = 0; i < m; i++){
138
- LUrowi = LU[i];
139
- // Most of the time is spent in the following dot product.
140
- var kmax = Math.min(i,j);
141
- var s = 0;
142
- for (var k = 0; k < kmax; k++){
143
- s += LUrowi[k]*LUcolj[k];
144
- }
145
- LUrowi[j] = LUcolj[i] -= s;
146
- }
147
- // Find pivot and exchange if necessary.
148
- var p = j;
149
- for (var i = j+1; i < m; i++){
150
- if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])){
151
- p = i;
152
- }
153
- }
154
- if (p != j){
155
- for (var k = 0; k < n; k++){
156
- var t = LU[p][k];
157
- LU[p][k] = LU[j][k];
158
- LU[j][k] = t;
159
- }
160
- var k = piv[p];
161
- piv[p] = piv[j];
162
- piv[j] = k;
163
- pivsign = -pivsign;
164
- }
165
- // Compute multipliers.
166
- if (j < m & LU[j][j] != 0){
167
- for (var i = j+1; i < m; i++){
168
- LU[i][j] /= LU[j][j];
169
- }
170
- }
171
- }
172
- if (B.length != m){
173
- console.log('Matrix row dimensions must agree.');
174
- }
175
- for (var j = 0; j < n; j++){
176
- if (LU[j][j] === 0){
177
- console.log('Matrix is singular.')
178
- }
179
- }
180
- var nx = B[0].length;
181
- var X = self.webgazer.mat.getMatrix(B,piv,0,nx-1);
182
- // Solve L*Y = B(piv,:)
183
- for (var k = 0; k < n; k++){
184
- for (var i = k+1; i < n; i++){
185
- for (var j = 0; j < nx; j++){
186
- X[i][j] -= X[k][j]*LU[i][k];
187
- }
188
- }
189
- }
190
- // Solve U*X = Y;
191
- for (var k = n-1; k >= 0; k--){
192
- for (var j = 0; j < nx; j++){
193
- X[k][j] /= LU[k][k];
194
- }
195
- for (var i = 0; i < k; i++){
196
- for (var j = 0; j < nx; j++){
197
- X[i][j] -= X[k][j]*LU[i][k];
198
- }
199
- }
200
- }
201
- return X;
202
- };
203
-
204
- /**
205
- * Least squares solution of A*X = B, based on WEKA code
206
- * @param {Array.<Array.<Number>>} A - left side matrix to be solved
207
- * @param {Array.<Array.<Number>>} B - a matrix with as many rows as A and any number of columns.
208
- * @return {Array.<Array.<Number>>} X - that minimizes the two norms of QR*X-B.
209
- */
210
- mat.QRDecomposition = function(A, B){
211
- // Initialize.
212
- var QR = new Array(A.length);
213
-
214
- for (var i = 0; i < A.length; i++){
215
- QR[i] = new Array(A[0].length);
216
- for (var j = 0; j < A[0].length; j++){
217
- QR[i][j] = A[i][j];
218
- }
219
- }
220
- var m = A.length;
221
- var n = A[0].length;
222
- var Rdiag = new Array(n);
223
- var nrm;
224
-
225
- // Main loop.
226
- for (var k = 0; k < n; k++){
227
- // Compute 2-norm of k-th column without under/overflow.
228
- nrm = 0;
229
- for (var i = k; i < m; i++){
230
- nrm = Math.hypot(nrm,QR[i][k]);
231
- }
232
- if (nrm != 0){
233
- // Form k-th Householder vector.
234
- if (QR[k][k] < 0){
235
- nrm = -nrm;
236
- }
237
- for (var i = k; i < m; i++){
238
- QR[i][k] /= nrm;
239
- }
240
- QR[k][k] += 1;
241
-
242
- // Apply transformation to remaining columns.
243
- for (var j = k+1; j < n; j++){
244
- var s = 0;
245
- for (var i = k; i < m; i++){
246
- s += QR[i][k]*QR[i][j];
247
- }
248
- s = -s/QR[k][k];
249
- for (var i = k; i < m; i++){
250
- QR[i][j] += s*QR[i][k];
251
- }
252
- }
253
- }
254
- Rdiag[k] = -nrm;
255
- }
256
- if (B.length != m){
257
- console.log('Matrix row dimensions must agree.');
258
- }
259
- for (var j = 0; j < n; j++){
260
- if (Rdiag[j] === 0)
261
- console.log('Matrix is rank deficient');
262
- }
263
- // Copy right hand side
264
- var nx = B[0].length;
265
- var X = new Array(B.length);
266
- for(var i=0; i<B.length; i++){
267
- X[i] = new Array(B[0].length);
268
- }
269
- for (var i = 0; i < B.length; i++){
270
- for (var j = 0; j < B[0].length; j++){
271
- X[i][j] = B[i][j];
272
- }
273
- }
274
- // Compute Y = transpose(Q)*B
275
- for (var k = 0; k < n; k++){
276
- for (var j = 0; j < nx; j++){
277
- var s = 0.0;
278
- for (var i = k; i < m; i++){
279
- s += QR[i][k]*X[i][j];
280
- }
281
- s = -s/QR[k][k];
282
- for (var i = k; i < m; i++){
283
- X[i][j] += s*QR[i][k];
284
- }
285
- }
286
- }
287
- // Solve R*X = Y;
288
- for (var k = n-1; k >= 0; k--){
289
- for (var j = 0; j < nx; j++){
290
- X[k][j] /= Rdiag[k];
291
- }
292
- for (var i = 0; i < k; i++){
293
- for (var j = 0; j < nx; j++){
294
- X[i][j] -= X[k][j]*QR[i][k];
295
- }
296
- }
297
- }
298
- return mat.getSubMatrix(X,0,n-1,0,nx-1);
299
- }
300
-
301
- export default mat;
@@ -1,29 +0,0 @@
1
- const params = {
2
- moveTickSize: 50,
3
- videoContainerId: 'webgazerVideoContainer',
4
- videoElementId: 'webgazerVideoFeed',
5
- videoElementCanvasId: 'webgazerVideoCanvas',
6
- faceOverlayId: 'webgazerFaceOverlay',
7
- faceFeedbackBoxId: 'webgazerFaceFeedbackBox',
8
- gazeDotId: 'webgazerGazeDot',
9
- videoViewerWidth: 320,
10
- videoViewerHeight: 240,
11
- faceFeedbackBoxRatio: 0.66,
12
- // View options
13
- showVideo: true,
14
- mirrorVideo: true,
15
- showFaceOverlay: true,
16
- showFaceFeedbackBox: true,
17
- showGazeDot: true,
18
- camConstraints: { video: { width: { min: 320, ideal: 640, max: 1920 }, height: { min: 240, ideal: 480, max: 1080 }, facingMode: "user" } },
19
- dataTimestep: 50,
20
- showVideoPreview: true,
21
- applyKalmanFilter: true,
22
- saveDataAcrossSessions: true,
23
- // Whether or not to store accuracy eigenValues, used by the calibration example file
24
- storingPoints: false,
25
- ////
26
- videoIsOn: false,
27
- };
28
-
29
- export default params;
@@ -1,109 +0,0 @@
1
- const pupil = {};
2
-
3
- /**
4
- * Returns intensity value at x,y position of a pixels image
5
- * @param {Array} pixels - array of size width*height
6
- * @param {Number} x - input x value
7
- * @param {Number} y - input y value
8
- * @param {Number} width - width of pixels image
9
- * @returns {Number} - intensity value in [0,255]
10
- */
11
- const getValue = function (pixels, x, y, width){
12
- return pixels[y * width + x];
13
- };
14
-
15
- /**
16
- * Computes summation area table/integral image of a pixel matrix
17
- * @param {Array} pixels value of eye area
18
- * @param {Number} width - of image in 'pixels'
19
- * @param {Number} height - of image in 'pixels'
20
- * @returns {Array} - integral image
21
- */
22
- const getSumTable = function (pixels, width, height){
23
- var integralImage = new Array(width);
24
- var sumx = 0;
25
- var sumy = 0;
26
-
27
- for (var i = 0; i < width; i++){
28
- integralImage[i] = new Array(height);
29
- sumx += getValue(pixels, i, 0, width);
30
- integralImage[i][0] = sumx;
31
- }
32
-
33
- for (var i = 0; i < height; i++){
34
- sumy += getValue(pixels, 0, i, width);
35
- integralImage[0][i] = sumy;
36
- }
37
-
38
- for (var x = 1; x < width; x++){
39
- for (var y = 1; y < height; y++){
40
- integralImage[x][y] = getValue(pixels, x, y, width) + integralImage[x - 1][y] + integralImage[x][y - 1] - integralImage[x - 1][y - 1];
41
- }
42
- }
43
- return integralImage;
44
- };
45
-
46
- /**
47
- * Detects a pupil in a set of pixels
48
- * @param {Array} pixels - patch of pixels to look for pupil into
49
- * @param {Number} width - of pixel patch
50
- * @param {Number} height - of pixel patch
51
- * @return {Array} coordinate of the bottom right corner and width of the best fitted pupil
52
- */
53
- const getSinglePupil = function (pixels, width, height){
54
- var summedAreaTable = getSumTable(pixels, width, height);
55
- var bestAvgScore = 999999; //want to minimize this score
56
- var bestPoint = [0, 0]; //bottom right corner of best fitted pupil
57
- var bestHalfWidth = 0; //corresponding half width of the best fitted pupil
58
- var offset = Math.floor(width / 10.0); //padding
59
- //halfWidth could also start at 1, but this makes it faster
60
- for (var halfWidth = Math.floor(height / 10.0); halfWidth < width / 2; halfWidth++){
61
- //think of a sliding rectangular window of width halfWidth*2 that goes through the whole eye pixel matrix and does the following:
62
- //1) computes the irisArea, which is the total intensity of the iris
63
- //2) computes the scleraIrisArea, which is multiple rows of pixels including the sclera and iris.
64
- //3) computes avg, which is the intensity of the area divided by the number of pixels.
65
- //start at the bottom right of the rectangle!not top left
66
- for (var x = halfWidth; x < width - offset; x++){
67
- for (var y = halfWidth; y < height - offset; y++){
68
- //evaluate area by the formula found on wikipedia about the summed area table: I(D)+I(A)-I(B)-I(C)
69
- var irisArea = summedAreaTable[x + offset][y + offset] + summedAreaTable[x + offset - halfWidth][y + offset - halfWidth] - summedAreaTable[x + offset][y + offset - halfWidth] - summedAreaTable[x + offset - halfWidth][y + offset];
70
- var avgScore = 1.0 * irisArea / ((halfWidth + 1) * (halfWidth + 1)) + 1;
71
- //summation area table again
72
- var scleraIrisArea = ((1.0 * summedAreaTable[width - 1 - offset][y + offset] + summedAreaTable[0 + offset][y + offset - halfWidth] - summedAreaTable[0 + offset][y + offset] - summedAreaTable[width - 1 - offset][y + offset - halfWidth]) - irisArea);
73
- //minimize avgScore/scleraIrisArea. 150 is too high, might have to change since it's closer to white
74
- if ((avgScore) / scleraIrisArea < bestAvgScore && avgScore < 150){
75
- bestAvgScore = (avgScore) / scleraIrisArea;
76
- bestPoint = [x + offset, y + offset];
77
- bestHalfWidth = halfWidth;
78
- }
79
- }
80
- }
81
- }
82
- return [bestPoint, bestHalfWidth];
83
- };
84
-
85
- /**
86
- * Given an object with two eye patches it finds the location of the detected pupils
87
- * @param {Object} eyesObj - left and right detected eye patches
88
- * @return {Object} eyesObj - updated eye patches with information about pupils' locations
89
- */
90
- const getPupils = function(eyesObj) {
91
- if (!eyesObj) {
92
- return eyesObj;
93
- }
94
- if (!eyesObj.left.blink) {
95
- eyesObj.left.pupil = getSinglePupil(Array.prototype.slice.call(webgazer.util.grayscale(eyesObj.left.patch.data, eyesObj.left.width, eyesObj.left.height)), eyesObj.left.width, eyesObj.left.height);
96
- eyesObj.left.pupil[0][0] -= eyesObj.left.pupil[1];
97
- eyesObj.left.pupil[0][1] -= eyesObj.left.pupil[1];
98
- }
99
- if (!eyesObj.right.blink) {
100
- eyesObj.right.pupil = getSinglePupil(Array.prototype.slice.call(webgazer.util.grayscale(eyesObj.right.patch.data, eyesObj.right.width, eyesObj.right.height)), eyesObj.right.width, eyesObj.right.height);
101
- eyesObj.right.pupil[0][0] -= eyesObj.right.pupil[1];
102
- eyesObj.right.pupil[0][1] -= eyesObj.right.pupil[1];
103
- }
104
- return eyesObj;
105
- }
106
-
107
- pupil.getPupils = getPupils;
108
-
109
- export default pupil;
@@ -1,104 +0,0 @@
1
- import util from './util.mjs';
2
- import util_regression from './util_regression.mjs';
3
- import params from './params.mjs';
4
-
5
- const reg = {};
6
-
7
- /**
8
- * Constructor of RidgeReg object,
9
- * this object allow to perform ridge regression
10
- * @constructor
11
- */
12
- reg.RidgeReg = function() {
13
- this.init();
14
- };
15
-
16
- /**
17
- * Initialize new arrays and initialize Kalman filter.
18
- */
19
- reg.RidgeReg.prototype.init = util_regression.InitRegression
20
-
21
- /**
22
- * Add given data from eyes
23
- * @param {Object} eyes - eyes where extract data to add
24
- * @param {Object} screenPos - The current screen point
25
- * @param {Object} type - The type of performed action
26
- */
27
- reg.RidgeReg.prototype.addData = util_regression.addData
28
-
29
- /**
30
- * Try to predict coordinates from pupil data
31
- * after apply linear regression on data set
32
- * @param {Object} eyesObj - The current user eyes object
33
- * @returns {Object}
34
- */
35
- reg.RidgeReg.prototype.predict = function(eyesObj) {
36
- if (!eyesObj || this.eyeFeaturesClicks.length === 0) {
37
- return null;
38
- }
39
- var acceptTime = performance.now() - this.trailTime;
40
- var trailX = [];
41
- var trailY = [];
42
- var trailFeat = [];
43
- for (var i = 0; i < this.trailDataWindow; i++) {
44
- if (this.trailTimes.get(i) > acceptTime) {
45
- trailX.push(this.screenXTrailArray.get(i));
46
- trailY.push(this.screenYTrailArray.get(i));
47
- trailFeat.push(this.eyeFeaturesTrail.get(i));
48
- }
49
- }
50
-
51
- var screenXArray = this.screenXClicksArray.data.concat(trailX);
52
- var screenYArray = this.screenYClicksArray.data.concat(trailY);
53
- var eyeFeatures = this.eyeFeaturesClicks.data.concat(trailFeat);
54
-
55
- var coefficientsX = util_regression.ridge(screenXArray, eyeFeatures, this.ridgeParameter);
56
- var coefficientsY = util_regression.ridge(screenYArray, eyeFeatures, this.ridgeParameter);
57
-
58
- var eyeFeats = util.getEyeFeats(eyesObj);
59
- var predictedX = 0;
60
- for(var i=0; i< eyeFeats.length; i++){
61
- predictedX += eyeFeats[i] * coefficientsX[i];
62
- }
63
- var predictedY = 0;
64
- for(var i=0; i< eyeFeats.length; i++){
65
- predictedY += eyeFeats[i] * coefficientsY[i];
66
- }
67
-
68
- predictedX = Math.floor(predictedX);
69
- predictedY = Math.floor(predictedY);
70
-
71
- if (params.applyKalmanFilter) {
72
- // Update Kalman model, and get prediction
73
- var newGaze = [predictedX, predictedY]; // [20200607 xk] Should we use a 1x4 vector?
74
- newGaze = this.kalman.update(newGaze);
75
-
76
- return {
77
- x: newGaze[0],
78
- y: newGaze[1]
79
- };
80
- } else {
81
- return {
82
- x: predictedX,
83
- y: predictedY
84
- };
85
- }
86
- };
87
-
88
- reg.RidgeReg.prototype.setData = util_regression.setData;
89
-
90
- /**
91
- * Return the data
92
- * @returns {Array.<Object>|*}
93
- */
94
- reg.RidgeReg.prototype.getData = function() {
95
- return this.dataClicks.data;
96
- }
97
-
98
- /**
99
- * The RidgeReg object name
100
- * @type {string}
101
- */
102
- reg.RidgeReg.prototype.name = 'ridge';
103
-
104
- export default reg;
@@ -1,161 +0,0 @@
1
- import util from './util.mjs';
2
- import numeric from 'numeric';
3
- import util_regression from './util_regression.mjs';
4
- import params from './params.mjs';
5
-
6
- const reg = {};
7
-
8
- var ridgeParameter = Math.pow(10,-5);
9
- var dataWindow = 700;
10
- var weights = {'X':[0],'Y':[0]};
11
- var trailDataWindow = 10;
12
-
13
-
14
- /**
15
- * Constructor of RidgeRegThreaded object,
16
- * it retrieve data window, and prepare a worker,
17
- * this object allow to perform threaded ridge regression
18
- * @constructor
19
- */
20
- reg.RidgeRegThreaded = function() {
21
- this.init();
22
- };
23
-
24
- /**
25
- * Initialize new arrays and initialize Kalman filter.
26
- */
27
- reg.RidgeRegThreaded.prototype.init = function() {
28
- this.screenXClicksArray = new util.DataWindow(dataWindow);
29
- this.screenYClicksArray = new util.DataWindow(dataWindow);
30
- this.eyeFeaturesClicks = new util.DataWindow(dataWindow);
31
-
32
- this.screenXTrailArray = new util.DataWindow(trailDataWindow);
33
- this.screenYTrailArray = new util.DataWindow(trailDataWindow);
34
- this.eyeFeaturesTrail = new util.DataWindow(trailDataWindow);
35
-
36
- this.dataClicks = new util.DataWindow(dataWindow);
37
- this.dataTrail = new util.DataWindow(dataWindow);
38
-
39
- // Place the src/ridgeworker.js file into the same directory as your html file.
40
- if (!this.worker) {
41
- this.worker = new Worker('ridgeWorker.mjs'); // [20200708] TODO: Figure out how to make this inline
42
- this.worker.onerror = function(err) { console.log(err.message); };
43
- this.worker.onmessage = function(evt) {
44
- weights.X = evt.data.X;
45
- weights.Y = evt.data.Y;
46
- };
47
- console.log('initialized worker');
48
- }
49
-
50
- // Initialize Kalman filter [20200608 xk] what do we do about parameters?
51
- // [20200611 xk] unsure what to do w.r.t. dimensionality of these matrices. So far at least
52
- // by my own anecdotal observation a 4x1 x vector seems to work alright
53
- var F = [ [1, 0, 1, 0],
54
- [0, 1, 0, 1],
55
- [0, 0, 1, 0],
56
- [0, 0, 0, 1]];
57
-
58
- //Parameters Q and R may require some fine tuning
59
- var Q = [ [1/4, 0, 1/2, 0],
60
- [0, 1/4, 0, 1/2],
61
- [1/2, 0, 1, 0],
62
- [0, 1/2, 0, 1]];// * delta_t
63
- var delta_t = 1/10; // The amount of time between frames
64
- Q = numeric.mul(Q, delta_t);
65
-
66
- var H = [ [1, 0, 0, 0, 0, 0],
67
- [0, 1, 0, 0, 0, 0],
68
- [0, 0, 1, 0, 0, 0],
69
- [0, 0, 0, 1, 0, 0]];
70
- var H = [ [1, 0, 0, 0],
71
- [0, 1, 0, 0]];
72
- var pixel_error = 47; //We will need to fine tune this value [20200611 xk] I just put a random value here
73
-
74
- //This matrix represents the expected measurement error
75
- var R = numeric.mul(numeric.identity(2), pixel_error);
76
-
77
- var P_initial = numeric.mul(numeric.identity(4), 0.0001); //Initial covariance matrix
78
- var x_initial = [[500], [500], [0], [0]]; // Initial measurement matrix
79
-
80
- this.kalman = new util_regression.KalmanFilter(F, H, Q, R, P_initial, x_initial);
81
- }
82
- /**
83
- * Add given data from eyes
84
- * @param {Object} eyes - eyes where extract data to add
85
- * @param {Object} screenPos - The current screen point
86
- * @param {Object} type - The type of performed action
87
- */
88
- reg.RidgeRegThreaded.prototype.addData = function(eyes, screenPos, type) {
89
- if (!eyes) {
90
- return;
91
- }
92
- //not doing anything with blink at present
93
- // if (eyes.left.blink || eyes.right.blink) {
94
- // return;
95
- // }
96
- this.worker.postMessage({'eyes':util.getEyeFeats(eyes), 'screenPos':screenPos, 'type':type});
97
- };
98
-
99
- /**
100
- * Try to predict coordinates from pupil data
101
- * after apply linear regression on data set
102
- * @param {Object} eyesObj - The current user eyes object
103
- * @returns {Object}
104
- */
105
- reg.RidgeRegThreaded.prototype.predict = function(eyesObj) {
106
- // console.log('LOGGING..');
107
- if (!eyesObj) {
108
- return null;
109
- }
110
- var coefficientsX = weights.X;
111
- var coefficientsY = weights.Y;
112
-
113
- var eyeFeats = util.getEyeFeats(eyesObj);
114
- var predictedX = 0, predictedY = 0;
115
- for(var i=0; i< eyeFeats.length; i++){
116
- predictedX += eyeFeats[i] * coefficientsX[i];
117
- predictedY += eyeFeats[i] * coefficientsY[i];
118
- }
119
-
120
- predictedX = Math.floor(predictedX);
121
- predictedY = Math.floor(predictedY);
122
-
123
- if (params.applyKalmanFilter) {
124
- // Update Kalman model, and get prediction
125
- var newGaze = [predictedX, predictedY]; // [20200607 xk] Should we use a 1x4 vector?
126
- newGaze = this.kalman.update(newGaze);
127
-
128
- return {
129
- x: newGaze[0],
130
- y: newGaze[1]
131
- };
132
- } else {
133
- return {
134
- x: predictedX,
135
- y: predictedY
136
- };
137
- }
138
- };
139
-
140
- /**
141
- * Add given data to current data set then,
142
- * replace current data member with given data
143
- * @param {Array.<Object>} data - The data to set
144
- */
145
- reg.RidgeRegThreaded.prototype.setData = util_regression.setData
146
-
147
- /**
148
- * Return the data
149
- * @returns {Array.<Object>|*}
150
- */
151
- reg.RidgeRegThreaded.prototype.getData = function() {
152
- return this.dataClicks.data;
153
- };
154
-
155
- /**
156
- * The RidgeRegThreaded object name
157
- * @type {string}
158
- */
159
- reg.RidgeRegThreaded.prototype.name = 'ridge';
160
-
161
- export default reg;