コード例 #1
0
def test_fnirs_channel_naming_and_order_custom_optical_density():
    """Ensure fNIRS channel checking on manually created data."""
    data = np.random.normal(size=(6, 10))

    # Start with a correctly named raw intensity dataset
    # These are the steps required to build an fNIRS Raw object from scratch
    ch_names = ['S1_D1 760', 'S1_D1 850', 'S2_D1 760', 'S2_D1 850',
                'S3_D1 760', 'S3_D1 850']
    ch_types = np.repeat("fnirs_od", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.tile([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f

    freqs = np.unique(_channel_frequencies(raw))
    picks = _check_channels_ordered(raw, freqs)
    assert len(picks) == len(raw.ch_names)
    assert len(picks) == 6

    # Check block naming for optical density
    ch_names = ['S1_D1 760', 'S2_D1 760', 'S3_D1 760',
                'S1_D1 850', 'S2_D1 850', 'S3_D1 850']
    ch_types = np.repeat("fnirs_od", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.repeat([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f
    with pytest.raises(ValueError, match='channels not ordered correctly'):
        _check_channels_ordered(raw, [760, 850])
    # and this is how you would fix the ordering, then it should pass
    raw.pick(picks=[0, 3, 1, 4, 2, 5])
    _check_channels_ordered(raw, [760, 850])
コード例 #2
0
def test_fnirs_channel_naming_and_order_readers(fname):
    """Ensure fNIRS channel checking on standard readers."""
    # fNIRS data requires specific channel naming and ordering.

    # All standard readers should pass tests
    raw = read_raw_nirx(fname)
    freqs = np.unique(_channel_frequencies(raw.info))
    assert_array_equal(freqs, [760, 850])
    chroma = np.unique(_channel_chromophore(raw.info))
    assert len(chroma) == 0

    picks = _check_channels_ordered(raw.info, freqs)
    assert len(picks) == len(raw.ch_names)  # as all fNIRS only data

    # Check that dropped channels are detected
    # For each source detector pair there must be two channels,
    # removing one should throw an error.
    raw_dropped = raw.copy().drop_channels(raw.ch_names[4])
    with pytest.raises(ValueError, match='not ordered correctly'):
        _check_channels_ordered(raw_dropped.info, freqs)

    # The ordering must be increasing for the pairs, if provided
    raw_names_reversed = raw.copy().ch_names
    raw_names_reversed.reverse()
    raw_reversed = raw.copy().pick_channels(raw_names_reversed, ordered=True)
    with pytest.raises(ValueError, match='The frequencies.*sorted.*'):
        _check_channels_ordered(raw_reversed.info, [850, 760])
    # So if we flip the second argument it should pass again
    picks = _check_channels_ordered(raw_reversed.info, freqs)
    got_first = set(raw_reversed.ch_names[pick].split()[1]
                    for pick in picks[::2])
    assert got_first == {'760'}
    got_second = set(raw_reversed.ch_names[pick].split()[1]
                     for pick in picks[1::2])
    assert got_second == {'850'}

    # Check on OD data
    raw = optical_density(raw)
    freqs = np.unique(_channel_frequencies(raw.info))
    assert_array_equal(freqs, [760, 850])
    chroma = np.unique(_channel_chromophore(raw.info))
    assert len(chroma) == 0
    picks = _check_channels_ordered(raw.info, freqs)
    assert len(picks) == len(raw.ch_names)  # as all fNIRS only data

    # Check on haemoglobin data
    raw = beer_lambert_law(raw)
    freqs = np.unique(_channel_frequencies(raw.info))
    assert len(freqs) == 0
    assert len(_channel_chromophore(raw.info)) == len(raw.ch_names)
    chroma = np.unique(_channel_chromophore(raw.info))
    assert_array_equal(chroma, ["hbo", "hbr"])
    picks = _check_channels_ordered(raw.info, chroma)
    assert len(picks) == len(raw.ch_names)
    with pytest.raises(ValueError, match='chromophore in info'):
        _check_channels_ordered(raw.info, ["hbr", "hbo"])
コード例 #3
0
def _interpolate_bads_nirs(inst, method='nearest', verbose=None):
    """Interpolate bad nirs channels. Simply replaces by closest non bad.

    Parameters
    ----------
    inst : mne.io.Raw, mne.Epochs or mne.Evoked
        The data to interpolate. Must be preloaded.
    method : str
        Only the method 'nearest' is currently available. This method replaces
        each bad channel with the nearest non bad channel.
    %(verbose)s
    """
    from scipy.spatial.distance import pdist, squareform
    from mne.preprocessing.nirs import _channel_frequencies,\
        _check_channels_ordered

    # Returns pick of all nirs and ensures channels are correctly ordered
    freqs = np.unique(_channel_frequencies(inst))
    picks_nirs = _check_channels_ordered(inst, freqs)
    if len(picks_nirs) == 0:
        return

    nirs_ch_names = [inst.info['ch_names'][p] for p in picks_nirs]
    bads_nirs = [ch for ch in inst.info['bads'] if ch in nirs_ch_names]
    if len(bads_nirs) == 0:
        return
    picks_bad = pick_channels(inst.info['ch_names'], bads_nirs, exclude=[])
    bads_mask = [p in picks_bad for p in picks_nirs]

    chs = [inst.info['chs'][i] for i in picks_nirs]
    locs3d = np.array([ch['loc'][:3] for ch in chs])

    _check_option('fnirs_method', method, ['nearest'])

    if method == 'nearest':

        dist = pdist(locs3d)
        dist = squareform(dist)

        for bad in picks_bad:
            dists_to_bad = dist[bad]
            # Ignore distances to self
            dists_to_bad[dists_to_bad == 0] = np.inf
            # Ignore distances to other bad channels
            dists_to_bad[bads_mask] = np.inf
            # Find closest remaining channels for same frequency
            closest_idx = np.argmin(dists_to_bad) + (bad % 2)
            inst._data[bad] = inst._data[closest_idx]

        inst.info['bads'] = []

    return inst
コード例 #4
0
def test_fnirs_channel_naming_and_order_custom_optical_density():
    """Ensure fNIRS channel checking on manually created data."""
    data = np.random.normal(size=(6, 10))

    # Start with a correctly named raw intensity dataset
    # These are the steps required to build an fNIRS Raw object from scratch
    ch_names = [
        'S1_D1 760', 'S1_D1 850', 'S2_D1 760', 'S2_D1 850', 'S3_D1 760',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_od", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.tile([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f

    freqs = np.unique(_channel_frequencies(raw.info))
    picks = _check_channels_ordered(raw.info, freqs)
    assert len(picks) == len(raw.ch_names)
    assert len(picks) == 6

    # Check block naming for optical density
    ch_names = [
        'S1_D1 760', 'S2_D1 760', 'S3_D1 760', 'S1_D1 850', 'S2_D1 850',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_od", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.repeat([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f
    # no problems here
    _check_channels_ordered(raw.info, [760, 850])
    # or with this (nirx) reordering
    raw.pick(picks=[0, 3, 1, 4, 2, 5])
    _check_channels_ordered(raw.info, [760, 850])

    # Check that if you mix types you get an error
    ch_names = [
        'S1_D1 hbo', 'S1_D1 hbr', 'S2_D1 hbo', 'S2_D1 hbr', 'S3_D1 hbo',
        'S3_D1 hbr'
    ]
    ch_types = np.tile(["hbo", "hbr"], 3)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw2 = RawArray(data, info, verbose=True)
    raw.add_channels([raw2])
    with pytest.raises(ValueError, match='does not support a combination'):
        _check_channels_ordered(raw.info, [760, 850])
コード例 #5
0
ファイル: interpolation.py プロジェクト: jackz314/mne-python
def _interpolate_bads_nirs(inst, method='nearest', exclude=(), verbose=None):
    from scipy.spatial.distance import pdist, squareform
    from mne.preprocessing.nirs import _channel_frequencies,\
        _check_channels_ordered

    # Returns pick of all nirs and ensures channels are correctly ordered
    freqs = np.unique(_channel_frequencies(inst.info))
    picks_nirs = _check_channels_ordered(inst.info, freqs)
    if len(picks_nirs) == 0:
        return

    nirs_ch_names = [inst.info['ch_names'][p] for p in picks_nirs]
    nirs_ch_names = [ch for ch in nirs_ch_names if ch not in exclude]
    bads_nirs = [ch for ch in inst.info['bads'] if ch in nirs_ch_names]
    if len(bads_nirs) == 0:
        return
    picks_bad = pick_channels(inst.info['ch_names'], bads_nirs, exclude=[])
    bads_mask = [p in picks_bad for p in picks_nirs]

    chs = [inst.info['chs'][i] for i in picks_nirs]
    locs3d = np.array([ch['loc'][:3] for ch in chs])

    _check_option('fnirs_method', method, ['nearest'])

    if method == 'nearest':

        dist = pdist(locs3d)
        dist = squareform(dist)

        for bad in picks_bad:
            dists_to_bad = dist[bad]
            # Ignore distances to self
            dists_to_bad[dists_to_bad == 0] = np.inf
            # Ignore distances to other bad channels
            dists_to_bad[bads_mask] = np.inf
            # Find closest remaining channels for same frequency
            closest_idx = np.argmin(dists_to_bad) + (bad % 2)
            inst._data[bad] = inst._data[closest_idx]

        inst.info['bads'] = [ch for ch in inst.info['bads'] if ch in exclude]

    return inst
コード例 #6
0
ファイル: test_nirs.py プロジェクト: sappelhoff/mne-python
def test_fnirs_channel_naming_and_order_custom_chroma():
    """Ensure fNIRS channel checking on manually created data."""
    data = np.random.RandomState(0).randn(6, 10)

    # Start with a correctly named raw intensity dataset
    # These are the steps required to build an fNIRS Raw object from scratch
    ch_names = [
        'S1_D1 hbo', 'S1_D1 hbr', 'S2_D1 hbo', 'S2_D1 hbr', 'S3_D1 hbo',
        'S3_D1 hbr'
    ]
    ch_types = np.tile(["hbo", "hbr"], 3)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)

    chroma = np.unique(_channel_chromophore(raw.info))
    picks = _check_channels_ordered(raw.info, chroma)
    assert len(picks) == len(raw.ch_names)
    assert len(picks) == 6

    # Test block creation fails
    ch_names = [
        'S1_D1 hbo', 'S2_D1 hbo', 'S3_D1 hbo', 'S1_D1 hbr', 'S2_D1 hbr',
        'S3_D1 hbr'
    ]
    ch_types = np.repeat(["hbo", "hbr"], 3)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    with pytest.raises(ValueError, match='not ordered .* chromophore'):
        _check_channels_ordered(raw.info, ["hbo", "hbr"])
    # Reordering should fix
    raw.pick(picks=[0, 3, 1, 4, 2, 5])
    _check_channels_ordered(raw.info, ["hbo", "hbr"])
    # Wrong names should fail
    with pytest.raises(ValueError, match='not ordered .* chromophore'):
        _check_channels_ordered(raw.info, ["hbb", "hbr"])

    # Test weird naming
    ch_names = [
        'S1_D1 hbb', 'S1_D1 hbr', 'S2_D1 hbb', 'S2_D1 hbr', 'S3_D1 hbb',
        'S3_D1 hbr'
    ]
    ch_types = np.tile(["hbo", "hbr"], 3)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    with pytest.raises(ValueError, match='naming conventions'):
        _check_channels_ordered(raw.info, ["hbb", "hbr"])

    # Check more weird naming
    ch_names = [
        'S1_DX hbo', 'S1_DX hbr', 'S2_D1 hbo', 'S2_D1 hbr', 'S3_D1 hbo',
        'S3_D1 hbr'
    ]
    ch_types = np.tile(["hbo", "hbr"], 3)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    with pytest.raises(ValueError, match='can not be parsed'):
        _check_channels_ordered(raw.info, ["hbo", "hbr"])
コード例 #7
0
ファイル: test_nirs.py プロジェクト: sappelhoff/mne-python
def test_fnirs_channel_naming_and_order_custom_raw():
    """Ensure fNIRS channel checking on manually created data."""
    data = np.random.normal(size=(6, 10))

    # Start with a correctly named raw intensity dataset
    # These are the steps required to build an fNIRS Raw object from scratch
    ch_names = [
        'S1_D1 760', 'S1_D1 850', 'S2_D1 760', 'S2_D1 850', 'S3_D1 760',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_cw_amplitude", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.tile([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f

    freqs = np.unique(_channel_frequencies(raw.info))
    picks = _check_channels_ordered(raw.info, freqs)
    assert len(picks) == len(raw.ch_names)
    assert len(picks) == 6

    # Different systems use different frequencies, so ensure that works
    ch_names = [
        'S1_D1 920', 'S1_D1 850', 'S2_D1 920', 'S2_D1 850', 'S3_D1 920',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_cw_amplitude", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.tile([920, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f

    picks = _check_channels_ordered(raw.info, [920, 850])
    assert len(picks) == len(raw.ch_names)
    assert len(picks) == 6

    # Catch expected errors

    # The frequencies named in the channel names must match the info loc field
    ch_names = [
        'S1_D1 760', 'S1_D1 850', 'S2_D1 760', 'S2_D1 850', 'S3_D1 760',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_cw_amplitude", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.tile([920, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f
    with pytest.raises(ValueError, match='not ordered'):
        _check_channels_ordered(raw.info, [920, 850])

    # Catch if someone doesn't set the info field
    ch_names = [
        'S1_D1 760', 'S1_D1 850', 'S2_D1 760', 'S2_D1 850', 'S3_D1 760',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_cw_amplitude", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    with pytest.raises(ValueError, match='missing wavelength information'):
        _check_channels_ordered(raw.info, [920, 850])

    # I have seen data encoded not in alternating frequency, but blocked.
    ch_names = [
        'S1_D1 760', 'S2_D1 760', 'S3_D1 760', 'S1_D1 850', 'S2_D1 850',
        'S3_D1 850'
    ]
    ch_types = np.repeat("fnirs_cw_amplitude", 6)
    info = create_info(ch_names=ch_names, ch_types=ch_types, sfreq=1.0)
    raw = RawArray(data, info, verbose=True)
    freqs = np.repeat([760, 850], 3)
    for idx, f in enumerate(freqs):
        raw.info["chs"][idx]["loc"][9] = f
    with pytest.raises(ValueError, match='channels not ordered correctly'):
        _check_channels_ordered(raw.info, [760, 850])
    # and this is how you would fix the ordering, then it should pass
    raw.pick(picks=[0, 3, 1, 4, 2, 5])
    _check_channels_ordered(raw.info, [760, 850])
コード例 #8
0
def scalp_coupling_index_windowed(raw,
                                  time_window=10,
                                  threshold=0.1,
                                  l_freq=0.7,
                                  h_freq=1.5,
                                  l_trans_bandwidth=0.3,
                                  h_trans_bandwidth=0.3,
                                  verbose=False):
    """
    Compute scalp coupling index for each channel and time window.

    As described in [1]_ and [2]_.
    This method provides a metric of data quality along the duration of
    the measurement. The user can specify the window over which the
    metric is computed.

    Parameters
    ----------
    raw : instance of Raw
        The haemoglobin data.
    time_window : number
        The duration of the window over which to calculate the metric.
        Default is 10 seconds as in PHOEBE paper.
    threshold : number
        Values below this are marked as bad and annotated in the raw file.
    %(l_freq)s
    %(h_freq)s
    %(l_trans_bandwidth)s
    %(h_trans_bandwidth)s
    %(verbose)s

    Returns
    -------
    raw : instance of Raw
        The Raw data. Optionally annotated with bad segments.
    scores : array (n_nirs, n_windows)
        Array of peak power values.
    times : list
        List of the start and end times of each window used to compute the
        peak spectral power.

    References
    ----------
    .. [1] Pollonini L et al., “PHOEBE: a method for real time mapping of
           optodes-scalp coupling in functional near-infrared spectroscopy” in
           Biomed. Opt. Express 7, 5104-5119 (2016).
    .. [2] Hernandez, Samuel Montero, and Luca Pollonini. "NIRSplot: a tool for
           quality assessment of fNIRS scans." Optics and the Brain.
           Optical Society of America, 2020.
    """

    raw = raw.copy().load_data()
    _validate_type(raw, BaseRaw, 'raw')

    if not len(pick_types(raw.info, fnirs='fnirs_od')):
        raise RuntimeError('Scalp coupling index '
                           'should be run on optical density data.')

    freqs = np.unique(_channel_frequencies(raw.info))
    picks = _check_channels_ordered(raw.info, freqs)

    filtered_data = filter_data(raw._data,
                                raw.info['sfreq'],
                                l_freq,
                                h_freq,
                                picks=picks,
                                verbose=verbose,
                                l_trans_bandwidth=l_trans_bandwidth,
                                h_trans_bandwidth=h_trans_bandwidth)

    window_samples = int(np.ceil(time_window * raw.info['sfreq']))
    n_windows = int(np.floor(len(raw) / window_samples))

    scores = np.zeros((len(picks), n_windows))
    times = []

    for window in range(n_windows):

        start_sample = int(window * window_samples)
        end_sample = start_sample + window_samples
        end_sample = np.min([end_sample, len(raw) - 1])

        t_start = raw.times[start_sample]
        t_stop = raw.times[end_sample]
        times.append((t_start, t_stop))

        for ii in picks[::2]:

            c1 = filtered_data[ii][start_sample:end_sample]
            c2 = filtered_data[ii + 1][start_sample:end_sample]
            c = np.corrcoef(c1, c2)[0][1]
            scores[ii, window] = c
            scores[ii + 1, window] = c

            if (threshold is not None) & (c < threshold):
                raw.annotations.append(t_start,
                                       time_window,
                                       'BAD_SCI',
                                       ch_names=[raw.ch_names[ii:ii + 2]])

    return raw, scores, times
コード例 #9
0
def test_fnirs_channel_naming_and_order_readers(fname):
    """Ensure fNIRS channel checking on standard readers."""
    # fNIRS data requires specific channel naming and ordering.

    # All standard readers should pass tests
    raw = read_raw_nirx(fname)
    freqs = np.unique(_channel_frequencies(raw))
    assert_array_equal(freqs, [760, 850])
    chroma = np.unique(_channel_chromophore(raw))
    assert len(chroma) == 0

    picks = _check_channels_ordered(raw, freqs)
    assert len(picks) == len(raw.ch_names)  # as all fNIRS only data

    # Check that dropped channels are detected
    # For each source detector pair there must be two channels,
    # removing one should throw an error.
    raw_dropped = raw.copy().drop_channels(raw.ch_names[4])
    with pytest.raises(ValueError, match='not ordered correctly'):
        _check_channels_ordered(raw_dropped, freqs)

    # The ordering must match the passed in argument
    raw_names_reversed = raw.copy().ch_names
    raw_names_reversed.reverse()
    raw_reversed = raw.copy().pick_channels(raw_names_reversed, ordered=True)
    with pytest.raises(ValueError, match='not ordered .* frequencies'):
        _check_channels_ordered(raw_reversed, freqs)
    # So if we flip the second argument it should pass again
    _check_channels_ordered(raw_reversed, [850, 760])

    # Check on OD data
    raw = optical_density(raw)
    freqs = np.unique(_channel_frequencies(raw))
    assert_array_equal(freqs, [760, 850])
    chroma = np.unique(_channel_chromophore(raw))
    assert len(chroma) == 0
    picks = _check_channels_ordered(raw, freqs)
    assert len(picks) == len(raw.ch_names)  # as all fNIRS only data

    # Check on haemoglobin data
    raw = beer_lambert_law(raw)
    freqs = np.unique(_channel_frequencies(raw))
    assert len(freqs) == 0
    assert len(_channel_chromophore(raw)) == len(raw.ch_names)
    chroma = np.unique(_channel_chromophore(raw))
    assert_array_equal(chroma, ["hbo", "hbr"])
    picks = _check_channels_ordered(raw, chroma)
    assert len(picks) == len(raw.ch_names)
    with pytest.raises(ValueError, match='not ordered .* chromophore'):
        _check_channels_ordered(raw, ["hbx", "hbr"])