diff --git a/package.json b/package.json index 9a04f8629..ed2fff8ce 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "clean": "rimraf dist", "prebuild": "npm run clean", "dev": "webpack --progress --config webpack.config.js --mode development", + "watch": "webpack --watch --progress --config webpack.config.js --mode development", "build": "webpack --progress --config webpack.config.js --mode production", "gen_docs": "jsdoc -c jsdoc.conf.json src/*" }, diff --git a/src/index.mjs b/src/index.mjs index b13ed27eb..913da4e29 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -10,6 +10,7 @@ import TFFaceMesh from './facemesh.mjs'; import Reg from './ridgeReg.mjs'; import ridgeRegWeighted from './ridgeWeightedReg.mjs'; import ridgeRegThreaded from './ridgeRegThreaded.mjs'; +import RidgeRegXpln from './ridgeRegXpln.mjs'; import util from './util.mjs'; const webgazer = {}; @@ -18,6 +19,7 @@ webgazer.tracker.TFFaceMesh = TFFaceMesh; webgazer.reg = Reg; webgazer.reg.RidgeWeightedReg = ridgeRegWeighted.RidgeWeightedReg; webgazer.reg.RidgeRegThreaded = ridgeRegThreaded.RidgeRegThreaded; +webgazer.reg.RidgeRegXpln = RidgeRegXpln; webgazer.util = util; webgazer.params = params; @@ -57,7 +59,10 @@ var eventTypes = ['click', 'move']; //movelistener timeout clock parameters var moveClock = performance.now(); //currently used tracker and regression models, defaults to clmtrackr and linear regression +/* xpln.ai 2023-11-19: Disable default TFFaceMesh tracker, because we use our own faceTracker! var curTracker = new webgazer.tracker.TFFaceMesh(); +*/ +var curTracker = null; // xpln.ai 2023-11-19 var regs = [new webgazer.reg.RidgeReg()]; // var blinkDetector = new webgazer.BlinkDetector(); diff --git a/src/ridgeRegXpln.mjs b/src/ridgeRegXpln.mjs new file mode 100644 index 000000000..57a685cbf --- /dev/null +++ b/src/ridgeRegXpln.mjs @@ -0,0 +1,87 @@ +import util from './util.mjs'; +import util_regression from './util_regression.mjs'; + +/** + * Constructor of RidgeRegXpln object, + * this object allow to perform ridge regression + * @constructor + */ +const RidgeRegXpln = function() { + this.init(); +}; + +/** + * Initialize new arrays and initialize Kalman filter. + */ +RidgeRegXpln.prototype.init = util_regression.InitRegression + +/** + * Add given data from features + * @param {Array} features - features where extract data to add + * @param {Object} screenPos - The current screen point + * @param {Object} type - The type of performed action + */ +RidgeRegXpln.prototype.addData = function(features, screenPos, type) { + if (!features) return; + if (type === 'click') { + this.screenXClicksArray.push([screenPos[0]]); + this.screenYClicksArray.push([screenPos[1]]); + this.eyeFeaturesClicks.push(features); + delete this.xCoef; + delete this.yCoef; + } else if (type === 'move') { + throw new Error('Not implemented!!!'); + } +}; + +/** + * Try to predict coordinates from pupil data + * after apply linear regression on data set + * @param {Array} features - The current user features features + * @returns {Object} + */ +RidgeRegXpln.prototype.predict = function(features) { + if (!features || this.eyeFeaturesClicks.length === 0) { + return null; + } + + if (!this.xCoef || !this.yCoef) { + var screenXArray = this.screenXClicksArray.data; + var screenYArray = this.screenYClicksArray.data; + var eyeFeatures = this.eyeFeaturesClicks.data; + this.xCoef = util_regression.ridge(screenXArray, eyeFeatures, this.ridgeParameter); + this.yCoef = util_regression.ridge(screenYArray, eyeFeatures, this.ridgeParameter); + } + + var predictedX = 0, predictedY = 0, cx = this.xCoef, cy = this.yCoef; + for(var i=0; i< features.length; i++) { + var f = features[i]; + predictedX += f * cx[i]; + predictedY += f * cy[i]; + } + + return { + x: Math.floor(predictedX), + y: Math.floor(predictedY) + }; +}; + +RidgeRegXpln.prototype.setData = function() { + throw new Error('Not implemented!!!'); +}; + +/** + * Return the data + * @returns {Array.|*} + */ +RidgeRegXpln.prototype.getData = function() { + throw new Error('Not implemented!!!'); +}; + +/** + * The RidgeRegXpln object name + * @type {string} + */ +RidgeRegXpln.prototype.name = 'ridgeXpln'; + +export default RidgeRegXpln; diff --git a/src/util.mjs b/src/util.mjs index 18bbdaecb..3c9b4c1ca 100644 --- a/src/util.mjs +++ b/src/util.mjs @@ -24,6 +24,20 @@ util.Eye = function(patch, imagex, imagey, width, height) { this.height = height; }; +// xpln.ai 2024-02-26: normalize pixel values between 0 and 1 +util.normalize = function(arr, digits) { + var min = Infinity, max = -Infinity, precision = Math.pow(10, digits); + for (var i=0, imax=arr.length; i arr[i]) min = arr[i]; + if (max < arr[i]) max = arr[i]; + } + var range = max - min; + if (!range) return; + for (var i=0, imax=arr.length; i