separate camera init from experiment init; dot size options

This commit is contained in:
Josh de Leeuw 2021-03-01 10:16:42 -05:00
parent 7e8b83825d
commit d330c4ad7d
8 changed files with 147 additions and 88 deletions

View File

@ -19,6 +19,7 @@ jsPsych.init({
Parameter | Type | Default Value | Description
----------|------|---------------|------------
webgazer | object | `undefined` | You can explicitly pass a reference to a loaded instance of the webgazer.js library. If no explicit reference is passed then the extension will look for a global `webgazer` object. If you are loading webgazer.js via a `<script>` tag you do not need to set this parameter in most circumstances.
auto_initialize | bool | false | Whether to automatically initialize webgazer when the experiment begins. If set to `true` then the experiment will attempt to access the user's webcam immediately upon page load. The default value is `false` because it is probably a good idea to explain to the user why camera permission will be needed before asking for it. The `webgazer-init-camera` plugin can be used to initialize the camera during the experiment.
round_predictions | bool | true | Whether to round the `x`,`y` coordinates predicted by WebGazer to the nearest whole number. This *greatly* reduces the size of the data, as WebGazer records data to 15 decimal places by default. Given the noise of the system, there's really no need to record data to this level of precision.
### Trial Parameters
@ -49,6 +50,14 @@ webgazer_targets | array | An array of objects contain the pixel coordinates of
In addition to the jsPsych webgazer-* plugins, the jsPsych webgazer extension provides a set of functions that allow the researcher to interact more directly with WebGazer. These functions can be called at any point during an experiment, and are crucial for building trial plugins that interact with WebGazer. All of the functions below must be prefixed with `jsPsych.extensions.webgazer` (e.g. `jsPsych.extensions.webgazer.faceDetected()`).
### start()
Performs initialization of webgazer, including requesting permissions from the user to access the camera. Returns a `Promise` that resolves when the camera is initialized and fails if the camera cannot be accessed, e.g., because the user denies permission. This is handled automatically if using the `webgazer-init-camera` plugin or setting `auto_initialize` to `true` in the extension parameters.
### isInitialized()
Returns `true` if `start()` has been successfully called at some point, and `false` otherwise.
### faceDetected()
Returns `true` if WebGazer is ready to make predictions (`webgazer.getTracker().predictionReady` is `true`).

View File

@ -11,6 +11,7 @@ Parameter | Type | Default Value | Description
calibration_points | array | `[[10,10], [10,50], [10,90], [50,10], [50,50], [50,90], [90,10], [90,50], [90,90]]` | Array of points in `[x,y]` coordinates. Specified as a percentage of the screen width and height, from the left and top edge. The default grid is 9 points.
calibration_mode | string | `'click'` | Can specify `click` to have subjects click on calibration points or `view` to have subjects passively watch calibration points.
repetitions_per_point | numeric | 1 | The number of times to repeat the sequence of calibration points.
point_size | numeric | 20 | Diameter of the calibration points in pixels.
randomize_calibration_order | bool | `false` | Whether to randomize the order of the calibration points.
time_to_saccade | numeric | 1000 | If `calibration_mode` is set to `view`, then this is the delay before calibrating after showing a point. Gives the participant time to fixate on the new target before assuming that the participant is looking at the target.
time_per_point | numeric | 1000 | If `calibration_mode` is set to `view`, then this is the length of time to show a point while calibrating. Note that if `click` calibration is used then the point will remain on the screen until clicked.

View File

@ -15,7 +15,7 @@ repetitions_per_point | numeric | 1 | The number of times to repeat the sequence
randomize_validation_order | bool | `false` | Whether to randomize the order of the validation points.
time_to_saccade | numeric | 1000 | The delay before validating after showing a point. Gives the participant time to fixate on the new target before assuming that the participant is looking at the target.
validation_duration | numeric | 2000 | If `calibration_mode` is set to `view`, then this is the length of time to show a point while calibrating. Note that if `click` calibration is used then the point will remain on the screen until clicked.
point_size | numeric | 10 | Diameter of the validation points in pixels.
point_size | numeric | 20 | Diameter of the validation points in pixels.
show_validation_data | bool | false | If `true` then a visualization of the validation data will be shown on the screen after the validation is complete. This will show each measured gaze location color coded by whether it is within the `roi_radius` of the target point. This is mainly intended for testing and debugging.
## Data Generated

View File

@ -20,6 +20,18 @@
<script>
var camera_instructions = {
type: 'html-button-response',
stimulus: `
<p>This experiment uses your camera for eye tracking.</p>
<p>In order to participate you must allow the experiment to use your camera.</p>
<p>You will be prompted to do this on the next screen.</p>
<p>If you do not want to permit the experiment to use your camera, please close the page.</p>
`,
choices: ['Click to begin'],
post_trial_gap: 1000
}
var init_camera = {
type: 'webgazer-init-camera'
}
@ -142,6 +154,7 @@
}
var timeline = [];
timeline.push(camera_instructions);
timeline.push(init_camera);
timeline.push(calibration_instructions);
timeline.push(calibration);

View File

@ -11,6 +11,10 @@ jsPsych.extensions['webgazer'] = (function () {
// required, will be called at jsPsych.init
// should return a Promise
extension.initialize = function (params) {
// setting default values for params if not defined
params.round_predictions = typeof params.round_predictions === 'undefined' ? true : params.round_predictions;
params.auto_initialize = typeof params.auto_initialize === 'undefined' ? false : params.auto_initialize;
return new Promise(function (resolve, reject) {
if (typeof params.webgazer === 'undefined') {
if (window.webgazer) {
@ -22,28 +26,34 @@ jsPsych.extensions['webgazer'] = (function () {
state.webgazer = params.webgazer;
}
if (typeof params.round_predictions === 'undefined'){
state.round_predictions = true;
} else {
state.round_predictions = params.round_predictions;
}
// sets up event handler for webgazer data
state.webgazer.setGazeListener(handleGazeDataUpdate);
// starts webgazer, and once it initializes we stop mouseCalibration and
// pause webgazer data.
state.webgazer.begin().then(function () {
extension.stopMouseCalibration();
extension.pause();
resolve();
})
// sets state for initialization
state.initialized = false;
state.activeTrial = false;
// hide video by default
extension.hideVideo();
// hide predictions by default
extension.hidePredictions();
if (params.auto_initialize) {
// starts webgazer, and once it initializes we stop mouseCalibration and
// pause webgazer data.
state.webgazer.begin().then(function () {
state.initialized = true;
extension.stopMouseCalibration();
extension.pause();
resolve();
}).catch(function (error) {
console.error(error);
reject(error);
});
} else {
resolve();
}
})
}
@ -101,6 +111,24 @@ jsPsych.extensions['webgazer'] = (function () {
}
}
extension.start = function () {
return new Promise(function (resolve, reject) {
state.webgazer.begin().then(function () {
state.initialized = true;
extension.stopMouseCalibration();
extension.pause();
resolve();
}).catch(function (error) {
console.error(error);
reject(error);
});
});
}
extension.isInitialized = function(){
return state.initialized;
}
extension.faceDetected = function () {
return state.webgazer.getTracker().predictionReady;
}

View File

@ -19,6 +19,10 @@ jsPsych.plugins["webgazer-calibrate"] = (function() {
type: jsPsych.plugins.parameterType.STRING,
default: 'click', // options: 'click', 'view'
},
point_size:{
type: jsPsych.plugins.parameterType.INT,
default: 20
},
repetitions_per_point: {
type: jsPsych.plugins.parameterType.INT,
default: 1
@ -38,12 +42,6 @@ jsPsych.plugins["webgazer-calibrate"] = (function() {
}
}
// 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 html = `
@ -62,7 +60,6 @@ jsPsych.plugins["webgazer-calibrate"] = (function() {
calibrate();
function calibrate(){
jsPsych.extensions['webgazer'].resume();
if(trial.calibration_mode == 'click'){
@ -97,7 +94,7 @@ jsPsych.plugins["webgazer-calibrate"] = (function() {
}
function calibration_display_gaze_only(pt){
var pt_html = '<div id="calibration-point" style="width:10px; height:10px; border-radius:10px; border: 1px solid #000; background-color: #333; position: absolute; left:'+pt[0]+'%; top:'+pt[1]+'%;"></div>'
var pt_html = `<div id="calibration-point" style="width:${trial.point_size}px; height:${trial.point_size}px; border-radius:${trial.point_size}px; border: 1px solid #000; background-color: #333; position: absolute; left:${pt[0]}%; top:${pt[1]}%;"></div>`
wg_container.innerHTML = pt_html;
var pt_dom = wg_container.querySelector('#calibration-point');

View File

@ -28,6 +28,16 @@ jsPsych.plugins["webgazer-init-camera"] = (function() {
plugin.trial = function (display_element, trial) {
if (!jsPsych.extensions.webgazer.isInitialized()) {
jsPsych.extensions.webgazer.start().then(function () {
showTrial();
}).catch(function () {
display_element.innerHTML = `<p>The experiment cannot continue because the eye tracker failed to start.</p>
<p>This may be because of a technical problem or because you did not grant permission for the page to use your camera.</p>`
});
}
function showTrial() {
var html = `
<div id='webgazer-init-container' style='position: relative; width:100vw; height:100vh'>
</div>`
@ -57,6 +67,7 @@ jsPsych.plugins["webgazer-init-camera"] = (function() {
observer.disconnect();
end_trial();
});
}
function face_detect_event_observer(mutationsList, observer) {
if (mutationsList[0].target == document.querySelector('#webgazerFaceFeedbackBox')) {

View File

@ -37,7 +37,7 @@ jsPsych.plugins["webgazer-validate"] = (function() {
},
point_size:{
type: jsPsych.plugins.parameterType.INT,
default: 10
default: 20
},
show_validation_data: {
type: jsPsych.plugins.parameterType.BOOL,