remote-calibrator 0.3.0 → 0.5.0-beta.3
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +12 -0
- package/README.md +29 -19
- package/homepage/example.js +9 -3
- package/i18n/fetch-languages-sheets.js +5 -4
- package/lib/RemoteCalibrator.min.js +1 -1
- package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
- package/lib/RemoteCalibrator.min.js.map +1 -1
- package/package.json +15 -15
- package/src/WebGazer4RC/.gitattributes +10 -0
- package/src/WebGazer4RC/LICENSE.md +15 -0
- package/src/WebGazer4RC/README.md +142 -0
- package/src/WebGazer4RC/gnu-lgpl-v3.0.md +163 -0
- package/src/WebGazer4RC/gplv3.md +636 -0
- package/src/WebGazer4RC/package-lock.json +1133 -0
- package/src/WebGazer4RC/package.json +28 -0
- package/src/WebGazer4RC/src/dom_util.mjs +27 -0
- package/src/WebGazer4RC/src/facemesh.mjs +150 -0
- package/src/WebGazer4RC/src/index.mjs +1235 -0
- package/src/WebGazer4RC/src/mat.mjs +301 -0
- package/src/WebGazer4RC/src/params.mjs +29 -0
- package/src/WebGazer4RC/src/pupil.mjs +109 -0
- package/src/WebGazer4RC/src/ridgeReg.mjs +104 -0
- package/src/WebGazer4RC/src/ridgeRegThreaded.mjs +161 -0
- package/src/WebGazer4RC/src/ridgeWeightedReg.mjs +125 -0
- package/src/WebGazer4RC/src/ridgeWorker.mjs +135 -0
- package/src/WebGazer4RC/src/util.mjs +348 -0
- package/src/WebGazer4RC/src/util_regression.mjs +240 -0
- package/src/WebGazer4RC/src/worker_scripts/mat.js +306 -0
- package/src/WebGazer4RC/src/worker_scripts/util.js +398 -0
- package/src/WebGazer4RC/test/regression_test.js +182 -0
- package/src/WebGazer4RC/test/run_tests_and_server.sh +24 -0
- package/src/WebGazer4RC/test/util_test.js +60 -0
- package/src/WebGazer4RC/test/webgazerExtract_test.js +40 -0
- package/src/WebGazer4RC/test/webgazer_test.js +160 -0
- package/src/WebGazer4RC/test/www_page_test.js +41 -0
- package/src/const.js +3 -0
- package/src/core.js +8 -0
- package/src/css/distance.scss +40 -0
- package/src/css/panel.scss +32 -1
- package/src/distance/distance.js +4 -4
- package/src/distance/distanceCheck.js +115 -0
- package/src/distance/distanceTrack.js +99 -41
- package/src/{interpupillaryDistance.js → distance/interPupillaryDistance.js} +14 -12
- package/src/gaze/gazeTracker.js +16 -1
- package/src/i18n.js +1 -1
- package/src/index.js +2 -1
- package/src/panel.js +32 -3
- package/webpack.config.js +4 -4
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"name": "webgazer",
|
3
|
+
"version": "2.1.0",
|
4
|
+
"private": "true",
|
5
|
+
"repository": {
|
6
|
+
"type": "git",
|
7
|
+
"url": "https://github.com/brownhci/WebGazer.git"
|
8
|
+
},
|
9
|
+
"license": "GPL-3.0-or-later",
|
10
|
+
"main": "dist/webgazer.js",
|
11
|
+
"module": "src/index.mjs",
|
12
|
+
"files": [
|
13
|
+
"/dist",
|
14
|
+
"/src"
|
15
|
+
],
|
16
|
+
"keywords": [
|
17
|
+
"webgazer",
|
18
|
+
"eyetracking",
|
19
|
+
"webcam"
|
20
|
+
],
|
21
|
+
"dependencies": {
|
22
|
+
"@tensorflow-models/facemesh": "^0.0.5",
|
23
|
+
"@tensorflow/tfjs": "^3.8.0",
|
24
|
+
"localforage": "^1.9.0",
|
25
|
+
"numeric": "1.2.6",
|
26
|
+
"regression": "2.0.1"
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
// helper functions
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Provides requestAnimationFrame in a cross browser way.
|
5
|
+
*/
|
6
|
+
window.requestAnimFrame = (function() {
|
7
|
+
return window.requestAnimationFrame ||
|
8
|
+
window.webkitRequestAnimationFrame ||
|
9
|
+
window.mozRequestAnimationFrame ||
|
10
|
+
window.oRequestAnimationFrame ||
|
11
|
+
window.msRequestAnimationFrame ||
|
12
|
+
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
|
13
|
+
return window.setTimeout(callback, 1000/60);
|
14
|
+
};
|
15
|
+
})();
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Provides cancelRequestAnimationFrame in a cross browser way.
|
19
|
+
*/
|
20
|
+
window.cancelRequestAnimFrame = (function() {
|
21
|
+
return window.cancelCancelRequestAnimationFrame ||
|
22
|
+
window.webkitCancelRequestAnimationFrame ||
|
23
|
+
window.mozCancelRequestAnimationFrame ||
|
24
|
+
window.oCancelRequestAnimationFrame ||
|
25
|
+
window.msCancelRequestAnimationFrame ||
|
26
|
+
window.clearTimeout;
|
27
|
+
})();
|
@@ -0,0 +1,150 @@
|
|
1
|
+
const FacemeshModel = require('@tensorflow-models/facemesh');
|
2
|
+
/**
|
3
|
+
* Constructor of TFFaceMesh object
|
4
|
+
* @constructor
|
5
|
+
* */
|
6
|
+
const TFFaceMesh = function() {
|
7
|
+
//Backend options are webgl, wasm, and CPU.
|
8
|
+
//For recent laptops WASM is better than WebGL.
|
9
|
+
//TODO: This hack makes loading the model block the UI. We should fix that
|
10
|
+
// this.model = (async () => { return await facemesh.load({"maxFaces":1}) })();
|
11
|
+
this.model = FacemeshModel.load({ "maxFaces": 1 });
|
12
|
+
this.predictionReady = false;
|
13
|
+
};
|
14
|
+
|
15
|
+
// Global variable for face landmark positions array
|
16
|
+
TFFaceMesh.prototype.positionsArray = null;
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Isolates the two patches that correspond to the user's eyes
|
20
|
+
* @param {Canvas} imageCanvas - canvas corresponding to the webcam stream
|
21
|
+
* @param {Number} width - of imageCanvas
|
22
|
+
* @param {Number} height - of imageCanvas
|
23
|
+
* @return {Object} the two eye-patches, first left, then right eye
|
24
|
+
*/
|
25
|
+
TFFaceMesh.prototype.getEyePatches = async function(imageCanvas, width, height) {
|
26
|
+
|
27
|
+
if (imageCanvas.width === 0) {
|
28
|
+
return null;
|
29
|
+
}
|
30
|
+
|
31
|
+
// Load the MediaPipe facemesh model.
|
32
|
+
const model = await this.model;
|
33
|
+
|
34
|
+
// Pass in a video stream (or an image, canvas, or 3D tensor) to obtain an
|
35
|
+
// array of detected faces from the MediaPipe graph.
|
36
|
+
const predictions = await model.estimateFaces(imageCanvas);
|
37
|
+
|
38
|
+
if (predictions.length == 0){
|
39
|
+
return false;
|
40
|
+
}
|
41
|
+
|
42
|
+
// Save positions to global variable
|
43
|
+
this.positionsArray = predictions[0].scaledMesh;
|
44
|
+
const positions = this.positionsArray;
|
45
|
+
|
46
|
+
// Fit the detected eye in a rectangle. [20200626 xk] not clear which approach is better
|
47
|
+
// https://raw.githubusercontent.com/tensorflow/tfjs-models/master/facemesh/mesh_map.jpg
|
48
|
+
|
49
|
+
// // Maintains a relatively stable shape of the bounding box at the cost of cutting off parts of
|
50
|
+
// // the eye when the eye is tilted.
|
51
|
+
// var leftOriginX = Math.round(positions[130][0]);
|
52
|
+
// var leftOriginY = Math.round(positions[27][1]);
|
53
|
+
// var leftWidth = Math.round(positions[243][0] - leftOriginX);
|
54
|
+
// var leftHeight = Math.round(positions[23][1] - leftOriginY);
|
55
|
+
// var rightOriginX = Math.round(positions[463][0]);
|
56
|
+
// var rightOriginY = Math.round(positions[257][1]);
|
57
|
+
// var rightWidth = Math.round(positions[359][0] - rightOriginX);
|
58
|
+
// var rightHeight = Math.round(positions[253][1] - rightOriginY);
|
59
|
+
|
60
|
+
// Won't really cut off any parts of the eye, at the cost of warping the shape (i.e. height/
|
61
|
+
// width ratio) of the bounding box.
|
62
|
+
var leftOriginX = Math.round(Math.min(positions[247][0], positions[130][0], positions[25][0]));
|
63
|
+
var leftOriginY = Math.round(Math.min(positions[247][1], positions[27][1], positions[190][1]));
|
64
|
+
var leftWidth = Math.round(Math.max(positions[190][0], positions[243][0], positions[233][0]) - leftOriginX);
|
65
|
+
var leftHeight = Math.round(Math.max(positions[25][1], positions[23][1], positions[112][1]) - leftOriginY);
|
66
|
+
var rightOriginX = Math.round(Math.min(positions[414][0], positions[463][0], positions[453][0]));
|
67
|
+
var rightOriginY = Math.round(Math.min(positions[414][1], positions[257][1], positions[467][1]));
|
68
|
+
var rightWidth = Math.round(Math.max(positions[467][0], positions[359][0], positions[255][0]) - rightOriginX);
|
69
|
+
var rightHeight = Math.round(Math.max(positions[341][1], positions[253][1], positions[255][1]) - rightOriginY);
|
70
|
+
|
71
|
+
if (leftWidth === 0 || rightWidth === 0){
|
72
|
+
console.log('an eye patch had zero width');
|
73
|
+
return null;
|
74
|
+
}
|
75
|
+
|
76
|
+
if (leftHeight === 0 || rightHeight === 0){
|
77
|
+
console.log('an eye patch had zero height');
|
78
|
+
return null;
|
79
|
+
}
|
80
|
+
|
81
|
+
// Start building object to be returned
|
82
|
+
var eyeObjs = {};
|
83
|
+
|
84
|
+
var leftImageData = imageCanvas.getContext('2d').getImageData(leftOriginX, leftOriginY, leftWidth, leftHeight);
|
85
|
+
eyeObjs.left = {
|
86
|
+
patch: leftImageData,
|
87
|
+
imagex: leftOriginX,
|
88
|
+
imagey: leftOriginY,
|
89
|
+
width: leftWidth,
|
90
|
+
height: leftHeight
|
91
|
+
};
|
92
|
+
|
93
|
+
var rightImageData = imageCanvas.getContext('2d').getImageData(rightOriginX, rightOriginY, rightWidth, rightHeight);
|
94
|
+
eyeObjs.right = {
|
95
|
+
patch: rightImageData,
|
96
|
+
imagex: rightOriginX,
|
97
|
+
imagey: rightOriginY,
|
98
|
+
width: rightWidth,
|
99
|
+
height: rightHeight
|
100
|
+
};
|
101
|
+
|
102
|
+
this.predictionReady = true;
|
103
|
+
|
104
|
+
return eyeObjs;
|
105
|
+
};
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Returns the positions array corresponding to the last call to getEyePatches.
|
109
|
+
* Requires that getEyePatches() was called previously, else returns null.
|
110
|
+
*/
|
111
|
+
TFFaceMesh.prototype.getPositions = function () {
|
112
|
+
return this.positionsArray;
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Reset the tracker to default values
|
117
|
+
*/
|
118
|
+
TFFaceMesh.prototype.reset = function(){
|
119
|
+
console.log( "Unimplemented; Tracking.js has no obvious reset function" );
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Draw TF_FaceMesh_Overlay
|
124
|
+
*/
|
125
|
+
TFFaceMesh.prototype.drawFaceOverlay= function(ctx, keypoints){
|
126
|
+
// If keypoints is falsy, don't do anything
|
127
|
+
if (keypoints) {
|
128
|
+
ctx.fillStyle = '#32EEDB';
|
129
|
+
ctx.strokeStyle = '#32EEDB';
|
130
|
+
ctx.lineWidth = 0.5;
|
131
|
+
|
132
|
+
for (let i = 0; i < keypoints.length; i++) {
|
133
|
+
const x = keypoints[i][0];
|
134
|
+
const y = keypoints[i][1];
|
135
|
+
|
136
|
+
ctx.beginPath();
|
137
|
+
ctx.arc(x, y, 1 /* radius */, 0, 2 * Math.PI);
|
138
|
+
ctx.closePath();
|
139
|
+
ctx.fill();
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* The TFFaceMesh object name
|
146
|
+
* @type {string}
|
147
|
+
*/
|
148
|
+
TFFaceMesh.prototype.name = 'TFFaceMesh';
|
149
|
+
|
150
|
+
export default TFFaceMesh;
|