1

I used MATLAB R2011b and EEGLab 13.4.3b to load a Biosemi .bdf file (EEG recording) that includes several spurious triggers. The position of triggers is stored in header.BDF.Trigger.POS and the trigger type is in header.BDF.Trigger.TYP.

After loading the file with:

header = sopen('05-AM-200-Deci.bdf','r',[1:40],'OVERFLOWDETECTION:OFF'); 

I made transformations to header.BDF.Trigger.POS and header.BDF.Trigger.TYP vectors and replaced them.

NOTE: These transformations shouldn't make a difference because even when I only load the bdf file and then try to save it back I get exactly the same errors.

Here's the structure of header (extracted with get()):

header = 

    FileName: '05-AM-200-Deci.bdf'
        FILE: [1x1 struct]
        TYPE: 'BDF'
      ErrNum: [1025 0]
      ErrMsg: ''
     keycode: [1x34 double]
          NS: 41
  SampleRate: 1
          T0: [2015 7 6 17 41 44]
      Filter: [1x1 struct]
        FLAG: [1x1 struct]
       EVENT: [1x1 struct]
     VERSION: -1
     Patient: [1x1 struct]
         PID: '05-AM-200'
         RID: '05-AM-200'
     HeadLen: 10752
   reserved1: '24BIT                                       '
        NRec: 1207
         Dur: 1
          AS: [1x1 struct]
       Label: {41x1 cell}
  Transducer: {41x1 cell}
     PhysDim: {41x1 cell}
     PhysMin: [1x41 double]
     PhysMax: [1x41 double]
      DigMin: [1x41 double]
      DigMax: [1x41 double]
     PreFilt: [41x80 char]
      GDFTYP: [1x41 double]
         SPR: 1
   THRESHOLD: [41x2 double]
         Cal: [1x41 double]
         Off: [1x41 double]
       Calib: [41x40 double]
         BDF: [1x1 struct]
 PhysDimCode: [41x1 double]
        ELEC: [1x1 struct]
  LeadIdCode: [41x1 double]
     CHANTYP: '                                         '
InChanSelect: [40x1 double]

I tried using EEGLab functions to save the transformed file (header) to no avail. I know how to save a bdf file in EEGLab using GUI. However, using GUI doesn't allow for a transformation that I need.

In order to save the file I tried using pop_writeeeg() and swrite() but none of them worked.

I used the following commands:

  1. First take:

    `pop_writeeeg(header);`
    

    Returns:

    Reference to non-existent field 'trials'.  
    Error in pop_writeeeg (line 38)
        if EEG.trials > 1
    
  2. Then I tried:

    `pop_writeeeg(header, '05-new', 'TYPE', 'BDF');`
    

    Which returned:

    Reference to non-existent field 'chanlocs'.
    Error in pop_writeeeg (line 66)
    if ~isempty(EEG.chanlocs)
    
  3. My next take was to use swrite() the following way:

    `swrite(header, '05-new');`
    

    Which returned:

    Error SWRITE can not be applied, File 05-AM-200-Deci.bdf is not opened in WRITE mode
    

I'm pretty sure I'm missing something simple here.

Does anyone know how to save transformed EEG (bdf) data back to a bdf file?

epo3
  • 2,991
  • 2
  • 33
  • 60
  • Please include the [link](ftp://sccn.ucsd.edu/pub/daily/) to EEGLAB releases to help people troubleshoot your problem and the link to the .bdf file that is creating you problems together with the code that applies the transformation. Without this info is practically impossible to help you. – Oleg Apr 11 '16 at 14:08
  • You cannot save just the header, but need to include the data. In fact the syntaxes are `ssave(HDR,data)` or `swrite(HDR,data)` – Oleg Apr 11 '16 at 14:20
  • I've added the link to EEGLab and will add the bdf file tonight. I assumed that data is part of the header in bdf files. Isn't that the case? – epo3 Apr 11 '16 at 15:09
  • 1
    No, usually any dataset format will have a header that describes the data, and the actual data. In your case the `eeg` dataset can be a single variable which should contain a header and the data. – Oleg Apr 11 '16 at 15:50
  • Going through the docs, `HDR = sopen(HDR, 'w');` allows you to write the header and also refers to some `demo3.m` file. You might want to read the help of the files and this demo. – Oleg Apr 11 '16 at 15:54
  • @Oleg I tried using `HDR = sopen(HDR, 'w');` but it seems like it can only write a structure of a bdf file. I had a look at [demo3.m](https://github.com/donnchadh/biosig/blob/master/biosig/demo/demo3.m) but it only describes the structure of a bdf file. The transformation doesn't make a difference because even without it I get the same errors. I updated the question with the bdf file used (although downsampled to 128 Hz to reduce size). – epo3 Apr 11 '16 at 20:22

1 Answers1

1

The EEGLAB toolbox does not include the functions you need; you want the Biosig toolbox (it is a plugin for EEGLAB). The paradigm of the Biosig functions is unusual. It uses a data structure with the file handle in the structure. So, to open a file and then save your modified data, you need to do the following steps:

1) Open the BDF file and read in the data. This step retrieves file metadata and creates a structure of the metadata (as shown in the question).

[raw_data,meta_data] = sload('05-AM-200-Deci.bdf',[1:40],'OVERFLOWDETECTION:OFF');

2) Even if the transformations only involve metadata, you have to re-write the file with the raw data & metadata. Your data must be in a variable with the size Rows x Number_of_Channels. Depending on the features of the .bdf file, you may have to add an additional column which represents the Status channel.

% do the transforms here. If it does not affect the raw data, no need to change variables.
transformed_data = raw_data; % transformed data to be saved to a file
meta_data.fZ = zeros([1 meta_data.NS]);
transformed_data(:,meta_data.NS)=0;

3) Open the file for writing WARNING: THIS WILL ERASE THE FILE ON DISK

meta_data = sopen(meta_data,'w');

4) Save your data to the file:

swrite(meta_data,transformed_data);

5) Close the file:

sclose(meta_data);

I tested this using the test files available here. The BDF format is old and has many options, so there is an extra step in this script to fill in the "Status" channel (channel 17), in order to make it work with the Biosig functions.

[s,hdr] = sload('Newtest17-2048.bdf');     % get the raw data from the file
hdr.fZ = zeros([1 hdr.NS]); s(:,hdr.NS)=0; % (create "missing" channel values)
hdr = sopen(hdr,'w');                      % open the file for writing (ERASES FILE!)
swrite(hdr,s);                             % write data to the file
sclose(hdr);                               % close the file

The hack for the "missing channel" is also required for the .bdf file linked in the question.

mhopeng
  • 1,063
  • 1
  • 7
  • 17
  • Thanks for pointing out that `sclose()` should be used. I tried loading the bdf file (link in the question) into both `myFileRef` and `transformed_data` (1., 2., and 3.). 4) returned error `Warning SOPEN: HDR.PhysDimCode of the following channel(s) is(are) not defined: 41 Error SOPEN (GDF/EDF/BDF)-W: PhysMin(41) does not fit into header` but it erased the file. 5) Returned `Undefined function 'minus' for input arguments of type 'struct'. Error in swrite (line 38) data = data - repmat(HDR.PhysMin(:)',size(data,1),1);` – epo3 Apr 11 '16 at 21:17
  • The example script I give above works for me with your sample file, *if* you include the "make it work" step of adding some filler rows for the extra channel, like in the example. Replace the test file name with your file and use 41 instead of 17 (extra channel) and see if it works for you. I still see the error about PhysMin(41), but the data gets written to the file. – mhopeng Apr 12 '16 at 04:31
  • I've done exactly what you recommended yesterday and got the error. I don't think the file was saved (but will check tonight). – epo3 Apr 12 '16 at 08:08
  • See if it works on your system with the sample file from [biosemi](http://www.biosemi.com/faq/file_format.htm), as well. – mhopeng Apr 12 '16 at 16:16
  • Is `sload()` necessary? You didn't include it in the first part of the code. The code you provided works (although it shows errors) with the bdf file from Biosemi's website (but saves an empty file). When I tried running the Biosemi code on another machine I got an [error](https://sourceforge.net/p/biosig/mailman/message/32879970/) :/ – epo3 Apr 13 '16 at 22:18
  • Good point; the `sload()` is included in Step 2, but I did not describe it explicity. I will edit the answer. – mhopeng Apr 15 '16 at 00:22
  • While editing the answer I realized that `sload()` duplicates the functionality of `sopen()`. When you load the raw data using `sload()`, you also get a metadata structure that you can use with `swrite()`. The answer has been updated to reflect this. – mhopeng Apr 15 '16 at 00:48