v2.0.0
Loading...
Searching...
No Matches
fiff_info_base.cpp
Go to the documentation of this file.
1//=============================================================================================================
18
19//=============================================================================================================
20// INCLUDES
21//=============================================================================================================
22
23#include "fiff_info_base.h"
24
25#include <iostream>
26
27#include <QFile>
28#include <QHash>
29#include <QRegularExpression>
30#include <QTextStream>
31#include <QDebug>
32
33#include <stdexcept>
34//=============================================================================================================
35// USED NAMESPACES
36//=============================================================================================================
37
38using namespace FIFFLIB;
39using namespace Eigen;
40
41//=============================================================================================================
42// DEFINE MEMBER METHODS
43//=============================================================================================================
44
46: filename("")
47, nchan(-1)
48{
49}
50
51//=============================================================================================================
52
54: filename(p_FiffInfoBase.filename)
55, bads(p_FiffInfoBase.bads)
56, meas_id(FiffId(p_FiffInfoBase.meas_id))
57, nchan(p_FiffInfoBase.nchan)
58, chs(p_FiffInfoBase.chs)
59, ch_names(p_FiffInfoBase.ch_names)
60, dev_head_t(p_FiffInfoBase.dev_head_t)
61, ctf_head_t(p_FiffInfoBase.ctf_head_t)
62, all_coord_trans(p_FiffInfoBase.all_coord_trans)
63{
64}
65
66//=============================================================================================================
67
71
72//=============================================================================================================
73
74namespace
75{
76
78QString channelTypeName(const FiffChInfo& ch)
79{
80 // mne-python's _first_rule (by kind) refined by _second_rules (MEG by unit, EEG/fNIRS/eye tracking by coil type)
81 static const QHash<int, QString> byKind{
82 {FIFFV_REF_MEG_CH, "ref_meg"}, {FIFFV_STIM_CH, "stim"}, {FIFFV_EOG_CH, "eog"}, {FIFFV_EMG_CH, "emg"}, {FIFFV_ECG_CH, "ecg"}, {FIFFV_RESP_CH, "resp"}, {FIFFV_MISC_CH, "misc"}, {FIFFV_EXCI_CH, "exci"}, {FIFFV_IAS_CH, "ias"}, {FIFFV_SYST_CH, "syst"}, {FIFFV_SEEG_CH, "seeg"}, {FIFFV_DBS_CH, "dbs"}, {FIFFV_BIO_CH, "bio"}, {FIFFV_DIPOLE_WAVE_CH, "dipole"}, {FIFFV_GOODNESS_FIT_CH, "gof"}, {FIFFV_ECOG_CH, "ecog"}, {FIFFV_TEMPERATURE_CH, "temperature"}, {FIFFV_GALVANIC_CH, "gsr"}};
83 static const QHash<int, QString> eegByCoil{
84 {FIFFV_COIL_EEG, "eeg"}, {FIFFV_COIL_EEG_BIPOLAR, "eeg"}, {FIFFV_COIL_NONE, "eeg"}, {FIFFV_COIL_EEG_CSD, "csd"}};
85 static const QHash<int, QString> fnirsByCoil{
86 {FIFFV_COIL_FNIRS_HBO, "hbo"}, {FIFFV_COIL_FNIRS_HBR, "hbr"}, {FIFFV_COIL_FNIRS_CW_AMPLITUDE, "fnirs_cw_amplitude"}, {FIFFV_COIL_FNIRS_FD_AC_AMPLITUDE, "fnirs_fd_ac_amplitude"}, {FIFFV_COIL_FNIRS_FD_PHASE, "fnirs_fd_phase"}, {FIFFV_COIL_FNIRS_OD, "fnirs_od"}, {FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE, "fnirs_td_gated_amplitude"}, {FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY, "fnirs_td_moments_intensity"}, {FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN, "fnirs_td_moments_mean"}, {FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE, "fnirs_td_moments_variance"}};
87 static const QHash<int, QString> eyetrackByCoil{{FIFFV_COIL_EYETRACK_POS, "eyegaze"}, {FIFFV_COIL_EYETRACK_PUPIL, "pupil"}};
88
89 const auto refine = [](const QHash<int, QString>& rule, int key) {
90 return rule.value(key);
91 };
92 if (ch.kind == FIFFV_MEG_CH)
93 return refine({{FIFF_UNIT_T_M, "grad"}, {FIFF_UNIT_T, "mag"}}, ch.unit);
94 if (ch.kind == FIFFV_EEG_CH)
95 return refine(eegByCoil, ch.chpos.coil_type);
96 if (ch.kind == FIFFV_FNIRS_CH)
97 return refine(fnirsByCoil, ch.chpos.coil_type);
98 if (ch.kind == FIFFV_EYETRACK_CH)
99 return refine(eyetrackByCoil, ch.chpos.coil_type);
100 if (FIFFM_QUAT_CH(ch.kind))
101 return "chpi"; // channels relative to head position monitoring
102 return refine(byKind, ch.kind);
103}
104
105} // namespace
106
107//=============================================================================================================
108
109QString FiffInfoBase::channel_type(qint32 idx) const
110{
111 const QString type = channelTypeName(this->chs[idx]);
112 if (type.isEmpty())
113 throw std::invalid_argument("Unknown channel type for channel " + this->chs[idx].ch_name.toStdString());
114 return type;
115}
116
117//=============================================================================================================
118
119QMap<QString, QList<int>> FiffInfoBase::channel_indices_by_type(const QList<int>& picks) const
120{
121 QMap<QString, QList<int>> byType;
122 for (const char* type : {"grad", "mag", "ref_meg", "eeg", "csd", "seeg", "dbs", "ecog", "eog", "emg", "ecg", "resp", "bio",
123 "misc", "stim", "exci", "syst", "ias", "gof", "dipole", "chpi", "temperature", "gsr", "hbo", "hbr",
124 "fnirs_cw_amplitude", "fnirs_fd_ac_amplitude", "fnirs_fd_phase", "fnirs_od",
125 "fnirs_td_gated_amplitude", "fnirs_td_moments_intensity", "fnirs_td_moments_mean",
126 "fnirs_td_moments_variance", "eyegaze", "pupil"}) {
127 byType.insert(QString::fromLatin1(type), {});
128 }
129 const qsizetype count = picks.isEmpty() ? chs.size() : picks.size();
130 for (qsizetype k = 0; k < count; ++k) {
131 const int idx = picks.isEmpty() ? static_cast<int>(k) : picks[k];
132 byType[channel_type(idx)].append(idx);
133 }
134 return byType;
135}
136
137//=============================================================================================================
138
139RowVectorXi FiffInfoBase::pick_data_channels(const QStringList& exclude) const
140{
141 static const QStringList dataTypes{"mag", "grad", "ref_meg", "eeg", "csd", "seeg", "ecog", "dbs", "hbo", "hbr"};
142 QList<int> picks;
143 for (int i = 0; i < chs.size(); ++i) {
144 const QString type = channelTypeName(chs[i]);
145 if ((dataTypes.contains(type) || type.startsWith(QLatin1String("fnirs_"))) && !exclude.contains(chs[i].ch_name))
146 picks.append(i);
147 }
148 return Map<const RowVectorXi>(picks.data(), picks.size());
149}
150
151//=============================================================================================================
152
153RowVectorXi FiffInfoBase::pick_channels_regexp(const QStringList& ch_names, const QString& regexp)
154{
155 const QRegularExpression re(QRegularExpression::anchoredPattern(regexp + QStringLiteral(".*")),
156 QRegularExpression::DotMatchesEverythingOption);
157 QList<int> sel;
158 for (int k = 0; k < ch_names.size(); ++k) {
159 if (re.match(ch_names[k]).hasMatch())
160 sel.append(k);
161 }
162 return Eigen::Map<const RowVectorXi>(sel.constData(), sel.size());
163}
164
165//=============================================================================================================
166
168{
169 filename = "";
170 meas_id.clear();
171 nchan = -1;
172 chs.clear();
173 ch_names.clear();
174 dev_head_t.clear();
175 ctf_head_t.clear();
176 all_coord_trans.clear();
177 bads.clear();
178}
179
180//=============================================================================================================
181
182RowVectorXi FiffInfoBase::pick_types(const QString meg, bool eeg, bool stim, const QStringList& include, const QStringList& exclude) const
183{
184 // A default-constructed info has nchan -1; count the channels actually present.
185 const qint32 nchPresent = static_cast<qint32>(this->chs.size());
186 RowVectorXi pick = RowVectorXi::Zero(nchPresent);
187
188 fiff_int_t kind;
189 qint32 k;
190 for (k = 0; k < nchPresent; ++k) {
191 kind = this->chs[k].kind;
192
193 if ((kind == FIFFV_MEG_CH || kind == FIFFV_REF_MEG_CH)) {
194 if (meg.compare("all") == 0) {
195 pick(k) = 1;
196 } else if (meg.compare("grad") == 0 && this->chs[k].unit == FIFF_UNIT_T_M) {
197 pick(k) = 1;
198 } else if (meg.compare("mag") == 0 && this->chs[k].unit == FIFF_UNIT_T) {
199 pick(k) = 1;
200 }
201 } else if (kind == FIFFV_EEG_CH && eeg)
202 pick(k) = 1;
203 else if (kind == FIFFV_STIM_CH && stim)
204 pick(k) = 1;
205 }
206
207 // restrict channels to selection if provided
208 qint32 p = 0;
209 QStringList myinclude;
210 for (k = 0; k < nchPresent; ++k) {
211 if (pick(0, k)) {
212 myinclude << this->ch_names[k];
213 ++p;
214 }
215 }
216
217 if (include.size() > 0) {
218 for (k = 0; k < include.size(); ++k) {
219 myinclude << include[k];
220 ++p;
221 }
222 }
223
224 RowVectorXi sel;
225 if (p != 0)
226 sel = FiffInfoBase::pick_channels(this->ch_names, myinclude, exclude);
227
228 return sel;
229}
230
231//=============================================================================================================
232
233RowVectorXi FiffInfoBase::pick_types(bool meg, bool eeg, bool stim, const QStringList& include, const QStringList& exclude) const
234{
235 if (meg)
236 return this->pick_types(QString("all"), eeg, stim, include, exclude);
237 else
238 return this->pick_types(QString(""), eeg, stim, include, exclude);
239}
240
241//=============================================================================================================
242
243RowVectorXi FiffInfoBase::pick_channels(const QStringList& ch_names, const QStringList& include, const QStringList& exclude)
244{
245 RowVectorXi sel = RowVectorXi::Zero(ch_names.size());
246
247 QStringList t_includedSelection;
248
249 qint32 count = 0;
250 for (qint32 k = 0; k < ch_names.size(); ++k) {
251 if ((include.size() == 0 || include.contains(ch_names[k])) && !exclude.contains(ch_names[k])) {
252 //make sure channel is unique
253 if (!t_includedSelection.contains(ch_names[k])) {
254 sel[count] = k;
255 ++count;
256 t_includedSelection << ch_names[k];
257 }
258 }
259 }
260 sel.conservativeResize(count);
261 return sel;
262}
263
264//=============================================================================================================
265
266FiffInfoBase FiffInfoBase::pick_info(const RowVectorXi* sel) const
267{
268 FiffInfoBase res = *this; //new FiffInfo(this);
269 if (sel == nullptr)
270 return res;
271
272 //ToDo when pointer List do deletion
273 res.chs.clear();
274 res.ch_names.clear();
275
276 qint32 idx;
277 for (qint32 i = 0; i < sel->size(); ++i) {
278 idx = (*sel)(0, i);
279 res.chs.append(this->chs[idx]);
280 res.ch_names.append(this->ch_names[idx]);
281 }
282 res.nchan = sel->size();
283 res.bads.removeIf([&res](const QString& bad) { return !res.ch_names.contains(bad); });
284
285 return res;
286}
287//=============================================================================================================
288
290 int& nmegp,
291 QList<FiffChInfo>& meg_compp,
292 int& nmeg_compp,
293 QList<FiffChInfo>& eegp,
294 int& neegp,
295 FiffCoordTrans& meg_head_t,
296 FiffId& idp) const
297{
298 for (int k = 0; k < nchan; k++) {
299 if (chs[k].kind == FIFFV_MEG_CH) {
300 megp.append(chs[k]);
301 nmegp++;
302 } else if (chs[k].kind == FIFFV_REF_MEG_CH) {
303 meg_compp.append(chs[k]);
304 nmeg_compp++;
305 } else if (chs[k].kind == FIFFV_EEG_CH) {
306 eegp.append(chs[k]);
307 neegp++;
308 }
309 }
310 meg_head_t = dev_head_t;
311 idp = meas_id;
312}
313
314//=============================================================================================================
315
317{
318 QStringList lChannelTypes;
319 for (const FiffChInfo& ch : chs) {
320 const QString type = channelTypeName(ch);
321 if (!type.isEmpty() && !lChannelTypes.contains(type))
322 lChannelTypes << type;
323 }
324 return lChannelTypes;
325}
326
327//=============================================================================================================
328
329bool FiffInfoBase::readBadChannelsFromFile(const QString& name, QStringList& listOut)
330{
331 if (name.isEmpty()) {
332 listOut.clear();
333 return true;
334 }
335
336 QFile file(name);
337 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
338 qCritical() << "Cannot open bad channel file:" << name;
339 return false;
340 }
341
342 QStringList list;
343 QTextStream in(&file);
344 while (!in.atEnd()) {
345 QString line = in.readLine().trimmed();
346 if (line.isEmpty() || line.startsWith('#'))
347 continue;
348 list.append(line);
349 }
350
351 if (file.error() != QFileDevice::NoError) {
352 qCritical() << "Error reading bad channel file:" << name;
353 return false;
354 }
355
356 listOut = list;
357 return true;
358}
constexpr auto FIFF_UNIT_T_M
constexpr auto FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE
constexpr auto FIFFV_ECG_CH
#define FIFFV_EEG_CH
constexpr auto FIFFV_EXCI_CH
constexpr auto FIFFV_SYST_CH
constexpr auto FIFFV_GOODNESS_FIT_CH
constexpr auto FIFFV_COIL_EYETRACK_POS
constexpr auto FIFFV_DBS_CH
constexpr auto FIFFV_IAS_CH
constexpr auto FIFFV_COIL_FNIRS_OD
constexpr auto FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN
constexpr auto FIFFV_COIL_FNIRS_CW_AMPLITUDE
constexpr auto FIFFV_COIL_EEG_CSD
constexpr auto FIFFV_COIL_EEG
constexpr auto FIFFV_MISC_CH
constexpr auto FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE
constexpr auto FIFFV_SEEG_CH
constexpr auto FIFFV_COIL_FNIRS_HBR
constexpr auto FIFFV_COIL_EEG_BIPOLAR
#define FIFFV_MEG_CH
constexpr auto FIFFV_GALVANIC_CH
constexpr auto FIFFV_ECOG_CH
constexpr auto FIFFV_COIL_NONE
constexpr auto FIFFV_STIM_CH
constexpr auto FIFFV_EYETRACK_CH
constexpr auto FIFFV_BIO_CH
constexpr auto FIFF_UNIT_T
constexpr auto FIFFV_DIPOLE_WAVE_CH
constexpr auto FIFFV_COIL_FNIRS_FD_AC_AMPLITUDE
constexpr auto FIFFV_COIL_FNIRS_FD_PHASE
constexpr auto FIFFV_EMG_CH
constexpr auto FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY
constexpr auto FIFFV_TEMPERATURE_CH
constexpr auto FIFFV_FNIRS_CH
constexpr auto FIFFV_COIL_FNIRS_HBO
constexpr auto FIFFV_EOG_CH
constexpr auto FIFFV_COIL_EYETRACK_PUPIL
constexpr auto FIFFV_REF_MEG_CH
constexpr auto FIFFV_RESP_CH
#define FIFFM_QUAT_CH(X)
Minimal measurement-info subset (channel list, sampling rate, basic transforms) shared by FIFF reader...
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
fiff_int_t coil_type
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
128-bit FIFF identifier: hardware machine ID plus creation time, stamped on every file and block.
Definition fiff_id.h:69
QList< FiffCoordTrans > all_coord_trans
static Eigen::RowVectorXi pick_channels_regexp(const QStringList &ch_names, const QString &regexp)
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
QMap< QString, QList< int > > channel_indices_by_type(const QList< int > &picks={}) const
QString channel_type(qint32 idx) const
QList< FiffChInfo > chs
FiffInfoBase pick_info(const Eigen::RowVectorXi *sel=nullptr) const
FiffCoordTrans ctf_head_t
QStringList get_channel_types()
Eigen::RowVectorXi pick_types(const QString meg, bool eeg=false, bool stim=false, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
void mne_read_meg_comp_eeg_ch_info(QList< FiffChInfo > &megp, int &nmegp, QList< FiffChInfo > &meg_compp, int &nmeg_compp, QList< FiffChInfo > &eegp, int &neegp, FiffCoordTrans &meg_head_t, FiffId &idp) const
static bool readBadChannelsFromFile(const QString &name, QStringList &listOut)
Eigen::RowVectorXi pick_data_channels(const QStringList &exclude=defaultQStringList) const
FiffCoordTrans dev_head_t