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_channels_regexp(const QStringList& ch_names, const QString& regexp)
140{
141 const QRegularExpression re(QRegularExpression::anchoredPattern(regexp + QStringLiteral(".*")),
142 QRegularExpression::DotMatchesEverythingOption);
143 QList<int> sel;
144 for (int k = 0; k < ch_names.size(); ++k) {
145 if (re.match(ch_names[k]).hasMatch())
146 sel.append(k);
147 }
148 return Eigen::Map<const RowVectorXi>(sel.constData(), sel.size());
149}
150
151//=============================================================================================================
152
154{
155 filename = "";
156 meas_id.clear();
157 nchan = -1;
158 chs.clear();
159 ch_names.clear();
160 dev_head_t.clear();
161 ctf_head_t.clear();
162 all_coord_trans.clear();
163 bads.clear();
164}
165
166//=============================================================================================================
167
168RowVectorXi FiffInfoBase::pick_types(const QString meg, bool eeg, bool stim, const QStringList& include, const QStringList& exclude) const
169{
170 // A default-constructed info has nchan -1; count the channels actually present.
171 const qint32 nchPresent = static_cast<qint32>(this->chs.size());
172 RowVectorXi pick = RowVectorXi::Zero(nchPresent);
173
174 fiff_int_t kind;
175 qint32 k;
176 for (k = 0; k < nchPresent; ++k) {
177 kind = this->chs[k].kind;
178
179 if ((kind == FIFFV_MEG_CH || kind == FIFFV_REF_MEG_CH)) {
180 if (meg.compare("all") == 0) {
181 pick(k) = 1;
182 } else if (meg.compare("grad") == 0 && this->chs[k].unit == FIFF_UNIT_T_M) {
183 pick(k) = 1;
184 } else if (meg.compare("mag") == 0 && this->chs[k].unit == FIFF_UNIT_T) {
185 pick(k) = 1;
186 }
187 } else if (kind == FIFFV_EEG_CH && eeg)
188 pick(k) = 1;
189 else if (kind == FIFFV_STIM_CH && stim)
190 pick(k) = 1;
191 }
192
193 // restrict channels to selection if provided
194 qint32 p = 0;
195 QStringList myinclude;
196 for (k = 0; k < nchPresent; ++k) {
197 if (pick(0, k)) {
198 myinclude << this->ch_names[k];
199 ++p;
200 }
201 }
202
203 if (include.size() > 0) {
204 for (k = 0; k < include.size(); ++k) {
205 myinclude << include[k];
206 ++p;
207 }
208 }
209
210 RowVectorXi sel;
211 if (p != 0)
212 sel = FiffInfoBase::pick_channels(this->ch_names, myinclude, exclude);
213
214 return sel;
215}
216
217//=============================================================================================================
218
219RowVectorXi FiffInfoBase::pick_types(bool meg, bool eeg, bool stim, const QStringList& include, const QStringList& exclude) const
220{
221 if (meg)
222 return this->pick_types(QString("all"), eeg, stim, include, exclude);
223 else
224 return this->pick_types(QString(""), eeg, stim, include, exclude);
225}
226
227//=============================================================================================================
228
229RowVectorXi FiffInfoBase::pick_channels(const QStringList& ch_names, const QStringList& include, const QStringList& exclude)
230{
231 RowVectorXi sel = RowVectorXi::Zero(ch_names.size());
232
233 QStringList t_includedSelection;
234
235 qint32 count = 0;
236 for (qint32 k = 0; k < ch_names.size(); ++k) {
237 if ((include.size() == 0 || include.contains(ch_names[k])) && !exclude.contains(ch_names[k])) {
238 //make sure channel is unique
239 if (!t_includedSelection.contains(ch_names[k])) {
240 sel[count] = k;
241 ++count;
242 t_includedSelection << ch_names[k];
243 }
244 }
245 }
246 sel.conservativeResize(count);
247 return sel;
248}
249
250//=============================================================================================================
251
252FiffInfoBase FiffInfoBase::pick_info(const RowVectorXi* sel) const
253{
254 FiffInfoBase res = *this; //new FiffInfo(this);
255 if (sel == nullptr)
256 return res;
257
258 //ToDo when pointer List do deletion
259 res.chs.clear();
260 res.ch_names.clear();
261
262 qint32 idx;
263 for (qint32 i = 0; i < sel->size(); ++i) {
264 idx = (*sel)(0, i);
265 res.chs.append(this->chs[idx]);
266 res.ch_names.append(this->ch_names[idx]);
267 }
268 res.nchan = sel->size();
269
270 return res;
271}
272//=============================================================================================================
273
275 int& nmegp,
276 QList<FiffChInfo>& meg_compp,
277 int& nmeg_compp,
278 QList<FiffChInfo>& eegp,
279 int& neegp,
280 FiffCoordTrans& meg_head_t,
281 FiffId& idp) const
282{
283 for (int k = 0; k < nchan; k++) {
284 if (chs[k].kind == FIFFV_MEG_CH) {
285 megp.append(chs[k]);
286 nmegp++;
287 } else if (chs[k].kind == FIFFV_REF_MEG_CH) {
288 meg_compp.append(chs[k]);
289 nmeg_compp++;
290 } else if (chs[k].kind == FIFFV_EEG_CH) {
291 eegp.append(chs[k]);
292 neegp++;
293 }
294 }
295 meg_head_t = dev_head_t;
296 idp = meas_id;
297}
298
299//=============================================================================================================
300
302{
303 QStringList lChannelTypes;
304 for (const FiffChInfo& ch : chs) {
305 const QString type = channelTypeName(ch);
306 if (!type.isEmpty() && !lChannelTypes.contains(type))
307 lChannelTypes << type;
308 }
309 return lChannelTypes;
310}
311
312//=============================================================================================================
313
314bool FiffInfoBase::readBadChannelsFromFile(const QString& name, QStringList& listOut)
315{
316 if (name.isEmpty()) {
317 listOut.clear();
318 return true;
319 }
320
321 QFile file(name);
322 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
323 qCritical() << "Cannot open bad channel file:" << name;
324 return false;
325 }
326
327 QStringList list;
328 QTextStream in(&file);
329 while (!in.atEnd()) {
330 QString line = in.readLine().trimmed();
331 if (line.isEmpty() || line.startsWith('#'))
332 continue;
333 list.append(line);
334 }
335
336 if (file.error() != QFileDevice::NoError) {
337 qCritical() << "Error reading bad channel file:" << name;
338 return false;
339 }
340
341 listOut = list;
342 return true;
343}
#define FIFFV_COIL_FNIRS_HBO
#define FIFFV_COIL_FNIRS_CW_AMPLITUDE
#define FIFFV_EYETRACK_CH
#define FIFFV_EOG_CH
#define FIFFV_COIL_EYETRACK_POS
#define FIFFV_SYST_CH
#define FIFFV_IAS_CH
#define FIFFV_SEEG_CH
#define FIFFV_EEG_CH
#define FIFFV_GALVANIC_CH
#define FIFFV_COIL_FNIRS_OD
#define FIFFV_GOODNESS_FIT_CH
#define FIFFV_FNIRS_CH
#define FIFFV_EXCI_CH
#define FIFFV_COIL_EEG_BIPOLAR
#define FIFFV_COIL_FNIRS_FD_PHASE
#define FIFFV_RESP_CH
#define FIFFV_COIL_FNIRS_FD_AC_AMPLITUDE
#define FIFFV_REF_MEG_CH
#define FIFFV_COIL_EYETRACK_PUPIL
#define FIFFV_MISC_CH
#define FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY
#define FIFFV_MEG_CH
#define FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN
#define FIFFV_ECOG_CH
#define FIFFV_DIPOLE_WAVE_CH
#define FIFFV_BIO_CH
#define FIFFV_STIM_CH
#define FIFFV_COIL_EEG_CSD
#define FIFF_UNIT_T
#define FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE
#define FIFFV_EMG_CH
#define FIFFV_TEMPERATURE_CH
#define FIFFV_ECG_CH
#define FIFFV_DBS_CH
#define FIFF_UNIT_T_M
#define FIFFV_COIL_FNIRS_HBR
#define FIFFV_COIL_NONE
#define FIFFV_COIL_EEG
#define FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE
#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)
FiffCoordTrans dev_head_t