bids-standard / pybv

A lightweight I/O utility for the BrainVision data format, written in Python.
https://pybv.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
21 stars 13 forks source link

Specify reference channel(s) #74

Closed hoechenberger closed 3 years ago

hoechenberger commented 3 years ago

Converting data from a proprietary format to BV with @dnacombo, we found ourselves in need of a way to specify the reference channel(s) used. It seems that currently, pybv doesn't support adding this information, implicitly declaring everything as referenced to a "common reference", if I'm not mistaken:

Screen Shot 2021-06-08 at 12 16 20

There should be a way to specify reference channels.

sappelhoff commented 3 years ago

Sounds like a good feature to support, PR welcome :)

hoechenberger commented 3 years ago

I'd be willing to give it a short, but how can we test whether the files we create are actually BV-compatible? I don't have BV analyzer to work with…

sappelhoff commented 3 years ago

I have a license for BV analyzer at one of the institute computers, so I could test some files in the near future.

Overall though I think it's more important that fieldtrip eeglab mne brainstorm etc can read it (given that we comply with the BV spec).

sappelhoff commented 3 years ago

coming here from my comment https://github.com/bids-standard/pybv/pull/75#discussion_r655194870 which was about:

implicitly declaring everything as referenced to a "common reference", if I'm not mistaken

I think this is poor wording in the BV spec. Based on my experience, the "common reference" does not imply a "common average reference", but rather a single reference channel that is used for all other channels. for Brain Products systems this is often FCz.

See my linked comment above where I post contents from a file that support this interpretation.

EDIT: This could also be clarified with an Email to Brain Products support (support@brainproducts.com) --> in the past I received quick replies (1-3 days in most cases) that were helpful.

sappelhoff commented 3 years ago

I sent an email, click details to see it:

Dear Brain Products support, we are maintaining an open source software in Python called pybv to write BrainVision files: https://github.com/bids-standard/pybv All our efforts are based on your specification of the format: https://www.brainproducts.com/productdetails.php?id=21&tab=5 Recently, Richard (in cc) wanted to extend the functionality of the pybv package to write each channel's reference channel in the VHDR file, see: https://github.com/bids-standard/pybv/pull/75 That extension (currently in process) produced some questions that we cannot answer, and that we would like to get your input on. Both of us are (or have been in the past) Brain Products customers through our institutes. --- We have some questions about the BrainVision specification, specifically, the "reference channel name" field in the channels section of the VHDR file, which looks like this: > `,[],[][,]` The "reference channel name" field must be a string and must be present, but it *may* be empty. If it is empty, the following applies: > `If is empty, then the common reference channel is used as the reference` Here are some of our questions: 1. What do you mean by "common reference channel" when "reference channel name" is empty? 2. Does it mean that each channel is referenced to the "common average reference" of all channels? 3. If the answer to question 2 is "yes", then there is a conflict, because my BrainAmp DC + BrainVision Recorder setup with a single reference at FCz also produces an empty "reference channel name" field (i.e., clearly not a common average reference) 4. Is the "reference channel name" field perhaps different depending on the Brain Products hardware that is being used? Practically, we need to know: - what an empty "reference channel name" truly refers to - if BrainVision Analyzer will produce any problems if we fill an arbitrary string into the "reference channel name" field, such as "MyReference2" (assuming that MyReference2 is not part of the data in the VHDR file) We are looking forward to your answers, and thanks in advance! Best regards, Stefan
sappelhoff commented 3 years ago

We got a reply :tada: and maybe another one will follow by the techsupport.

It looks like "it depends" on both the acquisition hardware (will need more information) & Software (BV recorder vs pycorder).

Currently it looks to me like we should say that an empty field means "all channels referenced to a common channel that is not further specified". WDYT @hoechenberger ?

Do you have an old pycorder file that contains info in the "comment" section that could be of interest to us?

Dear Stefan, Thanks for your e-mail and interest in our file format. You're right, some users fall in the trap of interpreting the term "common" as a "common average" referencing scheme. However, the term here implies that all channels use the same reference channel (that was used during online data acquisition). In your example this will be FCz for all channels. For the chosen online reference channel, the "reference channel name" field in the vhdr file generated via: - Recorder: is always empty. - PyCorder: is not empty and contains a label that'll be the same across all channels. The interpretation of this label is specified under the [Comment] section. For e.g: - if a single channel was used, it'll be displayed as "Reference channel: Ch1" or - if an average of two channels was used, it'll be displayed as "Reference channel: AVG(Ch1 + Ch2)". Besides the "reference channel name" field being empty or not, the interpretation of the used online reference channel depends on the hardware/amplifier, i.e., the distinction between unipolar vs bipolar recording scheme. For this, I would like to include our experts from the Technical Support team (in Cc) for further clarification. I hope this clarifies the doubts but please feel free to get back to us in case of any further questions.
sappelhoff commented 3 years ago

I sent a follow-up inquiry, so that we can proceed with implementing this feature.

Thank you, that is very helpful information. I think my remaining question is in how far BrainVision Analyzer makes use of the "reference channel name" field: 1. If it's empty: Does it "assume" some reference? Or does BV Analyzer treat that information as not available? 1. If it has a label, I can imagine two cases: 1. the label corresponds to a channel in the data: Does BV Analyzer then load the data as referenced to that channel? 1. the label does *not* correspond to a channel in the data: Does BV Analyzer then try to parse the [Comment] section and find a definition, as for example written by PyCorder (see your example from below)? Of course I'd be grateful for any further information that the technical support team may have. Best, Stefan
sappelhoff commented 3 years ago

We got two more answers. Based on them, I think we should proceed as follows:

First additional answer:

When importing data in Analyzer, the online reference channel is not imported as it would imply importing a zero channel. This channel only exists implicitly and it's also not listed in the vhdr file (i.e., [Channel Infos] section will contain #PhysicalChannel - #ReferenceChannel (usually 1)). Thus, as you've mentioned the data will be loaded in Analyzer as referenced to the online reference channel, irrespective of the "reference channel name" field being empty or not. It will also not be considered as "not available" even if the field is empty. However, Analyzer will extract the information stored in the vhdr file incl. the [Comment] section and display it in the Recording Info dialog (via right-clicking on the Raw Data and selecting the "Recording Infos" option form the context menu). In this way, users can get the information about the reference channel used during acquisition without opening the header file externally. In my previous example, the "Reference channel: AVG(Ch1 + Ch2)" will be displayed in the Recording Info dialog. To address how Analyzer uses the "reference channel name" field - it's when displaying the channel labels i.e., if the field is: - empty: channels will be displayed as (see left image). - not empty: channels will be displayed along with the reference channel label (see middle image). Another use-case will be if a user performs offline referencing (for e.g., a common average), provides a label to this new offline reference (e.g., AVG) and exports the data. In this case, Analyzer will fill the "reference channel name" field by this new label and if imported back to Analyzer, the channels will be displayed as (see right image). ![image](https://user-images.githubusercontent.com/9084751/122962772-081b0900-d386-11eb-9577-6b47f89a5aed.png)

Second additional answer:

the main thing to add from a technical perspective is that the reference scheme also differs depending on the EEG amplifier that is used: 1. actiCHamp: This amplifier allows to use different reference montages and therefore has the most options. -> When used with Recorder, the actiCHamp always operates with exactly one reference electrode that can be freely chosen from the available EEG channels. However, the is always empty, and the chosen reference is missing from the channel info list (in this example channel 2 was chosen as the reference channel) ![image](https://user-images.githubusercontent.com/9084751/122966313-77dec300-d389-11eb-9856-e04c30151944.png) Additionally, the reference channel is specified later in the .vhdr file ![image](https://user-images.githubusercontent.com/9084751/122966336-7e6d3a80-d389-11eb-93ad-bcf7a21f69b4.png) -> When used with PyCorder, more options are possible. As a preface to this, please note that the PyCorder is not developed anymore. The PyCorder + actiCHamp is the only combination currently where the can be filled in the .vhdr. Specifically, if one or more reference channels are selected in PyCorder, it is filled with REF and specified later in comments as described by Kidist in the last eMail. ![image](https://user-images.githubusercontent.com/9084751/122966368-86c57580-d389-11eb-844e-2c1cea96ff2a.png) ... ![image](https://user-images.githubusercontent.com/9084751/122966392-8dec8380-d389-11eb-8bc1-365b3baf6e79.png) or ![image](https://user-images.githubusercontent.com/9084751/122966424-9a70dc00-d389-11eb-9c1a-2b7aab1107b0.png) -> Only the PyCorder + actiCHamp combination can also used with no reference / reference-free acquisition. This basically uses the GND electrode signal as the reference (you can find more details about this in the actiCHamp manual on page 64). In this case the is also empty, but no channel is missing from the list and no reference channel is specified later. ![image](https://user-images.githubusercontent.com/9084751/122966475-aa88bb80-d389-11eb-9d5d-3eb9b4b94109.png) ... ![image](https://user-images.githubusercontent.com/9084751/122966494-af4d6f80-d389-11eb-803b-71a94a95c352.png) 2. BrainAmp, LiveAmp, QuickAmp (discontinued), V-Amp (discontinued) Different recording schemes exist for the different amplifiers: BrainAmp and LiveAmp can have both unipolar and bipolar electrodes. The QuickAmp always calculates an internal average reference. The V-Amp is unipolar but has 2 bipolar auxiliary channels. However, in all these cases, the reference electrodes are always fixed and cannot be changed. Hence, the is always empty: ![image](https://user-images.githubusercontent.com/9084751/122966516-b70d1400-d389-11eb-82f5-35272603da84.png)
cbrnr commented 3 years ago

Sounds good! We might also consider adding an option to add a zero-channel if the specified reference channel is not in the data (by default this should be False of course).

hoechenberger commented 3 years ago

I'm against adding channels automatically, if the user wants them, they should add them manually. They already need to pass the data as a numpy array, meaning they will have to know their way around numpy anyway... Actually, it's quite common that the ref channel is NOT included in the recording, so I'm not convinced that we should make the "non-standard" practice so easy 😅