MNE version: 1.10.0
Operating system: Windows 11
I have an issue regarding converting my fNIRS raw intensities to optical density. More specifically I receive the error code below:
mne.preprocessing.nirs.optical_density(raw_intensity)
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "<decorator-gen-206>", line 12, in optical_density
File "c:", line 30, in optical_density
picks = _validate_nirs_info(raw.info, fnirs="cw_amplitude")
File "c:", line 258, in _validate_nirs_info
raise RuntimeError(
f"{which} must operate on {kind} data, but none was found."
)
RuntimeError: None must operate on continuous wave data, but none was found.
The above happens when all channels are marked as bad due to my SNR computations. Obviously this is not ideal at all, but beyond that I don’t understand why bad channels would have any effect on the conversion from raw intensities to optical densities and it makes me question if the conversion is different for bad channels normally as well. As you can see from the code snippet below, I interpolate the bad channels later, but first after bad channels related to the SCI have been found. I considered interpolating the bad channels twice, but is unsure on whether this is mathematically rigor.
raw_intensity = self.define_raw_intensity(filename)
raw_intensity = self.make_annotations(raw_intensity)
raw_intensity.annotations.rename(self.annotation_names)
for _unwanted in self.unwanted:
unwanted = np.nonzero(raw_intensity.annotations.description == _unwanted)
raw_intensity.annotations.delete(unwanted)
if self.snr_rejection != "None":
snr = snr_rejection(raw_intensity, self.snr_rejection)
# Validation
if self.snr_rejection == "SNR" and self.snr_threshold < 1:
raise ValueError("Currently the classic signal to noise ratio is used but the threshold for SNR is below 1 resulting in all channels being marked as bad. Please set the threshold to a value above 1.")
if self.snr_rejection == "CV" and self.snr_threshold > 1:
raise ValueError("Currently the coefficient of variation is used but the threshold for CV is above 1 resulting in all channels being marked as bad. Please set the threshold to a value below 1.")
# Get bad channels based on pair logic
snr_bad_channels = get_bad_channels_by_pairs(raw_intensity.ch_names, snr, self.snr_threshold, self.snr_rejection)
raw_intensity.info["bads"] = snr_bad_channels
else:
snr_bad_channels = []
raw_od = mne.preprocessing.nirs.optical_density(raw_intensity)
raw_od_original = raw_od.copy()
# Check channel name consistency
assert raw_intensity.ch_names == raw_od.ch_names, \
f"Channel names mismatch!\nraw_intensity: {len(raw_intensity.ch_names)} channels\nraw_od: {len(raw_od.ch_names)} channels"
if self.short_channel_correction:
raw_od = mne_nirs.signal_enhancement.short_channel_regression(raw_od)
raw_od = mne_nirs.channels.get_long_channels(raw_od)
if self.apply_tddr:
raw_od = mne.preprocessing.nirs.temporal_derivative_distribution_repair(raw_od)
sci = mne.preprocessing.nirs.scalp_coupling_index(raw_od)
sci_bad_channels = list(compress(raw_od.ch_names, sci < self.scalp_coupling_threshold))
# Filter SNR bad channels to only include those that still exist in the long channels dataset
snr_bad_channels_long_only = [ch for ch in snr_bad_channels if ch in raw_od.ch_names]
# Combine bad channels from all preprocessing
all_bad_channels = list(set(snr_bad_channels_long_only + sci_bad_channels))
raw_od.info["bads"] = all_bad_channels
if self.interpolate_bad_channels:
raw_od.interpolate_bads()
Buttom line:
- Why do bad channels affect the computation from the raw intensities to optical densities
- How should I handle this the best way?