Skip to content

BeMoBIL BIDS tools : XDF2BIDS

sjeung edited this page May 28, 2022 · 21 revisions

Dependencies

  • FieldTrip
    data2bids version on master branch after 27.05.2022 is required.

Overview

For detailed description of the output structure, please refer to BIDS extension proposal for Motion data (BEP 029). The output can be validated using a bids-validator version in development.

Example data set

Here we have an example data set from a dual task experiment, where participants performed a visual discrimination task while walking.

source-data/
     vp_24/
         vp_24_walk.xdf  

In the xdf file, you will find eeg and motion data as separate streams. To explore the structure of your xdf file, one can do the following after adding their bemobil pipeline and fieldtrip to Matlab path.

ftPath = fileparts(which('ft_defaults'));
addpath(fullfile(ftPath, 'external','xdf')); 

% file name 
xdfFilePath                 = 'source-data\vp_24\vp_24_walk.xdf';

% load xdf streams 
xdfStreams                  = load_xdf(xdfFilePath); 
nStreams                    = numel(xdfStreams); 

% list all channel names per stream
for iStream = 1:nStreams
    xdfStreams{iStream}.info.name
    if isfield(xdfStreams{iStream}.info.desc, 'channels') % some streams do not have channels
        cellfun(@(x) x.label, xdfStreams{iStream}.info.desc.channels.channel, 'UniformOutput', false)'
    end
end

This will display the stream names and channel names contained in each stream like below. There are 9 streams, that track 9 different body parts. As each stream has its own sample number and time stamps, they are considered to be 9 different tracking systems in BIDS-Motion.

ans =

'PhaseSpace_Rigid1'


ans =

  8×1 cell array

    {'Rigid1_X'   }
    {'Rigid1_Y'   }
    {'Rigid1_Z'   }
    {'Rigid1_A'   }
    {'Rigid1_B'   }
    {'Rigid1_C'   }
    {'Rigid1_D'   }
    {'Rigid1_Conf'}

After conversion to BIDS, the data in the xdf file will be stored like the following. Now the EEG and motion data that was in a single xdf file are separated into different files in their own BIDS formats (metadata files omitted in this example). Motion data are separately saved as 9 different tracking systems.

1_BIDS-data/
    sub-24/
        eeg/
           sub-24_task-VisualDiscrimination_eeg.eeg
        motion/
           sub-24_task-VisualDiscrimination_tracksys-PhaseSpaceHead_motion.tsv 
           sub-24_task-VisualDiscrimination_tracksys-PhaseSpaceLeftForeFoot_motion.tsv 
                 ...
           sub-24_task-VisualDiscrimination_tracksys-PhaseSpaceRightAnkle_motion.tsv 

Usage 1. "Express", or "import-only"

This part is looped over each xdf file. The configuration input serves to inform bemobil_xdf2bids function of how to find streams and channels correctly. Note how some of the fields take their values from metadata fields in xdf. The comments specify where to find the strings to enter in these fields.

config                        = []; 
config.filename               = '...source-data\vp-64'\vp_24_walk.xdf';  % required, string, full path to the xdf file 
config.bids_target_folder     = '...\BIDS-data';                    % required, string, bids target folder to be created
config.subject                = 24;                                 % required, subject numerical ID
config.task                   = 'VisualDiscrimination';             % optional, string, task name, default value 'defaultTask'

config.eeg.stream_name        = 'BrainVision';                      % required, string, a unique keyword in EEG stream to be searched for

For different tracking systems, this part is looped over.

% cell array of tracking system names to be assigned to the streams
bidsNames                     =  { 'PhaseSpaceHead', 'PhaseSpaceLeftThigh', 'PhaseSpaceLeftLowerLeg','PhaseSpaceLeftAncle', 'PhaseSpaceLeftForeFoot', 'PhaseSpaceRightThigh','PhaseSpaceRightLowerLeg', 'PhaseSpaceRightAncle', 'PhaseSpaceRightForeFoot'};

for iMotionStream = 1:9
    config.motion.streams{iMotionStream}.xdfname = ['PhaseSpace_Rigid' num2str(iMotionStream)]; % required, keyword in stream name, searched for in field "xdfdata{streamIndex}.info.name"
    config.motion.streams{iMotionStream}.bidsname  = bidsNames{iMotionStream}; % optional, name to be assgined in BIDS file name key-value pair as tracking system name
    config.motion.streams{iMotionStream}.tracked_points = {['Rigid' num2str(iMotionStream)]}; % reuired, keyword in channel names, indicating which object (tracked point) is included in the stream
end

bemobil_xdf2bids(config); 

Usage 2. "Full BIDS" with metadata for data sharing

1. Specify general information about the data set

The next step is to specify general metadata about the data set. Every data set in BIDS must include this information about the authors, institution, and the task. For details please refer to BIDS page for modality agnostic files.

general_info = [];

% root directory (where you want your bids data to be saved)
general_info.bidsroot                                = fullfile(bemobil_config.study_folder, bemobil_config.bids_data_folder); 

% required for dataset_description.json
general_info.dataset_description.Name                = 'Walking task in the young and old';
general_info.dataset_description.BIDSVersion         = 'unofficial extension';

% optional for dataset_description.json
general_info.dataset_description.License             = 'n/a';
general_info.dataset_description.Authors             = 'JP & KG';
general_info.dataset_description.Acknowledgements    = 'Acknowledgements here';
general_info.dataset_description.Funding             = 'n/a';
general_info.dataset_description.ReferencesAndLinks  = 'n/a';
general_info.dataset_description.DatasetDOI          = 'n/a';

% general information shared across modality specific json files 
general_info.InstitutionName                         = 'Technische Universitaet zu Berlin';
general_info.InstitutionalDepartmentName             = 'Biological Psychology and Neuroergonomics';
general_info.InstitutionAddress                      = 'Strasse des 17. Juni 135, 10623, Berlin, Germany';
general_info.TaskDescription                         = 'Participants walked repeatedly on a straight path.';
general_info.task                                    = bemobil_config.bids_task_label;  

2. Specify eeg metadata

eeg_info.eeg.PowerLineFrequency                       = 50;                    
eeg_info.eeg.EEGReference                            = 'FCz';  

3. Specify motion metadata

% data type and acquisition label   
motion_info.acq                                     = 'PhS';  

% motion specific fields in json  
motion_info.motion.Manufacturer                     = 'PhaseSpace';  
motion_info.motion.ManufacturersModelName           = 'Impulse X2';  
motion_info.motion.RecordingType                    = 'continuous';  

4. Specify participant information

Participant information is recommended to be specified also following the description in BIDS page for modality agnostic files.

% here describe the fields in the participant file
% for numerical values  : 
%       subject_info.fields.[insert your field name here].Description    = 'describe what the field contains';
%       subject_info.fields.[insert your field name here].Unit           = 'write the unit of the quantity';
% for values with discrete levels :
%       subject_info.fields.[insert your field name here].Description    = 'describe what the field contains';
%       subject_info.fields.[insert your field name here].Levels.[insert the name of the first level] = 'describe  what the level means';
%       subject_info.fields.[insert your field name here].Levels.[insert the name of the Nth level]   = 'describe what the level means';
%--------------------------------------------------------------------------
subject_info.fields.age.Description          = 'age of the participant'; 
subject_info.fields.age.Unit                 = 'years'; 
subject_info.fields.sex.Description          = 'sex of the participant'; 
subject_info.fields.sex.Levels.M             = 'male'; 
subject_info.fields.sex.Levels.F             = 'female'; 
subject_info.fields.group.Description        = 'experiment group';
subject_info.fields.group.Levels.young       = 'younger participants under 40';
subject_info.fields.group.Levels.old         = 'older participants over 65';
subject_info.fields.handedness.Description    = 'handedness of the participant';
subject_info.fields.handedness.Levels.R       = 'right-handed';
subject_info.fields.handedness.Levels.L       = 'left-handed';

This part above is going to be used in writing a Json file for participant data. The role of the Json file is to describe the names and values of the variables that are considered to be "participant information". For generic fields such as age and sex, this kind of description may appear redundant, but the importance becomes obvious when we consider fields that are specific to the experiment, such as "group". Once you have identified and described all variables to be saved as participant information, you can start filling out the table below.

% names of the columns - 'nr' column is just the numerical IDs of subjects
%                         do not change the name of this column 
subject_info.cols = {'nr',   'age',  'sex',  'group',    'handedness'};
subject_info.data = {64,     71,     'F',    'old',      'R' ; ...
                   66,     67,     'M',    'old',      'R' ; ...
                   76,     34,     'M',    'young',    'R' ; ...
                   78,     33,     'M',    'young',    'R' };

Note that the first column of this table is reserved for numerical IDs of the participants. It is then followed by the names of variables you have described in subject_info.fields.

5. Loop over xdf files and fill out configuration for each file

6. Convert

Finally, you can now call function bemobil_xdf2bids.m with all of the structs above as inputs.

    bemobil_xdf2bids(config, general_metadata', general_info, 'motion_metadata', motion_info, 'eeg_metadata', eeg_info, 'participant_metadata', subject_info)

For other modalities than EEG and motion

One can import them as type "generic physiological data (PHYSIO)". This can be used to import any continuous recordings. The metadata is minimal and the channel names will be saved in the resulting _physio.json file in field "Columns". Because it can deal with generic data types, it doesn't do any type-specific processing.