v2.0.0
Loading...
Searching...
No Matches
mne_event_list.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "mne_event_list.h"
18
19#include <fiff/fiff_constants.h>
20#include <fiff/fiff_dir_node.h>
21#include <fiff/fiff_stream.h>
22#include <fiff/fiff_tag.h>
23
24//=============================================================================================================
25// QT INCLUDES
26//=============================================================================================================
27
28#include <QFile>
29#include <QRegularExpression>
30#include <QTextStream>
31
32//=============================================================================================================
33// STL INCLUDES
34//=============================================================================================================
35
36#include <algorithm>
37#include <cmath>
38
39//=============================================================================================================
40// USED NAMESPACES
41//=============================================================================================================
42
43using namespace MNELIB;
44using namespace FIFFLIB;
45
46//=============================================================================================================
47// DEFINE MEMBER METHODS
48//=============================================================================================================
49
50std::optional<MNEEventList> MNEEventList::readFif(const QString& path, int offset)
51{
52 QFile file(path);
53 FiffStream::SPtr stream(new FiffStream(&file));
54 if (!stream->open()) {
55 return std::nullopt;
56 }
57 const QList<FiffDirNode::SPtr> blocks = stream->dirtree()->dir_tree_find(FIFFB_MNE_EVENTS);
58 FiffTag::UPtr tag;
59 if (blocks.isEmpty() || !blocks[0]->find_tag(stream, FIFF_MNE_EVENT_LIST, tag)) {
60 qWarning("MNEEventList::readFif - No event data in %s", qPrintable(path));
61 return std::nullopt;
62 }
63 // Written as signed or unsigned integers; both have the same bit pattern.
64 const auto* values = reinterpret_cast<const qint32*>(tag->data());
65 const int nevent = static_cast<int>(tag->size() / (3 * sizeof(qint32)));
66 MNEEventList list;
67 list.events.resize(nevent);
68 for (int k = 0; k < nevent; ++k) {
69 list.events[k].sample = values[3 * k] - offset;
70 list.events[k].from = static_cast<unsigned int>(values[3 * k + 1]);
71 list.events[k].to = static_cast<unsigned int>(values[3 * k + 2]);
72 }
73 // The comments are one NUL-separated string, one entry per event.
74 if (blocks[0]->find_tag(stream, FIFF_MNE_EVENT_COMMENTS, tag)) {
75 const QList<QByteArray> comments = tag->split('\0');
76 for (int k = 0; k < nevent && k < comments.size(); ++k) {
77 list.events[k].comment = QString::fromLatin1(comments[k]);
78 }
79 }
80 return list;
81}
82
83//=============================================================================================================
84
85std::optional<MNEEventList> MNEEventList::readText(const QString& path, int offset, int oldOffset, float sfreq)
86{
87 QFile file(path);
88 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
89 qWarning("MNEEventList::readText - Cannot open %s", qPrintable(path));
90 return std::nullopt;
91 }
92 static const QRegularExpression whitespace(QStringLiteral("\\s+"));
93 MNEEventList list;
94 bool first = true;
95 QTextStream in(&file);
96 while (!in.atEnd()) {
97 const QString line = in.readLine().trimmed();
98 if (line.isEmpty() || line.startsWith('#')) {
99 continue;
100 }
101 const QStringList fields = line.split(whitespace);
102 bool ok[4];
103 int sample = fields.value(0).toInt(&ok[0]);
104 const float time = fields.value(1).toFloat(&ok[1]);
105 const unsigned int from = fields.value(2).toUInt(&ok[2]);
106 const unsigned int to = fields.value(3).toUInt(&ok[3]);
107 if (fields.size() < 4 || !(ok[0] && ok[1] && ok[2] && ok[3])) {
108 break;
109 }
110 if (sample < 0) {
111 sample = static_cast<int>(time * sfreq);
112 }
113 if (first) {
114 first = false;
115 if (from == 0 && to == 0) {
116 if (offset != sample) {
117 qWarning("MNEEventList::readText - First sample of the data (%d) and of %s (%d) differ", offset, qPrintable(path), sample);
118 }
119 continue;
120 }
121 offset -= oldOffset;
122 }
123 MNEEvent event;
124 event.sample = sample - offset;
125 event.from = from;
126 event.to = to;
127 event.comment = line.section(whitespace, 4).trimmed();
128 list.events.push_back(event);
129 }
130 if (list.events.empty()) {
131 qWarning("MNEEventList::readText - No events read from %s", qPrintable(path));
132 return std::nullopt;
133 }
134 return list;
135}
136
137//=============================================================================================================
138
139bool MNEEventList::writeFif(const QString& path, int offset) const
140{
141 QFile file(path);
143 if (!stream) {
144 return false;
145 }
146 std::vector<qint32> values;
147 values.reserve(3 * events.size());
148 QByteArray comments;
149 bool anyComment = false;
150 for (const MNEEvent& event : events) {
151 values.push_back(event.sample + offset);
152 values.push_back(static_cast<qint32>(event.from));
153 values.push_back(static_cast<qint32>(event.to));
154 comments += event.comment.toLatin1() + '\0';
155 anyComment = anyComment || !event.comment.isEmpty();
156 }
157 stream->start_block(FIFFB_MNE_EVENTS);
158 // Unsigned like MNE-C; trigger values are bit masks.
159 auto list = std::make_unique<FiffTag>();
160 list->kind = FIFF_MNE_EVENT_LIST;
161 list->type = FIFFT_UINT;
162 list->next = FIFFV_NEXT_SEQ;
163 const auto nbytes = static_cast<qsizetype>(values.size()) * static_cast<qsizetype>(sizeof(qint32));
164 list->append(reinterpret_cast<const char*>(values.data()), nbytes);
165 stream->write_tag(list);
166 if (anyComment) {
167 // Latin-1 bytes, one NUL-terminated comment per event, as MNE-C writes and mne-python reads them.
168 auto tag = std::make_unique<FiffTag>();
169 tag->kind = FIFF_MNE_EVENT_COMMENTS;
170 tag->type = FIFFT_BYTE;
171 tag->next = FIFFV_NEXT_SEQ;
172 tag->append(comments);
173 stream->write_tag(tag);
174 }
175 stream->end_block(FIFFB_MNE_EVENTS);
176 stream->end_file();
177 return true;
178}
179
180//=============================================================================================================
181
183{
184 std::stable_sort(events.begin(), events.end(), [](const MNEEvent& a, const MNEEvent& b) {
185 return a.sample < b.sample;
186 });
187}
188
189//=============================================================================================================
190
192{
193 events.insert(events.end(), other.events.cbegin(), other.events.cend());
194}
195
196//=============================================================================================================
197
199{
200 MNEEventList selected;
201 std::copy_if(events.cbegin(), events.cend(), std::back_inserter(selected.events), [value](const MNEEvent& event) {
202 return event.from == 0 && (value == 0 || event.to == value);
203 });
204 return selected;
205}
206
207//=============================================================================================================
208
209unsigned int MNEEventList::maxOnset() const
210{
211 unsigned int maximum = 0;
212 for (const MNEEvent& event : events) {
213 if (event.from == 0) {
214 maximum = std::max(maximum, event.to);
215 }
216 }
217 return maximum;
218}
219
220//=============================================================================================================
221
222Eigen::MatrixXi MNEEventList::toMatrix() const
223{
224 Eigen::MatrixXi matrix(nevent(), 3);
225 for (int k = 0; k < nevent(); ++k) {
226 matrix.row(k) << events[k].sample, static_cast<int>(events[k].from), static_cast<int>(events[k].to);
227 }
228 return matrix;
229}
230
231//=============================================================================================================
232
233MNEEventList MNEEventList::fromMatrix(const Eigen::MatrixXi& matrix)
234{
235 MNEEventList list;
236 list.events.resize(matrix.rows());
237 for (Eigen::Index k = 0; k < matrix.rows(); ++k) {
238 list.events[k].sample = matrix(k, 0);
239 list.events[k].from = static_cast<unsigned int>(matrix(k, 1));
240 list.events[k].to = static_cast<unsigned int>(matrix(k, 2));
241 }
242 return list;
243}
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFF_MNE_EVENT_LIST
#define FIFFB_MNE_EVENTS
#define FIFFV_NEXT_SEQ
#define FIFF_MNE_EVENT_COMMENTS
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
#define FIFFT_UINT
Definition fiff_file.h:229
#define FIFFT_BYTE
Definition fiff_file.h:222
Recursive node of the parsed FIFF block tree (FIFFB_* hierarchy with directory entries and children).
Event list with comments: MNE-C text and FIFF event files, sorting, merging and selection.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165
Single trigger-event marker.
Definition mne_event.h:46
Ordered list of trigger events with comments.
static MNEEventList fromMatrix(const Eigen::MatrixXi &matrix)
bool writeFif(const QString &path, int offset=0) const
unsigned int maxOnset() const
static std::optional< MNEEventList > readText(const QString &path, int offset, int oldOffset, float sfreq)
void append(const MNEEventList &other)
std::vector< MNEEvent > events
MNEEventList selectOnsets(unsigned int value=0) const
static std::optional< MNEEventList > readFif(const QString &path, int offset=0)
int nevent() const
Returns the number of events in the list.
Eigen::MatrixXi toMatrix() const