Skip to content

Snirf Fill Parsing Incorrect Assertions #12429

@alexk101

Description

@alexk101

Description of the problem

I am working with snirf files produced from a NirX Sport 2 system. I have validated the files against the official specification using (pysnirf2)[https://github.com/BUNPC/pysnirf2]. It shows that these are valid snirf files according to the official protocol. When attempting to read these files into mne using read_raw_snirf, an assertion is failed that causes the parsers to fail early. I haven't found any additional documentation to explain why this file isn't capable of being read. Regardless, the parser shouldn't fail to read a file that meets the official specification without explanation as to why (ie missing features). I include below the validator result. Additionally, the snirf file is following version 1.1 of the spec.

from snirf import validateSnirf

target = '2024-01-19_007.snirf'
result = validateSnirf(target)

>>> Found 564 OK      (hidden)
>>> Found 704 INFO    (hidden)
>>> Found 0 WARNING
>>> Found 0 FATAL  

>>> File is VALID

Looking into this problem myself, it seems that the assertion of the number of detector positions and number of detectors in the channel data does not always hold true. Looking at my data, the number of detector positions is 15, while the number of detectors found in the channel data is 14, where D8 is missing. An explanation of why this is the case follows.

In our optode layout, we have bundles of sources and detectors, 8 each. In this layout, we are using 16 sources and 15 detectors. However, because the sources and detectors are in these bundles, there is one remaining detector that would otherwise hang off the cap. To make this less of an obstruction, that detector is placed on the side of the cap, but is not used to capture any signal information. For that reason, it exists in the detector position array, but not in the channels.

Steps to reproduce

from mne.io import read_raw_snirf

target = '2024-01-19_007.snirf'
raw_intensity = read_raw_snirf(target, optode_frame='mri')

Link to data

2024-01-19_007.snirf.zip

Expected results

A parsed snirf file as a RawSNIRF object.

Actual results

Traceback (most recent call last):
  File "/home/alexk101/Documents/Research/Bertenthal/fnirs/fnirs-nirx/src/process.py", line 83, in <module>
    process_all()
  File "/home/alexk101/Documents/Research/Bertenthal/fnirs/fnirs-nirx/src/process.py", line 78, in process_all
    sub_data[int(sub.stem)] = process_sub(snirf_file)
                              ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/alexk101/Documents/Research/Bertenthal/fnirs/fnirs-nirx/src/process.py", line 35, in process_sub
    raw_intensity = read_raw_snirf(target, optode_frame='mri')
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/alexk101/.mambaforge/envs/fnirs/lib/python3.11/site-packages/mne/io/snirf/_snirf.py", line 56, in read_raw_snirf
    return RawSNIRF(fname, optode_frame, preload, verbose)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-340>", line 12, in __init__
  File "/home/alexk101/.mambaforge/envs/fnirs/lib/python3.11/site-packages/mne/io/snirf/_snirf.py", line 226, in __init__
    assert len(detectors) == detPos3D.shape[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Additional information

This issue was originally filed under the mne-fnirs repo, but it seems that io is the responsibility of the main mne package. The old issue can be found here. I have discussed a correction for this with @larsoner and will attach the pull request shortly.

Platform             Linux-6.7.2-arch1-1-x86_64-with-glibc2.38
Python               3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 10:40:35) [GCC 12.3.0]
Executable           /home/alexk101/.mambaforge/envs/fnirs/bin/python
CPU                   (16 cores)
Memory               30.5 GB

Core
├☒ mne               1.6.0 (outdated, release 1.6.1 is available!)
├☑ numpy             1.26.2 (OpenBLAS 0.3.25 with 16 threads)
├☑ scipy             1.11.4
├☑ matplotlib        3.8.2 (backend=QtAgg)
├☑ pooch             1.8.0
└☑ jinja2            3.1.2

Numerical (optional)
├☑ sklearn           1.3.2
├☑ numba             0.58.1
├☑ nibabel           5.1.0
├☑ nilearn           0.10.2
├☑ dipy              1.7.0
├☑ openmeeg          2.5.7
├☑ cupy              12.2.0
└☑ pandas            2.1.3

Visualization (optional)
├☑ pyvista           0.42.3 (OpenGL 4.5.0 NVIDIA 545.29.06 via NVIDIA GeForce RTX 3070 Ti Laptop GPU/PCIe/SSE2)
├☑ pyvistaqt         0.11.0
├☑ vtk               9.2.6
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
├☑ qtpy              2.4.1 (PyQt5=5.15.8)
├☑ pyqtgraph         0.13.3
├☑ mne-qt-browser    0.6.1
├☑ ipywidgets        8.1.1
├☑ trame_client      2.13.0
├☑ trame_server      2.12.1
├☑ trame_vtk         2.6.2
├☑ trame_vuetify     2.3.1
└☐ unavailable       ipympl

Ecosystem (optional)
├☑ mne-nirs          0.6.0
└☐ unavailable       mne-bids, mne-features, mne-connectivity, mne-icalabel, mne-bids-pipeline

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions