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,240 +0,0 @@
1
- import util from './util.mjs';
2
- import numeric from 'numeric';
3
- import mat from './mat.mjs';
4
- import params from './params.mjs';
5
-
6
- const util_regression = {};
7
-
8
-
9
- /**
10
- * Initialize new arrays and initialize Kalman filter for regressions.
11
- */
12
- util_regression.InitRegression = function() {
13
- var dataWindow = 700;
14
- var trailDataWindow = 10;
15
- this.ridgeParameter = Math.pow(10,-5);
16
- this.errorXArray = new util.DataWindow(dataWindow);
17
- this.errorYArray = new util.DataWindow(dataWindow);
18
-
19
-
20
- this.screenXClicksArray = new util.DataWindow(dataWindow);
21
- this.screenYClicksArray = new util.DataWindow(dataWindow);
22
- this.eyeFeaturesClicks = new util.DataWindow(dataWindow);
23
-
24
- //sets to one second worth of cursor trail
25
- this.trailTime = 1000;
26
- this.trailDataWindow = this.trailTime / params.moveTickSize;
27
- this.screenXTrailArray = new util.DataWindow(trailDataWindow);
28
- this.screenYTrailArray = new util.DataWindow(trailDataWindow);
29
- this.eyeFeaturesTrail = new util.DataWindow(trailDataWindow);
30
- this.trailTimes = new util.DataWindow(trailDataWindow);
31
-
32
- this.dataClicks = new util.DataWindow(dataWindow);
33
- this.dataTrail = new util.DataWindow(trailDataWindow);
34
-
35
- // Initialize Kalman filter [20200608 xk] what do we do about parameters?
36
- // [20200611 xk] unsure what to do w.r.t. dimensionality of these matrices. So far at least
37
- // by my own anecdotal observation a 4x1 x vector seems to work alright
38
- var F = [ [1, 0, 1, 0],
39
- [0, 1, 0, 1],
40
- [0, 0, 1, 0],
41
- [0, 0, 0, 1]];
42
-
43
- //Parameters Q and R may require some fine tuning
44
- var Q = [ [1/4, 0, 1/2, 0],
45
- [0, 1/4, 0, 1/2],
46
- [1/2, 0, 1, 0],
47
- [0, 1/2, 0, 1]];// * delta_t
48
- var delta_t = 1/10; // The amount of time between frames
49
- Q = numeric.mul(Q, delta_t);
50
-
51
- var H = [ [1, 0, 0, 0, 0, 0],
52
- [0, 1, 0, 0, 0, 0],
53
- [0, 0, 1, 0, 0, 0],
54
- [0, 0, 0, 1, 0, 0]];
55
- var H = [ [1, 0, 0, 0],
56
- [0, 1, 0, 0]];
57
- var pixel_error = 47; //We will need to fine tune this value [20200611 xk] I just put a random value here
58
-
59
- //This matrix represents the expected measurement error
60
- var R = numeric.mul(numeric.identity(2), pixel_error);
61
-
62
- var P_initial = numeric.mul(numeric.identity(4), 0.0001); //Initial covariance matrix
63
- var x_initial = [[500], [500], [0], [0]]; // Initial measurement matrix
64
-
65
- this.kalman = new util_regression.KalmanFilter(F, H, Q, R, P_initial, x_initial);
66
- }
67
-
68
- /**
69
- * Kalman Filter constructor
70
- * Kalman filters work by reducing the amount of noise in a models.
71
- * https://blog.cordiner.net/2011/05/03/object-tracking-using-a-kalman-filter-matlab/
72
- *
73
- * @param {Array.<Array.<Number>>} F - transition matrix
74
- * @param {Array.<Array.<Number>>} Q - process noise matrix
75
- * @param {Array.<Array.<Number>>} H - maps between measurement vector and noise matrix
76
- * @param {Array.<Array.<Number>>} R - defines measurement error of the device
77
- * @param {Array} P_initial - the initial state
78
- * @param {Array} X_initial - the initial state of the device
79
- */
80
- util_regression.KalmanFilter = function(F, H, Q, R, P_initial, X_initial) {
81
- this.F = F; // State transition matrix
82
- this.Q = Q; // Process noise matrix
83
- this.H = H; // Transformation matrix
84
- this.R = R; // Measurement Noise
85
- this.P = P_initial; //Initial covariance matrix
86
- this.X = X_initial; //Initial guess of measurement
87
- };
88
-
89
- /**
90
- * Get Kalman next filtered value and update the internal state
91
- * @param {Array} z - the new measurement
92
- * @return {Array}
93
- */
94
- util_regression.KalmanFilter.prototype.update = function(z) {
95
- // Here, we define all the different matrix operations we will need
96
- var add = numeric.add, sub = numeric.sub, inv = numeric.inv, identity = numeric.identity;
97
- var mult = mat.mult, transpose = mat.transpose;
98
- //TODO cache variables like the transpose of H
99
-
100
- // prediction: X = F * X | P = F * P * F' + Q
101
- var X_p = mult(this.F, this.X); //Update state vector
102
- var P_p = add(mult(mult(this.F,this.P), transpose(this.F)), this.Q); //Predicted covaraince
103
-
104
- //Calculate the update values
105
- var y = sub(z, mult(this.H, X_p)); // This is the measurement error (between what we expect and the actual value)
106
- var S = add(mult(mult(this.H, P_p), transpose(this.H)), this.R); //This is the residual covariance (the error in the covariance)
107
-
108
- // kalman multiplier: K = P * H' * (H * P * H' + R)^-1
109
- var K = mult(P_p, mult(transpose(this.H), inv(S))); //This is the Optimal Kalman Gain
110
-
111
- //We need to change Y into it's column vector form
112
- for(var i = 0; i < y.length; i++){
113
- y[i] = [y[i]];
114
- }
115
-
116
- //Now we correct the internal values of the model
117
- // correction: X = X + K * (m - H * X) | P = (I - K * H) * P
118
- this.X = add(X_p, mult(K, y));
119
- this.P = mult(sub(identity(K.length), mult(K,this.H)), P_p);
120
- return transpose(mult(this.H, this.X))[0]; //Transforms the predicted state back into it's measurement form
121
- };
122
-
123
- /**
124
- * Performs ridge regression, according to the Weka code.
125
- * @param {Array} y - corresponds to screen coordinates (either x or y) for each of n click events
126
- * @param {Array.<Array.<Number>>} X - corresponds to gray pixel features (120 pixels for both eyes) for each of n clicks
127
- * @param {Array} k - ridge parameter
128
- * @return{Array} regression coefficients
129
- */
130
- util_regression.ridge = function(y, X, k){
131
- var nc = X[0].length;
132
- var m_Coefficients = new Array(nc);
133
- var xt = mat.transpose(X);
134
- var solution = new Array();
135
- var success = true;
136
- do{
137
- var ss = mat.mult(xt,X);
138
- // Set ridge regression adjustment
139
- for (var i = 0; i < nc; i++) {
140
- ss[i][i] = ss[i][i] + k;
141
- }
142
-
143
- // Carry out the regression
144
- var bb = mat.mult(xt,y);
145
- for(var i = 0; i < nc; i++) {
146
- m_Coefficients[i] = bb[i][0];
147
- }
148
- try{
149
- var n = (m_Coefficients.length !== 0 ? m_Coefficients.length/m_Coefficients.length: 0);
150
- if (m_Coefficients.length*n !== m_Coefficients.length){
151
- console.log('Array length must be a multiple of m')
152
- }
153
- solution = (ss.length === ss[0].length ? (numeric.LUsolve(numeric.LU(ss,true),bb)) : (webgazer.mat.QRDecomposition(ss,bb)));
154
-
155
- for (var i = 0; i < nc; i++){
156
- m_Coefficients[i] = solution[i];
157
- }
158
- success = true;
159
- }
160
- catch (ex){
161
- k *= 10;
162
- console.log(ex);
163
- success = false;
164
- }
165
- } while (!success);
166
- return m_Coefficients;
167
- }
168
-
169
- /**
170
- * Add given data to current data set then,
171
- * replace current data member with given data
172
- * @param {Array.<Object>} data - The data to set
173
- */
174
- util_regression.setData = function(data) {
175
- for (var i = 0; i < data.length; i++) {
176
- // Clone data array
177
- var leftData = new Uint8ClampedArray(data[i].eyes.left.patch.data);
178
- var rightData = new Uint8ClampedArray(data[i].eyes.right.patch.data);
179
- // Duplicate ImageData object
180
- data[i].eyes.left.patch = new ImageData(leftData, data[i].eyes.left.width, data[i].eyes.left.height);
181
- data[i].eyes.right.patch = new ImageData(rightData, data[i].eyes.right.width, data[i].eyes.right.height);
182
-
183
- // Add those data objects to model
184
- this.addData(data[i].eyes, data[i].screenPos, data[i].type);
185
- }
186
- };
187
-
188
-
189
- //not used ?!
190
- //TODO: still usefull ???
191
- /**
192
- *
193
- * @returns {Number}
194
- */
195
- util_regression.getCurrentFixationIndex = function() {
196
- var index = 0;
197
- var recentX = this.screenXTrailArray.get(0);
198
- var recentY = this.screenYTrailArray.get(0);
199
- for (var i = this.screenXTrailArray.length - 1; i >= 0; i--) {
200
- var currX = this.screenXTrailArray.get(i);
201
- var currY = this.screenYTrailArray.get(i);
202
- var euclideanDistance = Math.sqrt(Math.pow((currX-recentX),2)+Math.pow((currY-recentY),2));
203
- if (euclideanDistance > 72){
204
- return i+1;
205
- }
206
- }
207
- return i;
208
- }
209
-
210
- util_regression.addData = function(eyes, screenPos, type) {
211
- if (!eyes) {
212
- return;
213
- }
214
- //not doing anything with blink at present
215
- // if (eyes.left.blink || eyes.right.blink) {
216
- // return;
217
- // }
218
- if (type === 'click') {
219
- this.screenXClicksArray.push([screenPos[0]]);
220
- this.screenYClicksArray.push([screenPos[1]]);
221
- this.eyeFeaturesClicks.push(util.getEyeFeats(eyes));
222
- this.dataClicks.push({'eyes':eyes, 'screenPos':screenPos, 'type':type});
223
- } else if (type === 'move') {
224
- this.screenXTrailArray.push([screenPos[0]]);
225
- this.screenYTrailArray.push([screenPos[1]]);
226
-
227
- this.eyeFeaturesTrail.push(util.getEyeFeats(eyes));
228
- this.trailTimes.push(performance.now());
229
- this.dataTrail.push({'eyes':eyes, 'screenPos':screenPos, 'type':type});
230
- }
231
-
232
- // [20180730 JT] Why do we do this? It doesn't return anything...
233
- // But as JS is pass by reference, it still affects it.
234
- //
235
- // Causes problems for when we want to call 'addData' twice in a row on the same object, but perhaps with different screenPos or types (think multiple interactions within one video frame)
236
- //eyes.left.patch = Array.from(eyes.left.patch.data);
237
- //eyes.right.patch = Array.from(eyes.right.patch.data);
238
- };
239
-
240
- export default util_regression;
@@ -1,306 +0,0 @@
1
- (function() {
2
- 'use strict';
3
-
4
- self.webgazer = self.webgazer || {};
5
- self.webgazer.mat = self.webgazer.mat || {};
6
-
7
- /**
8
- * Transposes an mxn array
9
- * @param {Array.<Array.<Number>>} matrix - of 'M x N' dimensionality
10
- * @return {Array.<Array.<Number>>} transposed matrix
11
- */
12
- self.webgazer.mat.transpose = function(matrix){
13
- var m = matrix.length;
14
- var n = matrix[0].length;
15
- var transposedMatrix = new Array(n);
16
-
17
- for (var i = 0; i < m; i++){
18
- for (var j = 0; j < n; j++){
19
- if (i === 0) transposedMatrix[j] = new Array(m);
20
- transposedMatrix[j][i] = matrix[i][j];
21
- }
22
- }
23
-
24
- return transposedMatrix;
25
- };
26
-
27
- /**
28
- * Get a sub-matrix of matrix
29
- * @param {Array.<Array.<Number>>} matrix - original matrix
30
- * @param {Array.<Number>} r - Array of row indices
31
- * @param {Number} j0 - Initial column index
32
- * @param {Number} j1 - Final column index
33
- * @returns {Array} The sub-matrix matrix(r(:),j0:j1)
34
- */
35
- self.webgazer.mat.getMatrix = function(matrix, r, j0, j1){
36
- var X = new Array(r.length),
37
- m = j1-j0+1;
38
-
39
- for (var i = 0; i < r.length; i++){
40
- X[i] = new Array(m);
41
- for (var j = j0; j <= j1; j++){
42
- X[i][j-j0] = matrix[r[i]][j];
43
- }
44
- }
45
- return X;
46
- };
47
-
48
- /**
49
- * Get a submatrix of matrix
50
- * @param {Array.<Array.<Number>>} matrix - original matrix
51
- * @param {Number} i0 - Initial row index
52
- * @param {Number} i1 - Final row index
53
- * @param {Number} j0 - Initial column index
54
- * @param {Number} j1 - Final column index
55
- * @return {Array} The sub-matrix matrix(i0:i1,j0:j1)
56
- */
57
- self.webgazer.mat.getSubMatrix = function(matrix, i0, i1, j0, j1){
58
- var size = j1 - j0 + 1,
59
- X = new Array(i1-i0+1);
60
-
61
- for (var i = i0; i <= i1; i++){
62
- var subI = i-i0;
63
-
64
- X[subI] = new Array(size);
65
-
66
- for (var j = j0; j <= j1; j++){
67
- X[subI][j-j0] = matrix[i][j];
68
- }
69
- }
70
- return X;
71
- };
72
-
73
- /**
74
- * Linear algebraic matrix multiplication, matrix1 * matrix2
75
- * @param {Array.<Array.<Number>>} matrix1
76
- * @param {Array.<Array.<Number>>} matrix2
77
- * @return {Array.<Array.<Number>>} Matrix product, matrix1 * matrix2
78
- */
79
- self.webgazer.mat.mult = function(matrix1, matrix2){
80
-
81
- if (matrix2.length != matrix1[0].length){
82
- console.log('Matrix inner dimensions must agree:');
83
-
84
- }
85
-
86
- var X = new Array(matrix1.length),
87
- Bcolj = new Array(matrix1[0].length);
88
-
89
- for (var j = 0; j < matrix2[0].length; j++){
90
- for (var k = 0; k < matrix1[0].length; k++){
91
- Bcolj[k] = matrix2[k][j];
92
- }
93
- for (var i = 0; i < matrix1.length; i++){
94
-
95
- if (j === 0)
96
- X[i] = new Array(matrix2[0].length);
97
-
98
- var Arowi = matrix1[i];
99
- var s = 0;
100
- for (var k = 0; k < matrix1[0].length; k++){
101
- s += Arowi[k]*Bcolj[k];
102
- }
103
- X[i][j] = s;
104
- }
105
- }
106
- return X;
107
- };
108
-
109
-
110
- /**
111
- * LUDecomposition to solve A*X = B, based on WEKA code
112
- * @param {Array.<Array.<Number>>} A - left matrix of equation to be solved
113
- * @param {Array.<Array.<Number>>} B - right matrix of equation to be solved
114
- * @return {Array.<Array.<Number>>} X so that L*U*X = B(piv,:)
115
- */
116
- self.webgazer.mat.LUDecomposition = function(A,B){
117
- var LU = new Array(A.length);
118
-
119
- for (var i = 0; i < A.length; i++){
120
- LU[i] = new Array(A[0].length);
121
- for (var j = 0; j < A[0].length; j++){
122
- LU[i][j] = A[i][j];
123
- }
124
- }
125
-
126
- var m = A.length;
127
- var n = A[0].length;
128
- var piv = new Array(m);
129
- for (var i = 0; i < m; i++){
130
- piv[i] = i;
131
- }
132
- var pivsign = 1;
133
- var LUrowi = new Array();
134
- var LUcolj = new Array(m);
135
- // Outer loop.
136
- for (var j = 0; j < n; j++){
137
- // Make a copy of the j-th column to localize references.
138
- for (var i = 0; i < m; i++){
139
- LUcolj[i] = LU[i][j];
140
- }
141
- // Apply previous transformations.
142
- for (var i = 0; i < m; i++){
143
- LUrowi = LU[i];
144
- // Most of the time is spent in the following dot product.
145
- var kmax = Math.min(i,j);
146
- var s = 0;
147
- for (var k = 0; k < kmax; k++){
148
- s += LUrowi[k]*LUcolj[k];
149
- }
150
- LUrowi[j] = LUcolj[i] -= s;
151
- }
152
- // Find pivot and exchange if necessary.
153
- var p = j;
154
- for (var i = j+1; i < m; i++){
155
- if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])){
156
- p = i;
157
- }
158
- }
159
- if (p != j){
160
- for (var k = 0; k < n; k++){
161
- var t = LU[p][k];
162
- LU[p][k] = LU[j][k];
163
- LU[j][k] = t;
164
- }
165
- var k = piv[p];
166
- piv[p] = piv[j];
167
- piv[j] = k;
168
- pivsign = -pivsign;
169
- }
170
- // Compute multipliers.
171
- if (j < m & LU[j][j] != 0){
172
- for (var i = j+1; i < m; i++){
173
- LU[i][j] /= LU[j][j];
174
- }
175
- }
176
- }
177
- if (B.length != m){
178
- console.log('Matrix row dimensions must agree.');
179
- }
180
- for (var j = 0; j < n; j++){
181
- if (LU[j][j] === 0){
182
- console.log('Matrix is singular.')
183
- }
184
- }
185
- var nx = B[0].length;
186
- var X = self.webgazer.mat.getMatrix(B,piv,0,nx-1);
187
- // Solve L*Y = B(piv,:)
188
- for (var k = 0; k < n; k++){
189
- for (var i = k+1; i < n; i++){
190
- for (var j = 0; j < nx; j++){
191
- X[i][j] -= X[k][j]*LU[i][k];
192
- }
193
- }
194
- }
195
- // Solve U*X = Y;
196
- for (var k = n-1; k >= 0; k--){
197
- for (var j = 0; j < nx; j++){
198
- X[k][j] /= LU[k][k];
199
- }
200
- for (var i = 0; i < k; i++){
201
- for (var j = 0; j < nx; j++){
202
- X[i][j] -= X[k][j]*LU[i][k];
203
- }
204
- }
205
- }
206
- return X;
207
- };
208
-
209
- /**
210
- * Least squares solution of A*X = B, based on WEKA code
211
- * @param {Array.<Array.<Number>>} A - left side matrix to be solved
212
- * @param {Array.<Array.<Number>>} B - a matrix with as many rows as A and any number of columns.
213
- * @return {Array.<Array.<Number>>} X - that minimizes the two norms of QR*X-B.
214
- */
215
- self.webgazer.mat.QRDecomposition = function(A, B){
216
- // Initialize.
217
- var QR = new Array(A.length);
218
-
219
- for (var i = 0; i < A.length; i++){
220
- QR[i] = new Array(A[0].length);
221
- for (var j = 0; j < A[0].length; j++){
222
- QR[i][j] = A[i][j];
223
- }
224
- }
225
- var m = A.length;
226
- var n = A[0].length;
227
- var Rdiag = new Array(n);
228
- var nrm;
229
-
230
- // Main loop.
231
- for (var k = 0; k < n; k++){
232
- // Compute 2-norm of k-th column without under/overflow.
233
- nrm = 0;
234
- for (var i = k; i < m; i++){
235
- nrm = Math.hypot(nrm,QR[i][k]);
236
- }
237
- if (nrm != 0){
238
- // Form k-th Householder vector.
239
- if (QR[k][k] < 0){
240
- nrm = -nrm;
241
- }
242
- for (var i = k; i < m; i++){
243
- QR[i][k] /= nrm;
244
- }
245
- QR[k][k] += 1;
246
-
247
- // Apply transformation to remaining columns.
248
- for (var j = k+1; j < n; j++){
249
- var s = 0;
250
- for (var i = k; i < m; i++){
251
- s += QR[i][k]*QR[i][j];
252
- }
253
- s = -s/QR[k][k];
254
- for (var i = k; i < m; i++){
255
- QR[i][j] += s*QR[i][k];
256
- }
257
- }
258
- }
259
- Rdiag[k] = -nrm;
260
- }
261
- if (B.length != m){
262
- console.log('Matrix row dimensions must agree.');
263
- }
264
- for (var j = 0; j < n; j++){
265
- if (Rdiag[j] === 0)
266
- console.log('Matrix is rank deficient');
267
- }
268
- // Copy right hand side
269
- var nx = B[0].length;
270
- var X = new Array(B.length);
271
- for(var i=0; i<B.length; i++){
272
- X[i] = new Array(B[0].length);
273
- }
274
- for (var i = 0; i < B.length; i++){
275
- for (var j = 0; j < B[0].length; j++){
276
- X[i][j] = B[i][j];
277
- }
278
- }
279
- // Compute Y = transpose(Q)*B
280
- for (var k = 0; k < n; k++){
281
- for (var j = 0; j < nx; j++){
282
- var s = 0.0;
283
- for (var i = k; i < m; i++){
284
- s += QR[i][k]*X[i][j];
285
- }
286
- s = -s/QR[k][k];
287
- for (var i = k; i < m; i++){
288
- X[i][j] += s*QR[i][k];
289
- }
290
- }
291
- }
292
- // Solve R*X = Y;
293
- for (var k = n-1; k >= 0; k--){
294
- for (var j = 0; j < nx; j++){
295
- X[k][j] /= Rdiag[k];
296
- }
297
- for (var i = 0; i < k; i++){
298
- for (var j = 0; j < nx; j++){
299
- X[i][j] -= X[k][j]*QR[i][k];
300
- }
301
- }
302
- }
303
- return self.webgazer.mat.getSubMatrix(X,0,n-1,0,nx-1);
304
- }
305
-
306
- }());