v2.0.0
Loading...
Searching...
No Matches
mne_bem.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23//=============================================================================================================
24// INCLUDES
25//=============================================================================================================
26
27#include "mne_bem.h"
28
29#include <math/warp.h>
30#include <fs/fs_label.h>
31
32//=============================================================================================================
33// QT INCLUDES
34//=============================================================================================================
35
36#include <QFile>
37
38#include <stdexcept>
39//=============================================================================================================
40// USED NAMESPACES
41//=============================================================================================================
42
43using namespace UTILSLIB;
44using namespace FSLIB;
45using namespace MNELIB;
46using namespace FIFFLIB;
47using namespace Eigen;
48
49//=============================================================================================================
50// DEFINE MEMBER METHODS
51//=============================================================================================================
52
56
57//=============================================================================================================
58
59MNEBem::MNEBem(const MNEBem& p_MNEBem)
60: m_qListBemSurface(p_MNEBem.m_qListBemSurface)
61{
62}
63
64//=============================================================================================================
65
66MNEBem::MNEBem(QIODevice& p_IODevice) //const MNEBem &p_MNEBem
67//: m_qListBemSurface()
68{
69 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
70
71 if (!MNEBem::readFromStream(t_pStream, true, *this)) {
72 t_pStream->close();
73 throw std::runtime_error("Could not read the bem surfaces\n");
74 //ToDo error(me,'Could not read the bem surfaces (%s)',mne_omit_first_line(lasterr));
75 // return false;
76 }
77
78 // bool testStream =t_pStream->device()->isOpen();
79}
80
81//=============================================================================================================
82
86
87//=============================================================================================================
88
90{
91 m_qListBemSurface.clear();
92}
93
94//=============================================================================================================
95
96bool MNEBem::readFromStream(FiffStream::SPtr& p_pStream, bool add_geom, MNEBem& p_Bem)
97{
98 //
99 // Open the file, create directory
100 //
101 bool open_here = false;
102 QFile t_file; //ToDo TCPSocket;
103
104 if (!p_pStream->device()->isOpen()) {
105 QString t_sFileName = p_pStream->streamName();
106
107 t_file.setFileName(t_sFileName);
108 p_pStream = FiffStream::SPtr(new FiffStream(&t_file));
109 if (!p_pStream->open()) {
110 return false;
111 }
112 open_here = true;
113 // if(t_pDir)
114 // delete t_pDir;
115 }
116
117 //
118 // Find all BEM surfaces
119 //
120
121 QList<FiffDirNode::SPtr> bem = p_pStream->dirtree()->dir_tree_find(FIFFB_BEM);
122 if (bem.isEmpty()) {
123 qCritical() << "No BEM block found!";
124 if (open_here) {
125 p_pStream->close();
126 }
127 return false;
128 }
129
130 QList<FiffDirNode::SPtr> bemsurf = p_pStream->dirtree()->dir_tree_find(FIFFB_BEM_SURF);
131 if (bemsurf.isEmpty()) {
132 qCritical() << "No BEM surfaces found!";
133 if (open_here) {
134 p_pStream->close();
135 }
136 return false;
137 }
138
139 for (int k = 0; k < bemsurf.size(); ++k) {
140 MNEBemSurface p_BemSurface;
141 qInfo("\tReading a BEM surface...");
142 MNEBem::readBemSurface(p_pStream, bemsurf[k], p_BemSurface);
143 p_BemSurface.addTriangleData();
144 if (add_geom || p_BemSurface.nn.rows() == 0) {
145 p_BemSurface.addVertexNormals();
146 }
147 qInfo("\t[done]\n");
148
149 p_Bem.m_qListBemSurface.append(p_BemSurface);
150 // src(k) = this;
151 }
152
153 qInfo("\t%lld bem surfaces read\n", static_cast<long long>(bemsurf.size()));
154
155 if (open_here) {
156 p_pStream->close();
157 }
158 return true;
159}
160
161//=============================================================================================================
162
163bool MNEBem::readBemSurface(FiffStream::SPtr& p_pStream, const FiffDirNode::SPtr& p_Tree, MNEBemSurface& p_BemSurface)
164{
165 p_BemSurface.clear();
166
167 FiffTag::UPtr t_pTag;
168
169 //=====================================================================
170 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_ID, t_pTag)) {
171 p_BemSurface.id = FIFFV_BEM_SURF_ID_UNKNOWN;
172 } else {
173 p_BemSurface.id = *t_pTag->toInt();
174 }
175
176 // qDebug() << "Read BemSurface ID; type:" << t_pTag->getType() << "value:" << *t_pTag->toInt();
177
178 //=====================================================================
179 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SIGMA, t_pTag)) {
180 p_BemSurface.sigma = 1.0;
181 } else {
182 p_BemSurface.sigma = *t_pTag->toFloat();
183 }
184
185 // qDebug() <<
186
187 //=====================================================================
188 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NNODE, t_pTag)) {
189 p_pStream->close();
190 qWarning() << "np not found!";
191 return false;
192 } else {
193 p_BemSurface.np = *t_pTag->toInt();
194 }
195
196 // qDebug() <<
197
198 //=====================================================================
199 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NTRI, t_pTag)) {
200 p_pStream->close();
201 qWarning() << "ntri not found!";
202 return false;
203 } else {
204 p_BemSurface.ntri = *t_pTag->toInt();
205 }
206
207 // qDebug() <<
208
209 //=====================================================================
210 if (!p_Tree->find_tag(p_pStream, FIFF_MNE_COORD_FRAME, t_pTag)) {
211 qWarning() << "FIFF_MNE_COORD_FRAME not found, trying FIFF_BEM_COORD_FRAME.";
212 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_COORD_FRAME, t_pTag)) {
213 p_pStream->close();
214 throw std::runtime_error("Coordinate frame information not found.");
215 } else {
216 p_BemSurface.coord_frame = *t_pTag->toInt();
217 }
218 } else {
219 p_BemSurface.coord_frame = *t_pTag->toInt();
220 }
221
222 // qDebug() <<
223
224 //=====================================================================
225 //
226 // Vertices, normals, and triangles
227 //
228 //=====================================================================
229 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NODES, t_pTag)) {
230 p_pStream->close();
231 throw std::runtime_error("Vertex data not found.");
232 }
233
234 p_BemSurface.rr = t_pTag->toFloatMatrix().transpose();
235 qint32 rows_rr = p_BemSurface.rr.rows();
236
237 if (rows_rr != p_BemSurface.np) {
238 p_pStream->close();
239 throw std::runtime_error("Vertex information is incorrect.");
240 }
241
242 // qDebug() << "Surf Nodes; type:" << t_pTag->getType();
243
244 //=====================================================================
245 // Stored normals are optional (mne-python, MNE-C); readFromStream computes missing ones.
246 if (p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NORMALS, t_pTag) || p_Tree->find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_NORMALS, t_pTag)) {
247 p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
248 if (p_BemSurface.nn.rows() != p_BemSurface.np) {
249 p_pStream->close();
250 throw std::runtime_error("Vertex normal information is incorrect.");
251 }
252 } else {
253 p_BemSurface.nn.resize(0, 3);
254 }
255
256 // qDebug() << "Bem Vertex Normals; type:" << t_pTag->getType();
257
258 //=====================================================================
259 if (p_BemSurface.ntri > 0) {
260 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_TRIANGLES, t_pTag)) {
261 if (!p_Tree->find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_TRIANGLES, t_pTag)) {
262 p_pStream->close();
263 throw std::runtime_error("Triangulation not found.");
264 } else {
265 p_BemSurface.itris = t_pTag->toIntMatrix().transpose();
266 p_BemSurface.itris -= MatrixXi::Constant(p_BemSurface.itris.rows(), 3, 1); //0 based indizes
267 }
268 } else {
269 p_BemSurface.itris = t_pTag->toIntMatrix().transpose();
270 p_BemSurface.itris -= MatrixXi::Constant(p_BemSurface.itris.rows(), 3, 1); //0 based indizes
271 }
272
273 if (p_BemSurface.itris.rows() != p_BemSurface.ntri) {
274 p_pStream->close();
275 throw std::runtime_error("Triangulation information is incorrect.");
276 }
277 } else {
278 p_BemSurface.itris.resize(0, 3);
279 }
280
281 return true;
282}
283
284//=============================================================================================================
285
286bool MNEBem::write(QIODevice& p_IODevice)
287{
288 FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
289 if (!t_pStream) {
290 return false;
291 }
292 qInfo("Write BEM surface in %s...\n", t_pStream->streamName().toUtf8().constData());
293 this->writeToStream(t_pStream.data());
294 t_pStream->end_file();
295 return true;
296}
297
298//=============================================================================================================
299
301{
302 p_pStream->start_block(FIFFB_BEM);
303 for (qint32 h = 0; h < m_qListBemSurface.size(); ++h) {
304 qInfo("\tWrite a bem surface... ");
305 p_pStream->start_block(FIFFB_BEM_SURF);
306 m_qListBemSurface[h].writeToStream(p_pStream);
307 p_pStream->end_block(FIFFB_BEM_SURF);
308 qInfo("[done]\n");
309 }
310 qInfo("\t%lld bem surfaces written\n", static_cast<long long>(m_qListBemSurface.size()));
311 p_pStream->end_block(FIFFB_BEM);
312}
313
314//=============================================================================================================
315
316const MNEBemSurface& MNEBem::operator[](qint32 idx) const
317{
318 //
319 // Falling back to surface 0 is no help when there are no surfaces at all:
320 // indexing an empty QList asserts in a debug build and is undefined
321 // otherwise. Hand back a default surface instead, which callers can spot
322 // through its empty vertex list.
323 //
324 if (m_qListBemSurface.isEmpty()) {
325 qWarning("Warning: No BEM surfaces available! Returning an empty surface.");
326
327 static const MNEBemSurface defaultSurface;
328
329 return defaultSurface;
330 }
331
332 if (idx < 0 || idx >= m_qListBemSurface.length()) {
333 qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
334 idx = 0;
335 }
336 return m_qListBemSurface[idx];
337}
338
339//=============================================================================================================
340
342{
343 //
344 // See the const overload: an empty list has no surface 0 to fall back to,
345 // so keep one default surface around rather than indexing out of bounds.
346 //
347 if (m_qListBemSurface.isEmpty()) {
348 qWarning("Warning: No BEM surfaces available! Returning an empty surface.");
349
350 static MNEBemSurface defaultSurface;
351 defaultSurface = MNEBemSurface();
352
353 return defaultSurface;
354 }
355
356 if (idx < 0 || idx >= m_qListBemSurface.length()) {
357 qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
358 idx = 0;
359 }
360 return m_qListBemSurface[idx];
361}
362
363//=============================================================================================================
364
366{
367 this->m_qListBemSurface.append(surf);
368 return *this;
369}
370
371//=============================================================================================================
372
374{
375 this->m_qListBemSurface.append(*surf);
376 return *this;
377}
378
379//=============================================================================================================
380
381void MNEBem::warp(const MatrixXf& sLm, const MatrixXf& dLm)
382{
383 Warp help;
384 QList<MatrixXf> vertList;
385 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
386 vertList.append(this->m_qListBemSurface[i].rr);
387 }
388
389 help.calculate(sLm, dLm, vertList);
390
391 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
392 this->m_qListBemSurface[i].rr = vertList.at(i);
393 }
394 return;
395}
396
397//=============================================================================================================
398
400{
401 MatrixX3f vert;
402 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
403 vert = this->m_qListBemSurface[i].rr;
404 vert = trans.apply_trans(vert);
405 this->m_qListBemSurface[i].rr = vert;
406 }
407 return;
408}
409
410//=============================================================================================================
411
413{
414 MatrixX3f vert;
415 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
416 vert = this->m_qListBemSurface[i].rr;
417 vert = trans.apply_inverse_trans(vert);
418 this->m_qListBemSurface[i].rr = vert;
419 }
420 return;
421}
#define FIFF_MNE_COORD_FRAME
#define FIFF_MNE_SOURCE_SPACE_NORMALS
#define FIFF_MNE_SOURCE_SPACE_TRIANGLES
#define FIFFV_BEM_SURF_ID_UNKNOWN
Definition fiff_file.h:741
#define FIFFB_BEM_SURF
Definition fiff_file.h:397
#define FIFF_BEM_SURF_NNODE
Definition fiff_file.h:726
#define FIFF_BEM_SURF_NODES
Definition fiff_file.h:728
#define FIFF_BEM_SURF_TRIANGLES
Definition fiff_file.h:729
#define FIFF_BEM_COORD_FRAME
Definition fiff_file.h:736
#define FIFFB_BEM
Definition fiff_file.h:396
#define FIFF_BEM_SURF_ID
Definition fiff_file.h:724
#define FIFF_BEM_SURF_NTRI
Definition fiff_file.h:727
#define FIFF_BEM_SIGMA
Definition fiff_file.h:737
#define FIFF_BEM_SURF_NORMALS
Definition fiff_file.h:730
Thin-plate-spline 3-D warp from landmark correspondences.
Reader and in-memory representation of a FreeSurfer/MNE surface label (.label).
Boundary element model bundle (inner skull, outer skull, outer skin) loaded from -bem....
Core MNE data structures (source spaces, source estimates, hemispheres).
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t start_block(fiff_int_t kind)
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165
Thin-plate-spline 3-D warp fitted from landmark correspondences.
Definition warp.h:78
Eigen::MatrixXf calculate(const Eigen::MatrixXf &sLm, const Eigen::MatrixXf &dLm, const Eigen::MatrixXf &sVert)
bool write(QIODevice &p_IODevice)
Definition mne_bem.cpp:286
void invtransform(const FIFFLIB::FiffCoordTrans &trans)
Definition mne_bem.cpp:412
void transform(const FIFFLIB::FiffCoordTrans &trans)
Definition mne_bem.cpp:399
static bool readBemSurface(FIFFLIB::FiffStream::SPtr &p_pStream, const FIFFLIB::FiffDirNode::SPtr &p_Tree, MNEBemSurface &p_BemSurface)
Definition mne_bem.cpp:163
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Definition mne_bem.cpp:300
void warp(const Eigen::MatrixXf &sLm, const Eigen::MatrixXf &dLm)
Definition mne_bem.cpp:381
static bool readFromStream(FIFFLIB::FiffStream::SPtr &p_pStream, bool add_geom, MNEBem &p_Bem)
Definition mne_bem.cpp:96
const MNEBemSurface & operator[](qint32 idx) const
Definition mne_bem.cpp:316
MNEBem & operator<<(const MNEBemSurface &surf)
Definition mne_bem.cpp:365
BEM surface provides geometry information.