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