mne-tools / mne-python

MNE: Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python
https://mne.tools
BSD 3-Clause "New" or "Revised" License
2.66k stars 1.31k forks source link

snirf import - source or detector position #10975

Open jeanbaptiste-billaud opened 2 years ago

jeanbaptiste-billaud commented 2 years ago

Describe the bug

The import of snirf data from an Artinis-portalite device cause an assert error. The device have 3 sources but only one detector which leads to a 2D position format error.

Expected results

in RawSNIRF class code:

detPos2D = np.array(dat.get('nirs/probe/detectorPos2D'))

shape of detPos2D:

detPos2D.shape --> (2,)
len(detPos2D.shape) --> 1

Solution

1) for oxy4 file directly converted in snirf : correction of detPos2D to be able to create detPos3D

detPos2D = np.array(dat.get('nirs/probe/detectorPos2D'))
if len(detPos2D.shape) == 1:
> detPos2D = detPos2D.reshape(len(detectors), 2)

2) for oxy4 file converted in nirs then in snirf : correction of detPos3D

if len(detectors) != detPos3D.shape[0]:
> detPos3D = detPos3D.T   
> assert len(detectors) == detPos3D.shape[0]
welcome[bot] commented 2 years ago

Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

drammock commented 2 years ago

ping @rob-luke, our fNIRS expert

rob-luke commented 2 years ago

Thanks for submitting this issue @Helianthus89. Sorry for the delayed response.

I have not used the Artinis-portalite before, but if the file is in snirf format we should (in theory) be able to read it. Can you share an example file with me and I can try and debug what the issue is?

jeanbaptiste-billaud commented 2 years ago

@rob-luke , here you'll find a file in both format oxy4 and snirf, and the optode template to be able to convert oxy4 to an other format. I also include my modified version of the mne import function.

portalite_mne.zip

rob-luke commented 1 year ago

Sorry for the delay getting back to you @Helianthus89

The file you provided is not a valid SNIRF file. I tested this by using the SNIRF validator from the Boston University Neurophotonics Center.

In [1]: import snirf

In [2]: v = snirf.validateSnirf("/Users/rluke/Downloads/portalite_mne/portalite_record.snirf")

In [3]: v
Out[3]: <snirf.pysnirf2.ValidationResult object at 0x10877d820> is_valid False

There are a number of errors with the file. It would be great if you could ask the manufacturer to ensure their files are valid using the official validator.

However, we can try and make it so you can load the file in MNE. I have managed to get the files to load, and I can submit this fix as a pull request to MNE. But first, can you help me understand how the portalite system works—how do you tell the software where the source and detector optodes are? Or does that not matter? Can you share with me where the sources and detectors were placed for the file you shared?

Thanks, Rob

rob-luke commented 1 year ago

And for anyone reading along, a fix seems to be to edit line 203 of the snirf.py to include

            # Fix issue when there is only one optode
            if detPos3D.shape == (3, 1):
                detPos3D = detPos3D.T
            if srcPos3D.shape == (3, 1):
                srcPos3D = srcPos3D.T
            assert len(sources) == srcPos3D.shape[0]
            assert len(detectors) == detPos3D.shape[0]
jeanbaptiste-billaud commented 1 year ago

@rob-luke,

I'm really not surprise by the issue that you get from the snirf file. Artinis is my 1st experience on fnirs, until now i almost work on MRI and a little bit on EEG. In any case, both modalities have common file format accross manufacturers. In the present case, Artinis use a proprietary file format, the oxy file, and the only tool for convert oxy file in an other format is the toolbox oxysoft2matlab, that's probably out of date (https://github.com/jayd1860/oxysoft2matlab). I also try to use the oxyfile MNE reader, avaible on the website of Artinis, without success despite some email exchanges with their support.

I can't tell you how the portalite system work, data was acquiered before my recruitment, but i guess that's work with the same way that other Artinis devices.

However, in the cas of portalite, there's not 3D optodes position, only 1D because there's 3 sources and 1 detector with a distance of 30, 35, and 40 mm, lined up on the same axis.

rob-luke commented 1 year ago

Thanks for the feedback @Helianthus89 Can I use the snirf file you shared as test data for MNE? Then I can submit a fix for the bug you found where there is only 1 source or detector.

We will also need to find a way to allow you to specify the S-D distance manually (for beer-lambert law), as you don't have accurate spatial information in the file. Once the fix for the data input is merged I can tackle this change to the API for beer lambert.

larsoner commented 1 year ago

We will also need to find a way to allow you to specify the S-D distance manually (for beer-lambert law), as you don't have accurate spatial information in the file. Once the fix for the data input is merged I can tackle this change to the API for beer lambert.

To me the right fix is to create and apply a montage with S* and D* channels that is as close to reality as possible. Then you get a lot of stuff for free (e.g., all sensor plots become reasonable instead of potentially nonsensical) in addition to having things set up properly for beer-lambert

jeanbaptiste-billaud commented 1 year ago

@rob-luke, of course you can use data-files i shared here, data are anonymised so it's ok. I'm not sur to well understand the last part of your post. After adapted the import function to be able to transform 1D to 3D optodes positions, i was able to process my data like any other nirs data (OD then BL). Do you think that the lack of precision about optodes location could be induce a wrong Beer-Lambert conversion ?

HanBnrd commented 1 year ago

Hi @jeanbaptiste-billaud,

Just came across this issue by doing a search for Artinis support in MNE. Hopefully you managed to solve your issue already. I had issues with non-supported fNIRS devices in the past and used some workarounds that you can find here: https://github.com/HanBnrd/NIRSimple/blob/master/examples/simple_probe.ipynb. You will however lose optode positions in that process, but it can be useful in cases where you only care about source-detector distances and not positions.

Hope that helps, good luck!