v2.0.0
Loading...
Searching...
No Matches
fs_annotation.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "fs_annotation.h"
18#include "fs_label.h"
19#include "fs_surface.h"
20
21#include <iostream>
22
23//=============================================================================================================
24// QT INCLUDES
25//=============================================================================================================
26
27#include <QDebug>
28#include <algorithm>
29#include <numeric>
30
31#include <QFile>
32#include <QDataStream>
33#include <QFileInfo>
34
35//=============================================================================================================
36// USED NAMESPACES
37//=============================================================================================================
38
39using namespace FSLIB;
40using namespace Eigen;
41
42//=============================================================================================================
43// DEFINE MEMBER METHODS
44//=============================================================================================================
45
47: m_iHemi(-1)
48{
49}
50
51//=============================================================================================================
52
53FsAnnotation::FsAnnotation(const QString& p_sFileName)
54: m_sFileName(p_sFileName)
55{
56 FsAnnotation t_Annotation;
57 FsAnnotation::read(m_sFileName, t_Annotation);
58 *this = t_Annotation;
59}
60
61//=============================================================================================================
62
63FsAnnotation::FsAnnotation(const QString& subject_id, qint32 hemi, const QString& atlas, const QString& subjects_dir)
64: m_iHemi(-1)
65{
66 FsAnnotation::read(subject_id, hemi, atlas, subjects_dir, *this);
67}
68
69//=============================================================================================================
70
71FsAnnotation::FsAnnotation(const QString& path, qint32 hemi, const QString& atlas)
72: m_iHemi(-1)
73{
74 FsAnnotation::read(path, hemi, atlas, *this);
75}
76
77//=============================================================================================================
78
82
83//=============================================================================================================
84
86{
87 m_sFileName = QString("");
88 m_Vertices = VectorXi::Zero(0);
89 m_LabelIds = VectorXi::Zero(0);
90 m_Colortable.clear();
91}
92
93//=============================================================================================================
94
95bool FsAnnotation::read(const QString& subject_id, qint32 hemi, const QString& atlas, const QString& subjects_dir, FsAnnotation& p_Annotation)
96{
97 if (hemi != 0 && hemi != 1)
98 return false;
99
100 QString p_sFile = QString("%1/%2/label/%3.%4.annot").arg(subjects_dir).arg(subject_id).arg(hemi == 0 ? "lh" : "rh").arg(atlas);
101
102 return read(p_sFile, p_Annotation);
103}
104
105//=============================================================================================================
106
107bool FsAnnotation::read(const QString& path, qint32 hemi, const QString& atlas, FsAnnotation& p_Annotation)
108{
109 if (hemi != 0 && hemi != 1)
110 return false;
111
112 QString p_sFile = QString("%1/%2.%3.annot").arg(path).arg(hemi == 0 ? "lh" : "rh").arg(atlas);
113
114 return read(p_sFile, p_Annotation);
115}
116
117//=============================================================================================================
118
119bool FsAnnotation::read(const QString& p_sFileName, FsAnnotation& p_Annotation)
120{
121 p_Annotation.clear();
122
123 qInfo("Reading annotation...\n");
124 QFile t_File(p_sFileName);
125 QFileInfo fileInfo(t_File.fileName());
126
127 p_Annotation.m_sFileName = fileInfo.fileName();
128 p_Annotation.m_sFilePath = fileInfo.filePath();
129
130 if (!t_File.open(QIODevice::ReadOnly)) {
131 qWarning("\tError: Couldn't open the file");
132 return false;
133 }
134
135 QDataStream t_Stream(&t_File);
136 t_Stream.setByteOrder(QDataStream::BigEndian);
137
138 qint32 numEl;
139 t_Stream >> numEl;
140
141 p_Annotation.m_Vertices = VectorXi(numEl);
142 p_Annotation.m_LabelIds = VectorXi(numEl);
143
144 for (qint32 i = 0; i < numEl; ++i) {
145 t_Stream >> p_Annotation.m_Vertices[i];
146 t_Stream >> p_Annotation.m_LabelIds[i];
147 }
148
149 qint32 hasColortable;
150 t_Stream >> hasColortable;
151 if (hasColortable) {
152 p_Annotation.m_Colortable.clear();
153
154 //Read colortable
155 qint32 numEntries;
156 t_Stream >> numEntries;
157 qint32 len;
158 if (numEntries > 0) {
159 qInfo("\tReading from Original Version\n");
160 p_Annotation.m_Colortable.numEntries = numEntries;
161 t_Stream >> len;
162 QByteArray tmp;
163 tmp.resize(len);
164 t_Stream.readRawData(tmp.data(), len);
165 // FreeSurfer includes null terminator in stored length – strip it
166 if (tmp.endsWith('\0'))
167 tmp.chop(1);
168 p_Annotation.m_Colortable.orig_tab = tmp;
169
170 for (qint32 i = 0; i < numEntries; ++i)
171 p_Annotation.m_Colortable.struct_names.append("");
172
173 p_Annotation.m_Colortable.table = MatrixXi(numEntries, 5);
174
175 for (qint32 i = 0; i < numEntries; ++i) {
176 t_Stream >> len;
177 tmp.resize(len);
178 t_Stream.readRawData(tmp.data(), len);
179 if (tmp.endsWith('\0'))
180 tmp.chop(1);
181
182 p_Annotation.m_Colortable.struct_names[i] = tmp;
183
184 for (qint32 j = 0; j < 4; ++j)
185 t_Stream >> p_Annotation.m_Colortable.table(i, j);
186
187 p_Annotation.m_Colortable.table(i, 4) = p_Annotation.m_Colortable.table(i, 0) + p_Annotation.m_Colortable.table(i, 1) * 256 //(2^8)
188 + p_Annotation.m_Colortable.table(i, 2) * 65536 //(2^16)
189 + p_Annotation.m_Colortable.table(i, 3) * 16777216; //(2^24);
190 }
191 } else {
192 qint32 version = -numEntries;
193 if (version != 2)
194 qWarning("\tError! Does not handle version %d", version);
195 else
196 qInfo("\tReading from version %d\n", version);
197
198 t_Stream >> numEntries;
199 p_Annotation.m_Colortable.numEntries = numEntries;
200
201 t_Stream >> len;
202 QByteArray tmp;
203 tmp.resize(len);
204 t_Stream.readRawData(tmp.data(), len);
205 // FreeSurfer includes null terminator in stored length – strip it
206 if (tmp.endsWith('\0'))
207 tmp.chop(1);
208 p_Annotation.m_Colortable.orig_tab = tmp;
209
210 for (qint32 i = 0; i < numEntries; ++i)
211 p_Annotation.m_Colortable.struct_names.append("");
212
213 p_Annotation.m_Colortable.table = MatrixXi(numEntries, 5);
214
215 qint32 numEntriesToRead;
216 t_Stream >> numEntriesToRead;
217
218 qint32 structure;
219 for (qint32 i = 0; i < numEntriesToRead; ++i) {
220 t_Stream >> structure;
221 if (structure < 0)
222 qWarning("\tError! Read entry, index %d", structure);
223
224 if (!p_Annotation.m_Colortable.struct_names[structure].isEmpty())
225 qWarning("Error! Duplicate Structure %d", structure);
226
227 t_Stream >> len;
228 tmp.resize(len);
229 t_Stream.readRawData(tmp.data(), len);
230 if (tmp.endsWith('\0'))
231 tmp.chop(1);
232
233 p_Annotation.m_Colortable.struct_names[structure] = tmp;
234
235 for (qint32 j = 0; j < 4; ++j)
236 t_Stream >> p_Annotation.m_Colortable.table(structure, j);
237
238 p_Annotation.m_Colortable.table(structure, 4) = p_Annotation.m_Colortable.table(structure, 0) + p_Annotation.m_Colortable.table(structure, 1) * 256 //(2^8)
239 + p_Annotation.m_Colortable.table(structure, 2) * 65536 //(2^16)
240 + p_Annotation.m_Colortable.table(structure, 3) * 16777216; //(2^24);
241 }
242 }
243 qInfo("\tcolortable with %d entries read\n\t(originally %s)\n", p_Annotation.m_Colortable.numEntries, p_Annotation.m_Colortable.orig_tab.toUtf8().constData());
244 } else {
245 qWarning("\tError! No colortable stored");
246 }
247
248 // hemi info
249 if (t_File.fileName().contains("lh."))
250 p_Annotation.m_iHemi = 0;
251 else
252 p_Annotation.m_iHemi = 1;
253
254 qInfo("[done]\n");
255
256 t_File.close();
257
258 return true;
259}
260
261//=============================================================================================================
262
264 QList<FsLabel>& p_qListLabels,
265 QList<RowVector4i>& p_qListLabelRGBAs,
266 const QStringList& lLabelPicks) const
267{
268 if (this->m_iHemi != p_surf.hemi()) {
269 qWarning("FsAnnotation and surface hemisphere (annot = %d; surf = %d) do not match!\n", this->m_iHemi, p_surf.hemi());
270 return false;
271 }
272
273 if (m_LabelIds.size() == 0) {
274 qWarning("FsAnnotation doesn't' contain data!\n");
275 return false;
276 }
277
278 qInfo("Converting labels from annotation...");
279
280 //n_read = 0
281 //labels = list()
282 //label_colors = list()
283
284 VectorXi label_ids = m_Colortable.getLabelIds();
285 QStringList label_names = m_Colortable.getNames();
286 MatrixX4i label_rgbas = m_Colortable.getRGBAs();
287
288 // load the vertex positions from surface
289 MatrixX3f vert_pos = p_surf.rr();
290
291 // qDebug() << label_rgbas.rows() << label_ids.size() << label_names.size();
292
293 // std::cout << label_ids;
294
295 const qsizetype firstNew = p_qListLabels.size();
296 qint32 label_id, count;
297 RowVector4i label_rgba;
298 VectorXi vertices;
299 VectorXd values;
300 MatrixX3f pos;
301 QString name;
302 for (qint32 i = 0; i < label_rgbas.rows(); ++i) {
303 label_id = label_ids[i];
304 label_rgba = label_rgbas.row(i);
305 count = 0;
306 vertices.resize(m_LabelIds.size());
307 //Where
308 for (qint32 j = 0; j < m_LabelIds.size(); ++j) {
309 if (m_LabelIds[j] == label_id) {
310 vertices[count] = j;
311 ++count;
312 }
313 }
314 // check if label is part of cortical surface
315 if (count == 0)
316 continue;
317 vertices.conservativeResize(count);
318
319 pos.resize(count, 3);
320 for (qint32 j = 0; j < count; ++j)
321 pos.row(j) = vert_pos.row(vertices[j]);
322
323 values = VectorXd::Ones(count);
324 name = QString("%1-%2").arg(label_names[i]).arg(this->m_iHemi == 0 ? "lh" : "rh");
325
326 // put it all together
327 if (lLabelPicks.isEmpty()) {
328 //t_tris
329 p_qListLabels.append(FsLabel(vertices, pos, values, this->m_iHemi, name, label_id));
330 // store the color
331 p_qListLabelRGBAs.append(label_rgba);
332 } else if (lLabelPicks.indexOf(name) != -1) {
333 //t_tris
334 p_qListLabels.append(FsLabel(vertices, pos, values, this->m_iHemi, name, label_id));
335 // store the color
336 p_qListLabelRGBAs.append(label_rgba);
337 }
338 }
339
340 // for label_id, label_name, label_rgba in
341 // zip(label_ids, label_names, label_rgbas):
342 // vertices = np.where(annot == label_id)[0]
343 // if len(vertices) == 0:
344 // # label is not part of cortical surface
345 // continue
346 // pos = vert_pos[vertices, :]
347 // values = np.zeros(len(vertices))
348 // name = label_name + '-' + hemi
349 // label = FsLabel(vertices, pos, values, hemi, name=name)
350 // labels.append(label)
351
352 // # store the color
353 // label_rgba = tuple(label_rgba / 255.)
354 // label_colors.append(label_rgba)
355
356 // n_read = len(labels) - n_read
357 // logger.info(' read %d labels from %s' % (n_read, fname))
358
359 //# sort the labels and colors by label name
360 //names = [label.name for label in labels]
361 //labels, label_colors = zip(*((label, color) for (name, label, color)
362 // in sorted(zip(names, labels, label_colors))))
363 //# convert tuples to lists
364 //labels = list(labels)
365 //label_colors = list(label_colors)
366
367 // mne.read_labels_from_annot sorts the labels by name
368 QList<qsizetype> order(p_qListLabels.size() - firstNew);
369 std::iota(order.begin(), order.end(), firstNew);
370 std::sort(order.begin(), order.end(), [&p_qListLabels](qsizetype a, qsizetype b) { return p_qListLabels[a].name < p_qListLabels[b].name; });
371 const QList<FsLabel> labels = p_qListLabels.mid(firstNew);
372 const QList<RowVector4i> rgbas = p_qListLabelRGBAs.mid(firstNew);
373 for (qsizetype k = 0; k < order.size(); ++k) {
374 p_qListLabels[firstNew + k] = labels[order[k] - firstNew];
375 p_qListLabelRGBAs[firstNew + k] = rgbas[order[k] - firstNew];
376 }
377
378 qInfo("[done]\n");
379
380 return true;
381}
Reader for FreeSurfer per-vertex annotation (parcellation) files such as lh.aparc....
Reader and in-memory representation of a FreeSurfer/MNE surface label (.label).
Reader and in-memory representation of a single FreeSurfer triangular surface (e.g....
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
qint32 hemi() const
static bool read(const QString &subject_id, qint32 hemi, const QString &atlas, const QString &subjects_dir, FsAnnotation &p_Annotation)
bool toLabels(const FsSurface &p_surf, QList< FsLabel > &p_qListLabels, QList< Eigen::RowVector4i > &p_qListLabelRGBAs, const QStringList &lLabelPicks=QStringList()) const
QStringList struct_names
Eigen::MatrixXi table
A FreeSurfer/MNE surface label: per-vertex indices, Tk-RAS positions and scalar values for one hemisp...
Definition fs_label.h:81
In-memory FreeSurfer triangular cortical surface for one hemisphere.
Definition fs_surface.h:94
qint32 hemi() const
Definition fs_surface.h:357
const Eigen::MatrixX3f & rr() const
Definition fs_surface.h:378