28#include <QJsonDocument>
53 return m_annotations.size();
60 return m_annotations.isEmpty();
67 return m_annotations[index];
74 return m_annotations[index];
81 m_annotations.append(annotation);
87 const QStringList& channelNames,
const QString& comment)
95 m_annotations.append(a);
102 m_annotations.remove(index);
109 m_annotations.clear();
116 return m_annotations;
123 return static_cast<int>(m_annotations[index].onset * sfreq) + firstSample;
131 return static_cast<int>((a.
onset + a.
duration) * sfreq) + firstSample;
140 if (a.description.startsWith(descriptionFilter)) {
153 if (a.channelNames.isEmpty() || a.channelNames.contains(channelName)) {
166 double aEnd = a.onset + a.duration;
169 if (a.onset > tmax || aEnd < tmin) {
174 if (clipped.
onset < tmin) {
175 double shift = tmin - clipped.
onset;
176 clipped.
onset = tmin;
180 if (clippedEnd > tmax) {
195 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
196 qWarning() <<
"[FiffAnnotations::readJson] Cannot open file:" << path;
200 QByteArray data = file.readAll();
203 QJsonParseError parseError;
204 QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
205 if (parseError.error != QJsonParseError::NoError) {
206 qWarning() <<
"[FiffAnnotations::readJson] JSON parse error:" << parseError.errorString();
210 QJsonObject root = doc.object();
211 QJsonArray arr = root.value(
"annotations").toArray();
213 for (
int i = 0; i < arr.size(); ++i) {
214 QJsonObject obj = arr[i].toObject();
216 a.
onset = obj.value(
"onset").toDouble(0.0);
217 a.
duration = obj.value(
"duration").toDouble(0.0);
218 a.
description = obj.value(
"description").toString();
220 QJsonArray chArr = obj.value(
"channel_names").toArray();
221 for (
int j = 0; j < chArr.size(); ++j) {
225 a.
comment = obj.value(
"comment").toString();
226 a.
extras = obj.value(
"extras").toObject().toVariantMap();
241 obj[
"onset"] = a.onset;
242 obj[
"duration"] = a.duration;
243 obj[
"description"] = a.description;
246 for (
const QString& ch : a.channelNames) {
249 obj[
"channel_names"] = chArr;
250 obj[
"comment"] = a.comment;
251 obj[
"extras"] = QJsonObject::fromVariantMap(a.extras);
256 root[
"annotations"] = arr;
257 QJsonDocument doc(root);
260 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
261 qWarning() <<
"[FiffAnnotations::writeJson] Cannot open file for writing:" << path;
264 file.write(doc.toJson(QJsonDocument::Indented));
276 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
277 qWarning() <<
"[FiffAnnotations::readCsv] Cannot open file:" << path;
281 QTextStream in(&file);
287 while (!in.atEnd()) {
288 QString line = in.readLine().trimmed();
289 if (line.isEmpty()) {
295 int firstComma = line.indexOf(
',');
296 if (firstComma < 0)
continue;
297 int secondComma = line.indexOf(
',', firstComma + 1);
298 if (secondComma < 0)
continue;
300 bool onsetOk =
false, durationOk =
false;
301 double onset = line.left(firstComma).toDouble(&onsetOk);
302 double duration = line.mid(firstComma + 1, secondComma - firstComma - 1).toDouble(&durationOk);
303 QString description = line.mid(secondComma + 1);
305 if (!onsetOk || !durationOk) {
309 annot.
append(onset, duration, description);
321 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
322 qWarning() <<
"[FiffAnnotations::writeCsv] Cannot open file for writing:" << path;
326 QTextStream out(&file);
327 out <<
"onset,duration,description\n";
330 out << a.onset <<
"," << a.duration <<
"," << a.description <<
"\n";
341 QString ext = QFileInfo(path).suffix().toLower();
345 }
else if (ext ==
"csv") {
361 QString ext = QFileInfo(path).suffix().toLower();
FIFF / MNE annotations: time-tagged textual marks (BAD_*, EDGE, custom) with onset,...
FIFF file I/O, in-memory data structures and high-level readers/writers.
One FIFF / MNE annotation: onset (s), duration (s) and description string.
int onsetToSample(int index, double sfreq, int firstSample=0) const
static FiffAnnotations readCsv(const QString &path)
const QVector< FiffAnnotation > & toVector() const
FiffAnnotations selectByChannel(const QString &channelName) const
FiffAnnotations select(const QString &descriptionFilter) const
static FiffAnnotations read(const QString &path)
static bool write(const QString &path, const FiffAnnotations &annot)
static bool writeJson(const QString &path, const FiffAnnotations &annot)
int endToSample(int index, double sfreq, int firstSample=0) const
static bool writeCsv(const QString &path, const FiffAnnotations &annot)
static FiffAnnotations readJson(const QString &path)
void append(const FiffAnnotation &annotation)
FiffAnnotations crop(double tmin, double tmax) const
const FiffAnnotation & operator[](int index) const