diff --git a/examples/webgazer.html b/examples/webgazer.html index e0790a65..ec2a21f9 100644 --- a/examples/webgazer.html +++ b/examples/webgazer.html @@ -20,17 +20,20 @@ var calibration = { type: 'webgazer-calibrate', - calibration_points: [[50,50], [25,25], [25,75], [75,25], [75,75]], + //calibration_points: [[50,50], [25,25], [25,75], [75,25], [75,75]], + calibration_points: [[10,10],[10,30],[10,50],[10,70],[10,90],[30,10],[30,30],[30,50],[30,70],[30,90],[50,10],[50,30],[50,50],[50,70],[50,90],[70,10],[70,30],[70,50],[70,70],[70,90],[90,10],[90,30],[90,50],[90,70],[90,90]], repetitions_per_point: 1, randomize_calibration_order: true, - time_per_point: 1000, + //calibration_mode: 'view', + time_per_point: 500, time_to_saccade: 1000 } var validation = { type: 'webgazer-validate', validation_points: [[-200,-200], [-200,200], [200,-200], [200,200]], - validation_point_mode: 'center-offset-pixels' + validation_point_coordinates: 'center-offset-pixels', + show_validation_data: true } var fixation = { diff --git a/extensions/jspsych-ext-webgazer.js b/extensions/jspsych-ext-webgazer.js index b1e3ee63..e9eb3080 100644 --- a/extensions/jspsych-ext-webgazer.js +++ b/extensions/jspsych-ext-webgazer.js @@ -70,7 +70,7 @@ jsPsych.extensions['webgazer'] = (function () { // send back the gazeData return { - gazeData: JSON.stringify(state.currentTrialData) + webgazer_gaze_data: state.currentTrialData } } diff --git a/plugins/jspsych-webgazer-calibrate.js b/plugins/jspsych-webgazer-calibrate.js index ca01582e..5f92f445 100644 --- a/plugins/jspsych-webgazer-calibrate.js +++ b/plugins/jspsych-webgazer-calibrate.js @@ -15,6 +15,10 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { type: jsPsych.plugins.parameterType.INT, default: [[10,10], [10,50], [10,90], [50,10], [50,50], [50,90], [90,10], [90,50], [90,90]] }, + calibration_mode: { + type: jsPsych.plugins.parameterType.STRING, + default: 'click', // options: 'click', 'view' + }, repetitions_per_point: { type: jsPsych.plugins.parameterType.INT, default: 1 @@ -64,10 +68,6 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { "

Use the video in the upper-left corner as a guide. Center your face in the box.

"+ "

When your face is centered in the box and the box turns green, you can click to continue.

"+ "" - // "

Quality of detection:

"+ - // "
"+ - // "
"+ - // "
"+ "
"+ "" @@ -81,20 +81,7 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { document.querySelector('#jspsych-wg-cont').addEventListener('click', function(){ observer.disconnect(); show_begin_calibrate_message(); - }) - - // function waitForFace(){ - // var score = check_face_score(); - // //wg_container.querySelector('#video-detect-quality-inner').style.width = (score*100) + "%" - // if(score){ - // document.querySelector('#jspsych-wg-cont').disabled = false; - // } else { - // requestAnimationFrame(waitForFace); - // } - // } - // requestAnimationFrame(waitForFace); - - + }) } function face_detect_event_observer(mutationsList, observer){ @@ -110,12 +97,22 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { function show_begin_calibrate_message(){ jsPsych.extensions['webgazer'].hideVideo(); - wg_container.innerHTML = "
"+ + if(trial.calibration_mode == 'view'){ + wg_container.innerHTML = "
"+ + "

Great! Now the eye tracker will be calibrated to translate the image of your eyes from the webcam to a location on your screen.

"+ + "

To do this, you need to look at a series of dots.

"+ + "

Keep your head still, and focus on each dot as quickly as possible. Keep your gaze fixed on the dot for as long as it is on the screen.

"+ + ""+ + "
" + } + if(trial.calibration_mode == 'click'){ + wg_container.innerHTML = "
"+ "

Great! Now the eye tracker will be calibrated to translate the image of your eyes from the webcam to a location on your screen.

"+ - "

To do this, you need to look at a series of dots.

"+ - "

Keep your head still, and focus on each dot as quickly as possible. Keep your gaze fixed on the dot for as long as it is on the screen.

"+ + "

To do this, you need to click a series of dots.

"+ + "

Keep your head still, and click on each dot as it appears. Look at the dot as you click it.

"+ ""+ "
" + } document.querySelector('#begin-calibrate-btn').addEventListener('click', function(){ calibrate(); }); @@ -127,6 +124,9 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { function calibrate(){ jsPsych.extensions['webgazer'].resume(); + if(trial.calibration_mode == 'click'){ + jsPsych.extensions['webgazer'].startMouseCalibration(); + } next_calibration_round(); } @@ -160,36 +160,40 @@ jsPsych.plugins["webgazer-calibrate"] = (function() { wg_container.innerHTML = pt_html; var pt_dom = wg_container.querySelector('#calibration-point'); - - var br = pt_dom.getBoundingClientRect(); - var x = br.left + br.width / 2; - var y = br.top + br.height / 2; - - var pt_start_cal = performance.now() + trial.time_to_saccade; - var pt_finish = performance.now() + trial.time_to_saccade + trial.time_per_point; - requestAnimationFrame(function watch_dot(){ - - if(performance.now() > pt_start_cal){ - jsPsych.extensions['webgazer'].calibratePoint(x,y,'click'); - } - if(performance.now() < pt_finish){ - requestAnimationFrame(watch_dot); - } else { + if(trial.calibration_mode == 'click'){ + pt_dom.style.cursor = 'pointer'; + pt_dom.addEventListener('click', function(){ next_calibration_point(); - } - }) - - // jsPsych.pluginAPI.setTimeout(function(){ - // pt_dom.style.backgroundColor = "#fff"; - // pt_dom.addEventListener('click', function(){ - // next_calibration_point(); - // }); - // }, Math.random()*(trial.maximum_dot_change_delay-trial.minimum_dot_change_delay)+trial.minimum_dot_change_delay); + }) + } + + if(trial.calibration_mode == 'view'){ + var br = pt_dom.getBoundingClientRect(); + var x = br.left + br.width / 2; + var y = br.top + br.height / 2; + var pt_start_cal = performance.now() + trial.time_to_saccade; + var pt_finish = performance.now() + trial.time_to_saccade + trial.time_per_point; + + requestAnimationFrame(function watch_dot(){ + + if(performance.now() > pt_start_cal){ + jsPsych.extensions['webgazer'].calibratePoint(x,y,'click'); + } + if(performance.now() < pt_finish){ + requestAnimationFrame(watch_dot); + } else { + next_calibration_point(); + } + }) + } } function calibration_done(){ + if(trial.calibration_mode == 'click'){ + jsPsych.extensions['webgazer'].stopMouseCalibration(); + } wg_container.innerHTML = ""; end_trial(); } diff --git a/plugins/jspsych-webgazer-validate.js b/plugins/jspsych-webgazer-validate.js index a314353b..8f63eeff 100644 --- a/plugins/jspsych-webgazer-validate.js +++ b/plugins/jspsych-webgazer-validate.js @@ -15,7 +15,7 @@ jsPsych.plugins["webgazer-validate"] = (function() { type: jsPsych.plugins.parameterType.INT, default: [[10,10], [10,50], [10,90], [50,10], [50,50], [50,90], [90,10], [90,50], [90,90]] }, - validation_point_mode: { + validation_point_coordinates: { type: jsPsych.plugins.parameterType.STRING, default: 'percent' // options: 'percent', 'center-offset-pixels' }, @@ -34,21 +34,20 @@ jsPsych.plugins["webgazer-validate"] = (function() { point_size:{ type: jsPsych.plugins.parameterType.INT, default: 10 + }, + show_validation_data: { + type: jsPsych.plugins.parameterType.BOOL, + default: false } } } - // provide options for calibration routines? - // dot clicks? - // track a dot with mouse? - - // then a validation phase of staring at the dot in different locations? - plugin.trial = function(display_element, trial) { var trial_data = {} trial_data.raw_gaze = []; trial_data.percent_in_roi = []; + trial_data.average_offset = []; var html = `
@@ -125,22 +124,15 @@ jsPsych.plugins["webgazer-validate"] = (function() { trial_data.raw_gaze.push(pt_data); next_validation_point(); } - }) - - // jsPsych.pluginAPI.setTimeout(function(){ - // pt_dom.style.backgroundColor = "#fff"; - // pt_dom.addEventListener('click', function(){ - // next_calibration_point(); - // }); - // }, Math.random()*(trial.maximum_dot_change_delay-trial.minimum_dot_change_delay)+trial.minimum_dot_change_delay); + }); } function drawValidationPoint(x,y){ - if(trial.validation_point_mode == 'percent'){ + if(trial.validation_point_coordinates == 'percent'){ return drawValidationPoint_PercentMode(x,y); } - if(trial.validation_point_mode == 'center-offset-pixels'){ + if(trial.validation_point_coordinates == 'center-offset-pixels'){ return drawValidationPoint_CenterOffsetMode(x,y); } } @@ -154,43 +146,45 @@ jsPsych.plugins["webgazer-validate"] = (function() { } function drawCircle(target_x, target_y, dx, dy, r){ - if(trial.validation_point_mode == 'percent'){ + if(trial.validation_point_coordinates == 'percent'){ return drawCircle_PercentMode(target_x, target_y, dx, dy, r); } - if(trial.validation_point_mode == 'center-offset-pixels'){ + if(trial.validation_point_coordinates == 'center-offset-pixels'){ return drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r); } } function drawCircle_PercentMode(target_x, target_y, dx, dy, r){ var html = ` -
+
` return html; } function drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r){ var html = ` -
+
` return html; } function drawRawDataPoint(target_x, target_y, dx, dy, ){ - if(trial.validation_point_mode == 'percent'){ + if(trial.validation_point_coordinates == 'percent'){ return drawRawDataPoint_PercentMode(target_x, target_y, dx, dy); } - if(trial.validation_point_mode == 'center-offset-pixels'){ + if(trial.validation_point_coordinates == 'center-offset-pixels'){ return drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy); } } function drawRawDataPoint_PercentMode(target_x, target_y, dx, dy){ - return `
` + var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa'; + return `
` } function drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy){ - return `
` + var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa'; + return `
` } function median(arr){ @@ -247,23 +241,31 @@ jsPsych.plugins["webgazer-validate"] = (function() { } function validation_done(){ + for(var i=0; i