v2.0.0
Loading...
Searching...
No Matches
fs_surface.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "fs_surface.h"
18#include <fiff/fiff_byte_swap.h>
19
20#include <iostream>
21
22//=============================================================================================================
23// QT INCLUDES
24//=============================================================================================================
25
26#include <QFile>
27#include <QDataStream>
28#include <QTextStream>
29
30//=============================================================================================================
31// USED NAMESPACES
32//=============================================================================================================
33
34using namespace FSLIB;
35using namespace Eigen;
36
37//=============================================================================================================
38// DEFINE MEMBER METHODS
39//=============================================================================================================
40
42: m_sFilePath("")
43, m_sFileName("")
44, m_iHemi(-1)
45, m_sSurf("")
46, m_vecOffset(Vector3f::Zero(3))
47{
48}
49
50//=============================================================================================================
51
52FsSurface::FsSurface(const QString& p_sFile)
53: m_sFilePath("")
54, m_sFileName("")
55, m_iHemi(-1)
56, m_sSurf("")
57, m_vecOffset(Vector3f::Zero(3))
58{
59 FsSurface::read(p_sFile, *this);
60}
61
62//=============================================================================================================
63
64FsSurface::FsSurface(const QString &subject_id, qint32 hemi, const QString &surf, const QString &subjects_dir)
65: m_sFilePath("")
66, m_sFileName("")
67, m_iHemi(-1)
68, m_sSurf("")
69, m_vecOffset(Vector3f::Zero(3))
70{
71 FsSurface::read(subject_id, hemi, surf, subjects_dir, *this);
72}
73
74//=============================================================================================================
75
76FsSurface::FsSurface(const QString &path, qint32 hemi, const QString &surf)
77: m_sFilePath("")
78, m_sFileName("")
79, m_iHemi(-1)
80, m_sSurf("")
81, m_vecOffset(Vector3f::Zero(3))
82{
83 FsSurface::read(path, hemi, surf, *this);
84}
85
86//=============================================================================================================
87
91
92//=============================================================================================================
93
95{
96 m_sFilePath.clear();
97 m_sFileName.clear();
98 m_iHemi = -1;
99 m_sSurf.clear();
100 m_matRR.resize(0,3);
101 m_matTris.resize(0,3);
102 m_matNN.resize(0,3);
103 m_vecCurv.resize(0);
104}
105
106//=============================================================================================================
107
108MatrixX3f FsSurface::compute_normals(const MatrixX3f& rr, const MatrixX3i& tris)
109{
110 qInfo("\tcomputing normals\n");
111 // first, compute triangle normals
112 MatrixX3f r1(tris.rows(),3); MatrixX3f r2(tris.rows(),3); MatrixX3f r3(tris.rows(),3);
113
114 for(qint32 i = 0; i < tris.rows(); ++i)
115 {
116 r1.row(i) = rr.row(tris(i, 0));
117 r2.row(i) = rr.row(tris(i, 1));
118 r3.row(i) = rr.row(tris(i, 2));
119 }
120
121 MatrixX3f x = r2 - r1;
122 MatrixX3f y = r3 - r1;
123 MatrixX3f tri_nn(x.rows(),y.cols());
124 tri_nn.col(0) = x.col(1).cwiseProduct(y.col(2)) - x.col(2).cwiseProduct(y.col(1));
125 tri_nn.col(1) = x.col(2).cwiseProduct(y.col(0)) - x.col(0).cwiseProduct(y.col(2));
126 tri_nn.col(2) = x.col(0).cwiseProduct(y.col(1)) - x.col(1).cwiseProduct(y.col(0));
127
128 // Triangle normals and areas
129 MatrixX3f tmp = tri_nn.cwiseProduct(tri_nn);
130 VectorXf normSize = tmp.rowwise().sum();
131 normSize = normSize.cwiseSqrt();
132
133 for(qint32 i = 0; i < normSize.size(); ++i)
134 if(normSize(i) != 0)
135 tri_nn.row(i) /= normSize(i);
136
137 MatrixX3f nn = MatrixX3f::Zero(rr.rows(), 3);
138
139 for(qint32 p = 0; p < tris.rows(); ++p)
140 {
141 Vector3i verts = tris.row(p);
142 for(qint32 j = 0; j < verts.size(); ++j)
143 nn.row(verts(j)) = tri_nn.row(p);
144 }
145
146 tmp = nn.cwiseProduct(nn);
147 normSize = tmp.rowwise().sum();
148 normSize = normSize.cwiseSqrt();
149
150 for(qint32 i = 0; i < normSize.size(); ++i)
151 if(normSize(i) != 0)
152 nn.row(i) /= normSize(i);
153
154 return nn;
155}
156
157//=============================================================================================================
158
159bool FsSurface::read(const QString &subject_id, qint32 hemi, const QString &surf, const QString &subjects_dir, FsSurface &p_Surface, bool p_bLoadCurvature)
160{
161 if(hemi != 0 && hemi != 1)
162 return false;
163
164 QString p_sFile = QString("%1/%2/surf/%3.%4").arg(subjects_dir).arg(subject_id).arg(hemi == 0 ? "lh" : "rh").arg(surf);
165
166 return read(p_sFile, p_Surface, p_bLoadCurvature);
167}
168
169//=============================================================================================================
170
171bool FsSurface::read(const QString &path, qint32 hemi, const QString &surf, FsSurface &p_Surface, bool p_bLoadCurvature)
172{
173 if(hemi != 0 && hemi != 1)
174 return false;
175
176 QString p_sFile = QString("%1/%2.%3").arg(path).arg(hemi == 0 ? "lh" : "rh").arg(surf);
177
178 return read(p_sFile, p_Surface, p_bLoadCurvature);
179}
180
181//=============================================================================================================
182
183bool FsSurface::read(const QString &p_sFile, FsSurface &p_Surface, bool p_bLoadCurvature)
184{
185 p_Surface.clear();
186
187 QFile t_File(p_sFile);
188
189 if (!t_File.open(QIODevice::ReadOnly))
190 {
191 qWarning("\tError: Couldn't open the surface file");
192 return false;
193 }
194
195 qInfo("Reading surface...\n");
196
197 //Strip file name and path
198 qint32 t_NameIdx = 0;
199 if(p_sFile.contains("lh."))
200 t_NameIdx = p_sFile.indexOf("lh.");
201 else if(p_sFile.contains("rh."))
202 t_NameIdx = p_sFile.indexOf("rh.");
203 else
204 return false;
205
206 p_Surface.m_sFilePath = p_sFile.mid(0,t_NameIdx);
207 p_Surface.m_sFileName = p_sFile.mid(t_NameIdx,p_sFile.size()-t_NameIdx);
208
209 QDataStream t_DataStream(&t_File);
210 t_DataStream.setByteOrder(QDataStream::BigEndian);
211
212 //
213 // Magic numbers to identify QUAD and TRIANGLE files
214 //
215 // QUAD_FILE_MAGIC_NUMBER = (-1 & 0x00ffffff) ;
216 // NEW_QUAD_FILE_MAGIC_NUMBER = (-3 & 0x00ffffff) ;
217 //
218 qint32 NEW_QUAD_FILE_MAGIC_NUMBER = 16777213;
219 qint32 TRIANGLE_FILE_MAGIC_NUMBER = 16777214;
220 qint32 QUAD_FILE_MAGIC_NUMBER = 16777215;
221
222 qint32 magic = FsSurface::fread3(t_DataStream);
223
224 qint32 nvert = 0;
225 qint32 nquad = 0;
226 qint32 nface = 0;
227 MatrixXf verts;
228 MatrixXi faces;
229
231 {
232 nvert = FsSurface::fread3(t_DataStream);
233 nquad = FsSurface::fread3(t_DataStream);
234 if(magic == QUAD_FILE_MAGIC_NUMBER)
235 qInfo("\t%s is a quad file (nvert = %d nquad = %d)\n", p_sFile.toUtf8().constData(),nvert,nquad);
236 else
237 qInfo("\t%s is a new quad file (nvert = %d nquad = %d)\n", p_sFile.toUtf8().constData(),nvert,nquad);
238
239 //vertices
240 verts.resize(nvert, 3);
241 if(magic == QUAD_FILE_MAGIC_NUMBER)
242 {
243 qint16 iVal;
244 for(qint32 i = 0; i < nvert; ++i)
245 {
246 for(qint32 j = 0; j < 3; ++j)
247 {
248 t_DataStream >> iVal;
250 verts(i,j) = static_cast<float>(iVal) / 100;
251 }
252 }
253 }
254 else
255 {
256 t_DataStream.readRawData(reinterpret_cast<char *>(verts.data()), nvert*3*sizeof(float));
257 for(qint32 i = 0; i < nvert; ++i)
258 for(qint32 j = 0; j < 3; ++j)
259 FIFFLIB::swap_floatp(&verts(i,j));
260 }
261
262 MatrixXi quads = FsSurface::fread3_many(t_DataStream, nquad*4);
263 MatrixXi quads_new(4, nquad);
264 qint32 count = 0;
265 for(qint32 j = 0; j < quads.cols(); ++j)
266 {
267 for(qint32 i = 0; i < quads.rows(); ++i)
268 {
269 quads_new(i,j) = quads(count, 0);
270 ++count;
271 }
272 }
273 quads = quads_new.transpose();
274 //
275 // Face splitting follows
276 //
277 faces = MatrixXi::Zero(2*nquad,3);
278 for(qint32 k = 0; k < nquad; ++k)
279 {
280 RowVectorXi quad = quads.row(k);
281 if ((quad[0] % 2) == 0)
282 {
283 faces(nface,0) = quad[0];
284 faces(nface,1) = quad[1];
285 faces(nface,2) = quad[3];
286 ++nface;
287
288 faces(nface,0) = quad[2];
289 faces(nface,1) = quad[3];
290 faces(nface,2) = quad[1];
291 ++nface;
292 }
293 else
294 {
295 faces(nface,0) = quad(0);
296 faces(nface,1) = quad(1);
297 faces(nface,2) = quad(2);
298 ++nface;
299
300 faces(nface,0) = quad(0);
301 faces(nface,1) = quad(2);
302 faces(nface,2) = quad(3);
303 ++nface;
304 }
305 }
306 }
307 else if(magic == TRIANGLE_FILE_MAGIC_NUMBER)
308 {
309 QString s = t_File.readLine();
310 t_File.readLine();
311
312 t_DataStream >> nvert;
313 t_DataStream >> nface;
314 FIFFLIB::swap_int(nvert);
315 FIFFLIB::swap_int(nface);
316
317 qInfo("\t%s is a triangle file (nvert = %d ntri = %d)\n", p_sFile.toUtf8().constData(), nvert, nface);
318 qInfo("\t%s", s.toUtf8().constData());
319
320 //vertices
321 verts.resize(3, nvert);
322 t_DataStream.readRawData(reinterpret_cast<char *>(verts.data()), nvert*3*sizeof(float));
323 for(qint32 i = 0; i < 3; ++i)
324 for(qint32 j = 0; j < nvert; ++j)
325 FIFFLIB::swap_floatp(&verts(i,j));
326
327 //faces
328 faces.resize(nface, 3);
329 qint32 iVal;
330 for(qint32 i = 0; i < nface; ++i)
331 {
332 for(qint32 j = 0; j < 3; ++j)
333 {
334 t_DataStream >> iVal;
335 FIFFLIB::swap_int(iVal);
336 faces(i,j) = iVal;
337 }
338 }
339 }
340 else
341 {
342 qWarning("Bad magic number (%d) in surface file %s",magic,p_sFile.toUtf8().constData());
343 return false;
344 }
345
346 verts.transposeInPlace();
347 verts.array() *= 0.001f;
348
349 p_Surface.m_matRR = verts.block(0,0,verts.rows(),3);
350 p_Surface.m_matTris = faces.block(0,0,faces.rows(),3);
351
352 //-> not needed since qglbuilder is doing that for us
353 p_Surface.m_matNN = compute_normals(p_Surface.m_matRR, p_Surface.m_matTris);
354
355 // hemi info
356 if(t_File.fileName().contains("lh."))
357 p_Surface.m_iHemi = 0;
358 else if(t_File.fileName().contains("rh."))
359 p_Surface.m_iHemi = 1;
360 else
361 {
362 p_Surface.m_iHemi = -1;
363 return false;
364 }
365
366 //Loaded surface
367 p_Surface.m_sSurf = t_File.fileName().mid((t_NameIdx+3),t_File.fileName().size() - (t_NameIdx+3));
368
369 //Load curvature
370 if(p_bLoadCurvature)
371 {
372 QString t_sCurvatureFile = QString("%1%2.curv").arg(p_Surface.m_sFilePath).arg(p_Surface.m_iHemi == 0 ? "lh" : "rh");
373 qInfo("\t");
374 p_Surface.m_vecCurv = FsSurface::read_curv(t_sCurvatureFile);
375 }
376
377 t_File.close();
378 qInfo("\tRead a surface with %d vertices from %s\n[done]\n",nvert,p_sFile.toUtf8().constData());
379
380 return true;
381}
382
383//=============================================================================================================
384
385VectorXf FsSurface::read_curv(const QString &p_sFileName)
386{
387 VectorXf curv;
388
389 qInfo("Reading curvature...");
390 QFile t_File(p_sFileName);
391
392 if (!t_File.open(QIODevice::ReadOnly))
393 {
394 qWarning("\tError: Couldn't open the curvature file");
395 return curv;
396 }
397
398 QDataStream t_DataStream(&t_File);
399 t_DataStream.setByteOrder(QDataStream::BigEndian);
400
401 qint32 vnum = FsSurface::fread3(t_DataStream);
402 qint32 NEW_VERSION_MAGIC_NUMBER = 16777215;
403
404 if(vnum == NEW_VERSION_MAGIC_NUMBER)
405 {
406 qint32 fnum, vals_per_vertex;
407 t_DataStream >> vnum;
408
409 t_DataStream >> fnum;
410 t_DataStream >> vals_per_vertex;
411
412 curv.resize(vnum, 1);
413 t_DataStream.readRawData(reinterpret_cast<char *>(curv.data()), vnum*sizeof(float));
414 for(qint32 i = 0; i < vnum; ++i)
416 }
417 else
418 {
419 qint32 fnum = FsSurface::fread3(t_DataStream);
420 Q_UNUSED(fnum)
421 qint16 iVal;
422 curv.resize(vnum, 1);
423 for(qint32 i = 0; i < vnum; ++i)
424 {
425 t_DataStream >> iVal;
427 curv(i) = static_cast<float>(iVal) / 100;
428 }
429 }
430 t_File.close();
431
432 qInfo("[done]\n");
433
434 return curv;
435}
436
437//=============================================================================================================
438
439qint32 FsSurface::fread3(QDataStream &stream)
440{
441 char bytes[3];
442 stream.readRawData(bytes, 3);
443 return (static_cast<unsigned char>(bytes[0]) << 16) + (static_cast<unsigned char>(bytes[1]) << 8) + static_cast<unsigned char>(bytes[2]);
444}
445
446//=============================================================================================================
447
448qint32 FsSurface::fread3(std::iostream &stream)
449{
450 char bytes[3];
451 stream.read(bytes, 3);
452 return (static_cast<unsigned char>(bytes[0]) << 16) + (static_cast<unsigned char>(bytes[1]) << 8) + static_cast<unsigned char>(bytes[2]);
453}
454
455//=============================================================================================================
456
457VectorXi FsSurface::fread3_many(QDataStream &stream, qint32 count)
458{
459 VectorXi res(count);
460 for(qint32 i = 0; i < count; ++i)
461 res[i] = FsSurface::fread3(stream);
462 return res;
463}
464
465//=============================================================================================================
466
467VectorXi FsSurface::fread3_many(std::iostream &stream, qint32 count)
468{
469 VectorXi res(count);
470 for(qint32 i = 0; i < count; ++i)
471 res[i] = FsSurface::fread3(stream);
472 return res;
473}
Reader and in-memory representation of a single FreeSurfer triangular surface (e.g....
#define QUAD_FILE_MAGIC_NUMBER
#define NEW_QUAD_FILE_MAGIC_NUMBER
#define TRIANGLE_FILE_MAGIC_NUMBER
Endianness swap helpers for the FIFF binary tag I/O layer (FIFF is always written big-endian on disk)...
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
qint32 swap_int(qint32 source)
void swap_floatp(float *source)
qint16 swap_short(qint16 source)
static Eigen::VectorXf read_curv(const QString &p_sFileName)
const Eigen::MatrixX3f & nn() const
Definition fs_surface.h:390
static Eigen::VectorXi fread3_many(QDataStream &stream, qint32 count)
qint32 hemi() const
Definition fs_surface.h:355
static qint32 fread3(QDataStream &stream)
const Eigen::MatrixX3i & tris() const
Definition fs_surface.h:383
QString surf() const
Definition fs_surface.h:369
static bool read(const QString &subject_id, qint32 hemi, const QString &surf, const QString &subjects_dir, FsSurface &p_Surface, bool p_bLoadCurvature=true)
const Eigen::MatrixX3f & rr() const
Definition fs_surface.h:376
const Eigen::VectorXf & curv() const
Definition fs_surface.h:397
static Eigen::MatrixX3f compute_normals(const Eigen::MatrixX3f &rr, const Eigen::MatrixX3i &tris)