Topographic visualisation is not well centred

I cannot understand why this figure (see image) is not well centred. Any thought?

This is the code that I used

grp_results = df.query("Condition in ['Tapping']")
grp_results = grp_results.query("Chroma in ['hbo']")
ch_model = smf.mixedlm("theta ~ -1 + ch_name:Chroma:subject",grp_results, groups=grp_results['ID']).fit(method='nm')


ch_model_df = statsmodels_to_results(ch_model)

plot_glm_group_topo(raw_haemo.copy().pick(picks="hbo"),
                    ch_model_df.query("subject in ['1']"),
                    colorbar=False, axes=axes[0, 0],
                    vmin=0, vmax=20, cmap=mpl.cm.Oranges)

plot_glm_group_topo(raw_haemo.copy().pick(picks="hbo"),
                    ch_model_df.query("subject in ['2']"),
                    colorbar=True, axes=axes[0, 1],
                    vmin=0, vmax=20, cmap=mpl.cm.Oranges)

grp_results = df.query("Condition in ['Tapping']")
grp_results = grp_results.query("Chroma in ['hbr']")

ch_model = smf.mixedlm("theta ~ -1 + ch_name:Chroma:subject",grp_results, groups=grp_results['ID']).fit(method='nm')


ch_model_df = statsmodels_to_results(ch_model)

plot_glm_group_topo(raw_haemo.copy().pick(picks="hbr"),
                    ch_model_df.query("subject in ['1']"),
                    colorbar=False, axes=axes[1, 0],
                    vmin=-10, vmax=0, cmap=mpl.cm.Blues_r)

plot_glm_group_topo(raw_haemo.copy().pick(picks="hbr"),
                    ch_model_df.query("subject in ['2']"),
                    colorbar=True, axes=axes[1, 1],
                    vmin=-10, vmax=0, cmap=mpl.cm.Blues_r)```


![cc|504x499](upload://nNEh5M6VUSl9JWjPL12QrEizrhi.png)

@alexrockhill gave me this suggestion:

Looks like maybe you’re channel positions are in the wrong coordinate frame. Maybe try mne.viz.plot_alignment or mne.viz.Brain and mne.viz.Brain.add_sensors (I made the second one so shameless plug :slight_smile: ). I would guess that your coordinates are in voxels (all positive) and in terms on head. Here’s the example I would follow: Importing data from fNIRS devices — MNE 1.0.0 documentation

So, I applied this code obtaining this image (see brain image). Not sure what this means and how I can fix the original topographic visualisation problem.

            'S3_D1 hbo', 'S3_D1 hbr', 'S4_D1 hbo', 'S4_D1 hbr',
            'S5_D2 hbo', 'S5_D2 hbr', 'S6_D2 hbo', 'S6_D2 hbr',
            'S7_D2 hbo', 'S7_D2 hbr', 'S8_D2 hbo', 'S8_D2 hbr']
ch_types = ['hbo', 'hbr', 'hbo', 'hbr',
            'hbo', 'hbr', 'hbo', 'hbr',
            'hbo', 'hbr', 'hbo', 'hbr',
            'hbo', 'hbr', 'hbo', 'hbr']
sfreq = 7.8

info = mne.create_info(ch_names=ch_names, ch_types=ch_types, sfreq=sfreq)

brain = mne.viz.Brain('fsaverage', subjects_dir='/Applications/freesurfer/subjects/', background='w', cortex='0.5')
brain.add_sensors(raw.info, trans='fsaverage',fnirs=['channels', 'pairs', 'sources', 'detectors'])
brain.show_view(azimuth=20, elevation=60, distance=400)```

I would try zooming out (scroll wheel on a mouse or pinch on a trackpad) you might see the sensors. How did you get your channel locations? It looks like there is an issue with their locations/coordinate frame.

yeah I found those (see image). For the channel location, I have a file probeInfo.mat generated directly from the acquisition. I imported data with mne.io.read_raw_nirx.

How can I fix the coordinate problem? Otherwise i cannot see properly any analysis result.

It looks like from the documentation that NIRScout and NIRSport devices are supported mne.io.read_raw_nirx — MNE 1.0.0 documentation. I’m not sure which device you’re using but it looks like you’ll need to adjust the coordinates. You may have to consult the device manufacturer to figure out which coordinate frame they are in and possibly align them with the MNE coregistration GUI mne.coreg.Coregistration — MNE 1.0.0 documentation

Hi,

thank you for your answer

I am attaching an example of data that I have with also the relative coordinates.

The experiment was conducted with the continuous-wave NIRS system (NIRSport 8 Ă— 8, Nirx Medical Technologies LLC, Berlin, Germany). It is a wearable, multi-channel, fNIRS device for the measurement of brain oxygenation. The fNIRS acquisition software was the NIRStar 14.2 (Version 14, Revision 2, Release Build, 2016-04-15 NIRx Medizintechnik GmbH, Berlin, Germany; www.nirx.net).

Could you help me to implement a way to convert this coordinates in MNE coordinates. Otherwise I can’t produce any figure from the results.

Thank you, I really appreciate your help,

(Attachment 2018-04-17_REALE_CTRL_0001.zip is missing)

the system didn’t allow me to share a zip directory with a data example. Is there a way to do that?

Ah, I looked back and you used subject='fsaverage' and trans='fsaverage' which should work if the fiducials are included in the montage but if not, then you’ll get something way off (this maybe should throw and error actually, I’ll have to check). Do you have fiducial points in your montage?

EDIT: I looked and it does not check that you have fiducials in the montage when you call trans='fsaverage', it just assumes your coordinates have been transformed to fsaverage.

You may want to come to an office hours to go over how do go coregistration.

1 Like

Dear @alexrockhill,

thank you for your answer,

actually in my data there are fiducials, this is an example (see screenshot).

Ok, I will come in the office hours, is the next one April 29 at 5 pm (UTC)?

Office hours are at 3 PM UTC, which is 5 PM CEST (Paris, Berlin, …).

Hey @mlrocca, I looked into this and it looks like it’s a problem with mne.io.read_raw_nirx since that’s the format of the data, it should just work without any modification (we shouldn’t have to use the probeInfo mat file). This isn’t really my area of expertise, I haven’t worked with this type of system before so maybe @rob-luke has a minute to help. @rob-luke, the data is posted on Discord on the office hours channel. It looks like for some reason the fiducials are being read as being very far from the sensors. I really don’t know much about this, but maybe it is just reading the first 3 of the 13 fiducial points posted above and those are very different than what MNE expects (MNE expects left and right pre-auricular points and nasion) so since there are 13 and not 3, I’m guessing these are very different things.

That’s a little odd that this is an issue at all since this is in a device manufacturer format that MNE supports, but even so things don’t always work out as perfectly as you’d hope with different setups across the world, sorry @mlrocca!

1 Like

I looked into the code a bit more and it assumes the positions in probeInfo.mat are in MNI coordinates. Here are some positions from some MNE sample data in NIRx format (the only step was converting to m):

array([[ 0.01484444, -0.1186096 , -0.03058643],
       [ 0.06703548, -0.0877003 , -0.00301732],
       [ 0.0849505 , -0.02859492,  0.02743647],
       [ 0.05828255,  0.04093365,  0.04528764],
       [ 0.00951329,  0.08760125,  0.01047327],
       [-0.00041748,  0.04718504,  0.0780284 ],
       [-0.03847298,  0.00863416,  0.08460399],
       [-0.07234127, -0.02961652,  0.06552235],
       [-0.07737436, -0.06410442,  0.02685754],
       [-0.06508504, -0.08662543, -0.025913  ]])

And here are your positions:

array([[0.16142004, 0.12078901, 0.23869662],
       [0.16190077, 0.15281011, 0.24067443],
       [0.17805025, 0.13694617, 0.23186901],
       [0.17564347, 0.10489866, 0.22882012],
       [0.18902322, 0.1196376 , 0.22042102],
       [0.1963126 , 0.10167294, 0.20434089],
       [0.17677754, 0.16762691, 0.22983916],
       [0.19067097, 0.15024533, 0.22068665],
       [0.20081685, 0.13287539, 0.20610075],
       [0.20493866, 0.11535151, 0.19094783],
       [0.09543835, 0.11853737, 0.23809364],
       [0.09205033, 0.15109726, 0.24079295],
       [0.07789328, 0.13440933, 0.23010027],
       [0.08324163, 0.10251256, 0.22770537],
       [0.06645596, 0.11696868, 0.21818607],
       [0.05848653, 0.09967643, 0.19999609],
       [0.07669894, 0.16724697, 0.22812119],
       [0.06353808, 0.15020131, 0.21844956],
       [0.05210999, 0.13223815, 0.20172453],
       [0.04942323, 0.11429096, 0.18430076]])

So clearly the coordinates that you got from the device have an offset and are not in MNI coordinates as the code says is expected. Here’s the part of the code @rob-luke wrote that reads in NIRx channel positions

# Read information about probe/montage/optodes
# A word on terminology used here:
#   Sources produce light
#   Detectors measure light
#   Sources and detectors are both called optodes
#   Each source - detector pair produces a channel
#   Channels are defined as the midpoint between source and detector
mat_data = loadmat(files['probeInfo.mat'])
probes = mat_data['probeInfo']['probes'][0, 0]
requested_channels = probes['index_c'][0, 0]
src_locs = probes['coords_s3'][0, 0] / 100.
det_locs = probes['coords_d3'][0, 0] / 100.
ch_locs = probes['coords_c3'][0, 0] / 100.

# These are all in MNI coordinates, so let's transform them to
# the Neuromag head coordinate frame
src_locs, det_locs, ch_locs, mri_head_t = _convert_fnirs_to_head(
    'fsaverage', 'mri', 'head', src_locs, det_locs, ch_locs)

So, if your coordinates are not in MNI, then we could guess at how to get them there but that’s really not ideal, we should have better positions. @mlrocca, do you know what coordinate frame the locations of your sensors are in? Is this somehow set in the device settings? It’s a bit odd that NIRx uses mat files since they allow any type of data and structure to the data, usually files from a device like this (e.g. edf or fif) have a well-specified structure (e.g. the first line is the sampling frequency, the next lines before a break are the channels type of thing) which in my opinion is a pretty good idea to avoid chaos, confusion and issues saving and loading the files like we have here where the program expects a particular structure but doesn’t get it. In summary, it seems like there is unfortunately a bit of a flawed design choice in the NIRx device structure that doesn’t specify exactly where things that you need in the recording should be and that’s causing the program to fail and requiring the experimenter to have to go back and find things (e.g. the transform to MNI) that they really shouldn’t have to do. Sorry it’s a bit of a mess!

1 Like

Hi both,

Sorry I’m a bit late to the conversation. MNE does not currently support reading files acquired with nirstar 14.2 (I think that version was superseded around 6 years ago).

We currently support reading version 15 and above because that is when they switched to using a standard coordinate system. Also they standardised various other aspects of their file formats.

I’d be pleased to review a PR that adds support for version 14 and earlier. But I can’t write it myself as it’s a a lot of work for a deprecated spec.

Instead I’d suggest coding the locations manually using set montage.

Hope this helps,
Rob

1 Like

“ MNE-Python only supports NIRx files recorded with NIRStar version 15.0 and above and Aurora version 2021 and above. MNE-Python supports reading data from NIRScout and NIRSport devices.” from Importing data from fNIRS devices — MNE 1.1.dev0 documentation

Another alternative you could try is using another software to convert your files to snirf and read those. I’d suggest homer3

2 Likes

Hmm, not sure why it says that the file is version 15.0 and reads properly using the MNE function… that’s a bit odd, and it would have been helpful to for @mlrocca to get that version error rather than an improper alignment.

1 Like

The reader does throw an error when you read a file from an earlier unsupported version. See…

I’ve tested that the warning is thrown on version 14 files that I have.

1 Like