remote-calibrator 0.2.1 → 0.2.2-beta.0
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.
- package/CHANGELOG.md +16 -0
- package/README.md +2 -0
- package/homepage/example.js +11 -13
- 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 +8 -8
- 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 +1213 -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/components/onCanvas.js +1 -2
- package/src/core.js +18 -0
- package/src/css/distance.scss +1 -0
- package/src/distance/distance.js +4 -1
- package/src/distance/distanceTrack.js +2 -2
- package/src/panel/panel.js +14 -5
- package/src/text.json +4 -2
@@ -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;
|