remote-calibrator 0.2.1 → 0.2.2-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -0
  3. package/homepage/example.js +11 -13
  4. package/lib/RemoteCalibrator.min.js +1 -1
  5. package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
  6. package/lib/RemoteCalibrator.min.js.map +1 -1
  7. package/package.json +8 -8
  8. package/src/WebGazer4RC/.gitattributes +10 -0
  9. package/src/WebGazer4RC/LICENSE.md +15 -0
  10. package/src/WebGazer4RC/README.md +142 -0
  11. package/src/WebGazer4RC/gnu-lgpl-v3.0.md +163 -0
  12. package/src/WebGazer4RC/gplv3.md +636 -0
  13. package/src/WebGazer4RC/package-lock.json +1133 -0
  14. package/src/WebGazer4RC/package.json +28 -0
  15. package/src/WebGazer4RC/src/dom_util.mjs +27 -0
  16. package/src/WebGazer4RC/src/facemesh.mjs +150 -0
  17. package/src/WebGazer4RC/src/index.mjs +1213 -0
  18. package/src/WebGazer4RC/src/mat.mjs +301 -0
  19. package/src/WebGazer4RC/src/params.mjs +29 -0
  20. package/src/WebGazer4RC/src/pupil.mjs +109 -0
  21. package/src/WebGazer4RC/src/ridgeReg.mjs +104 -0
  22. package/src/WebGazer4RC/src/ridgeRegThreaded.mjs +161 -0
  23. package/src/WebGazer4RC/src/ridgeWeightedReg.mjs +125 -0
  24. package/src/WebGazer4RC/src/ridgeWorker.mjs +135 -0
  25. package/src/WebGazer4RC/src/util.mjs +348 -0
  26. package/src/WebGazer4RC/src/util_regression.mjs +240 -0
  27. package/src/WebGazer4RC/src/worker_scripts/mat.js +306 -0
  28. package/src/WebGazer4RC/src/worker_scripts/util.js +398 -0
  29. package/src/WebGazer4RC/test/regression_test.js +182 -0
  30. package/src/WebGazer4RC/test/run_tests_and_server.sh +24 -0
  31. package/src/WebGazer4RC/test/util_test.js +60 -0
  32. package/src/WebGazer4RC/test/webgazerExtract_test.js +40 -0
  33. package/src/WebGazer4RC/test/webgazer_test.js +160 -0
  34. package/src/WebGazer4RC/test/www_page_test.js +41 -0
  35. package/src/components/onCanvas.js +1 -2
  36. package/src/core.js +18 -0
  37. package/src/css/distance.scss +1 -0
  38. package/src/distance/distance.js +4 -1
  39. package/src/distance/distanceTrack.js +2 -2
  40. package/src/panel/panel.js +14 -5
  41. 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;