NIRSimple example

Download this notebook

[1]:
import scipy.io
import numpy as np
import nirsimple as ns

The data used in this example is from the Homer MATLAB toolbox tutorial and can be downloaded here.

Download the zip archive and extract it. In this example we are just going to use Simple_Probe.nirs which is in the Example1_Simple_Probe folder of the archive.

Load data from the file

Load the file as a MATLAB file:

[2]:
file_path = "./Simple_Probe.nirs"  # replace by the path to Simple_Probe.nirs
simple_probe = scipy.io.loadmat(file_path)

Get fNIRS light intensities as a numpy array

Get signals from Simple Probe:

[3]:
intensities = np.swapaxes(simple_probe['d'], 0, 1)
print("Intensity data shape: {}".format(intensities.shape))
print("  number of channels: {}".format(intensities.shape[0]))
print("  number of time points: {}".format(intensities.shape[1]))
Intensity data shape: (8, 1200)
  number of channels: 8
  number of time points: 1200

Convert light intensities to optical density changes

Get optical density changes relative to the average intensity of each channel:

[4]:
dod = ns.intensities_to_od_changes(intensities)
print("Delta OD shape: {}".format(dod.shape))
Delta OD shape: (8, 1200)

Get channel info as lists

Get channel names from Simple Probe:

[5]:
channels = simple_probe['SD']['MeasList'][0, 0][:, :2].tolist()
raw_ch_names = [str(ch).replace('[', '').replace(']', '').replace(', ', '-')
                for ch in channels]
print("Channel names: {}".format(raw_ch_names))
Channel names: ['1-1', '1-2', '1-3', '1-4', '1-1', '1-2', '1-3', '1-4']

Get channel wavelengths from Simple Probe:

[6]:
wavelengths = simple_probe['SD']['Lambda'][0, 0][0].tolist()
ch_high_wl = [wavelengths[0] for _ in range(int(len(raw_ch_names)/2))]
ch_low_wl = [wavelengths[1] for _ in range(int(len(raw_ch_names)/2))]
ch_wl = ch_high_wl + ch_low_wl
print("Channel wavelengths: {}".format(ch_wl))
Channel wavelengths: [830, 830, 830, 830, 690, 690, 690, 690]

Define the differential pathlengths factor (DPF) for each channel:

[7]:
unique_dpf = 6
ch_dpf = [unique_dpf for _ in enumerate(raw_ch_names)]
print("Channel DPFs: {}".format(ch_dpf))
Channel DPFs: [6, 6, 6, 6, 6, 6, 6, 6]

Define source-detector distance for each channel:

[8]:
unique_distance = 2.8  # cm
ch_distances = [unique_distance for _ in enumerate(raw_ch_names)]
print("Channel distances: {}".format(ch_distances))
Channel distances: [2.8, 2.8, 2.8, 2.8, 2.8, 2.8, 2.8, 2.8]

Convert optical density changes to concentration changes

Get oxygenated and deoxygenated hemoglobin concentration changes (HbO and HbR) with the modified Beer-Lambert law (from Delpy et al., 1988):

[9]:
data = ns.mbll(dod, raw_ch_names, ch_wl, ch_dpf, ch_distances,
               unit='cm', table='wray')
dc, ch_names, ch_types = data
print("Delta HbO and HbR shape: {}".format(dc.shape))
print("Channel names: {}".format(ch_names))
print("Channel types: {}".format(ch_types))
Delta HbO and HbR shape: (8, 1200)
Channel names: ['1-1', '1-1', '1-2', '1-2', '1-3', '1-3', '1-4', '1-4']
Channel types: ['hbo', 'hbr', 'hbo', 'hbr', 'hbo', 'hbr', 'hbo', 'hbr']

Plot data with MNE

[10]:
import mne
[11]:
S_FREQ = 5  # sampling frequency in Hz

Plot data with MNE:

[12]:
mne_ch_names = [ch + ' ' + ch_types[i] for i, ch in enumerate(ch_names)]
print("MNE channel names: {}".format(mne_ch_names))
info = mne.create_info(ch_names=mne_ch_names, sfreq=S_FREQ,
                       ch_types=ch_types)
raw = mne.io.RawArray(dc, info)
graph = raw.plot(scalings=0.5e-6, duration=50)
MNE channel names: ['1-1 hbo', '1-1 hbr', '1-2 hbo', '1-2 hbr', '1-3 hbo', '1-3 hbr', '1-4 hbo', '1-4 hbr']
Creating RawArray with float64 data, n_channels=8, n_times=1200
    Range : 0 ... 1199 =      0.000 ...   239.800 secs
Ready.
../_images/examples_simple-probe_31_1.png

Signal correction

Apply correlation based signal improvement (from Cui at al., 2010) to hemoglobin concentration changes:

[13]:
data = ns.cbsi(dc, ch_names, ch_types)
dc_0, ch_names_0, ch_types_0 = data
print("Delta HbO_0 and HbR_0 shape: {}".format(dc_0.shape))
print(ch_names_0)
Delta HbO_0 and HbR_0 shape: (8, 1200)
['1-1', '1-1', '1-2', '1-2', '1-3', '1-3', '1-4', '1-4']

Plot corrected data with MNE

[14]:
mne_ch_names_0 = [ch + ' ' + ch_types_0[i] + '_0'
                  for i, ch in enumerate(ch_names_0)]
print("MNE channel names: {}".format(mne_ch_names_0))
info_0 = mne.create_info(ch_names=mne_ch_names_0, sfreq=S_FREQ,
                         ch_types=ch_types_0)
raw_0 = mne.io.RawArray(dc_0, info_0)
graph_0 = raw_0.plot(scalings=0.5e-6, duration=50)
MNE channel names: ['1-1 hbo_0', '1-1 hbr_0', '1-2 hbo_0', '1-2 hbr_0', '1-3 hbo_0', '1-3 hbr_0', '1-4 hbo_0', '1-4 hbr_0']
Creating RawArray with float64 data, n_channels=8, n_times=1200
    Range : 0 ... 1199 =      0.000 ...   239.800 secs
Ready.
../_images/examples_simple-probe_36_1.png